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_gprf.c: Call Graph Profiling module. */
13 
14 #include "inddef.h"
15 #if !defined(__INTEGRITY)
16 #include "ind_crt1.h"
17 #endif
18 #include "ind_io.h"
19 #include "ind_exit.h"
20 #include "ind_heap.h"
21 #include "indos.h" /* For O_CREAT etc. */
22 
23 #include <string.h> /* for memset() */
24 #include <stdint.h> /* for int32_t, etc. */
25 
26 /* Constants to establish the size of the hash table and the
27    size of the arc pool.
28 
29    The number of entries in the hash table is the size of the
30    .text section divided by HASHDIVISOR.
31 
32    The hash function is the call source's offset into the
33    .text section divided by the HASHDIVISOR.
34 
35    HASHDIVISOR should be a power of 2 and sized so a small
36    number of call sources lie in each hash bucket
37    Use an unsigned constant so division is a simple shift.
38 
39    Decreasing HASHDIVISOR will increase the hash table size
40    and reduce the linked list searching needed for consecutive
41    function calls that would otherwise hash to the same hash table
42    entry.
43  */
44 #define HASHDIVISOR 32U
45 
46 /* The number of entries in the arc pool is ARCMULTIPLIER arcs for
47    each ARCDIVISOR bytes in the .text section. This fraction,
48    ARCMULTIPLIER/ARCDIVISOR, is 3/32 by default.
49 
50    If you see the error "gcount: not enough arcs to record call graph",
51    adjust these quantities to make a larger fraction, such as 1/8.
52 
53    If you see the error "gcount: could not allocate memory", adjust
54    these quantities to make a smaller fraction. Most programs can
55    can use a fraction of 1/16 or 1/32 without running out of arcs.
56 
57    If ARCDIVISOR is an unsigned power of 2, the division operation
58    will consist of a simple shift.
59  */
60 #define ARCMULTIPLIER	 3U
61 #define ARCDIVISOR	32U
62 
63 /* Macro for printing errors */
64 #define PERROR(msg) write(2, msg, sizeof(msg)-1);
65 
66 /* File System I/O Calls to write the file to the host */
67 #if defined(__INTEGRITY) || defined(__INTEGRITY_SHARED_LIBS)
68 #define HOSTIO_CREAT hostio_creat
69 #define HOSTIO_WRITE write
70 #define HOSTIO_CLOSE close
71 #else
72 #define HOSTIO_CREAT __ghs_hostio_creat
73 #define HOSTIO_WRITE __ghs_hostio_write
74 #define HOSTIO_CLOSE __ghs_hostio_close
75 #endif
76 
77 /* Typedef for address. It must be at least 32 bits */
78 #if __PTR_BIT <= 32
79 typedef int32_t arcf_t;
80 #else /* __PTR_BIT */
81 typedef char *  arcf_t;
82 #endif /* __PTR_BIT */
83 
84 /* Arc data structure held in the arc pool */
85 typedef struct _Arc {
86     arcf_t	arctail; /* Address in caller function */
87     arcf_t	archead; /* Address in called function */
88     int32_t	count;
89     struct _Arc *next;
90 } Arc;
91 
92 
93 /* Process only the .text section.
94    (.vletext is used in place of .text for certain PowerPC targets.)
95 
96    Code to process .secinfo could be added, although .secinfo is
97    primarily used to describe program sections that must be copied
98    from ROM to RAM, and not to mark text sections. Any program
99    text sections that were copied from ROM to RAM, however,
100    could be detected by examining .secinfo.
101  */
102 #ifdef __vle
103 #define TEXT_BEGIN __ghsbegin_vletext
104 #define TEXT_END   __ghsend_vletext
105 extern void __ghsbegin_vletext(void), __ghsend_vletext(void);
106 #else
107 #define TEXT_BEGIN __ghsbegin_text
108 #define TEXT_END   __ghsend_text
109 extern void __ghsbegin_text(void), __ghsend_text(void);
110 #endif
111 static int32_t __ghscallgraph_nomemory;
112 
113 
114 /************
115   Hash Table
116 *************/
117 /* The hash table of pointers to Arcs */
118 static Arc **        __ghscallgraph_hashtbl;
119 /* The total number of entries in the hash table */
120 static unsigned long __ghscallgraph_hashtbl_count;
121 
122 
123 /**********
124   Arc Pool
125 ***********/
126 /* The pool of arc data structures */
127 static Arc *         __ghscallgraph_arcpool;
128 /* The total number of arcs in the arc pool */
129 static unsigned long __ghscallgraph_arcpool_count;
130 /* The next available arc in the arc pool */
131 static unsigned long __ghscallgraph_arcpool_next;
132 
133 
134 /************
135   Arc Buffer
136 *************/
137 /* The arc buffer used by __ghs_prof_dump_callgraph to hold a
138    reasonable amount of callgraph data for each call to write()  */
139 static char *        __ghscallgraph_arcbuffer;
140 /* The maximum number of arcs held by the arc buffer */
141 #define ARCBUFFER_MAX_ELEMENTS 1024
142 /* The number of arcs held by the arc buffer */
143 static unsigned long __ghscallgraph_arcbuffer_elements;
144 /* The size of one arc to hold arctail, archead, and count */
145 #define ARCBUFFER_ARCSIZE (2*sizeof(arcf_t) + sizeof(int32_t))
146 /* The total size, in bytes, of the arc buffer */
147 static unsigned long __ghscallgraph_arcbuffer_bytesize;
148 
149 
150 /* The size, in bytes, of memory allocated in __ghs_prof_init_callgraph */
151 static unsigned long __ghscallgraph_memallocated;
152 
153 /* Prevents reentrancy from interrupt handlers. */
154 #ifdef __disable_thread_safe_extensions
155 static int32_t __ghscallgraph_in_gcount;
156 #endif
157 
158 /* Holds the atexit call to __ghs_prof_dump_callgraph */
159 static struct __GHS_AT_EXIT atexit_dump_callgraph;
160 
161 /**
162  * __ghs_prof_clear_callgraph()
163  *
164  * Clears the call graph information.  This is run by a command-line
165  * procedure call from MULTI using the "profilemode clear" command.
166  *
167  * This allows you to retrieve the call graph for a specific interval
168  * in a run of a program by stopping the program, clearing the data,
169  * running to another point, and then dumping the call graph data.
170  *
171  */
__ghs_prof_clear_callgraph(void)172 void __ghs_prof_clear_callgraph(void) {
173     if (__ghscallgraph_hashtbl) {
174 	memset((char *) __ghscallgraph_hashtbl, 0,
175 		__ghscallgraph_memallocated);
176     }
177 #ifdef __disable_thread_safe_extensions
178     __ghscallgraph_in_gcount = 0;
179 #endif
180 }
181 
182 #define _0660	0x1b0	/* MISRA C does not allow octal constants */
183 
184 /**
185  * __ghs_prof_dump_callgraph()
186  *
187  * Writes out the call graph information.  This is run by a command-line
188  * procedure call from MULTI using the "profdump" command.
189  *
190  * This allows you to retrieve the call graph for a specific interval
191  * in a run of a program by stopping the program, clearing the data,
192  * running to another point, and then dumping the call graph data.
193  *
194  */
__ghs_prof_dump_callgraph(void)195 void __ghs_prof_dump_callgraph(void) {
196     static const char writefailed[] = "gcount: could not write gmon.out\n";
197     int32_t fd;
198     unsigned long hashidx;
199     int32_t arcbufferidx;
200 
201 #if __PTR_BIT <= 32
202     /* lpc, hpc, count */
203     int32_t gmon_header[3];
204 #else /* __PTR_BIT */
205     /* 64-bit pointer data */
206     /* [0-1]=lpc, [2-3]=hpc, [4]=cnt
207      * use 32-bit array to avoid padding */
208     uint32_t gmon_header[5];
209 #endif /* __PTR_BIT */
210 
211     static const char gmonout[] = "gmon.out";
212 
213     if (__ghscallgraph_nomemory != 0) {
214 	PERROR("gcount: could not allocate memory\n");
215 	return;
216     }
217 
218 #ifdef __disable_thread_safe_extensions
219     __ghscallgraph_in_gcount = 1;		/* disable profiling */
220 #endif
221 
222     if(__ghscallgraph_arcpool_next >=
223 	    __ghscallgraph_arcpool_count) {
224 	/* If you get this error, decrease the size of
225 	   ARCDIVISOR, above. It should be a power of 2. */
226 	PERROR("gcount: not enough arcs to record call graph\n");
227     }
228 
229     /************************
230       Write gmon.out header
231     ************************/
232 
233     /* The header contains begin_addr, end_addr, sizeof(mcount bucket) */
234     gmon_header[0] = gmon_header[1] = 0; /* no sampling information */
235 #if __PTR_BIT <= 32
236     gmon_header[2] = sizeof(gmon_header);
237 #else /* __PTR_BIT */
238     /* 64-bit support */
239     gmon_header[2] = gmon_header[3] = 0;
240     gmon_header[4] = sizeof(gmon_header);
241 #endif /* __PTR_BIT */
242 
243     if ((fd = HOSTIO_CREAT(gmonout, _0660)) < 0) {
244 	PERROR("gcount: could not create gmon.out\n");
245 	return;
246     }
247 
248     if (HOSTIO_WRITE(fd, gmon_header, sizeof(gmon_header)) != sizeof(gmon_header)) {
249 	PERROR(writefailed);
250 	goto closefile;
251     }
252 
253     /**********************
254       Write callgraph arcs
255     ***********************/
256 
257     arcbufferidx = 0;
258     for(hashidx = 0; hashidx < __ghscallgraph_hashtbl_count; ++hashidx) {
259 	Arc *a = __ghscallgraph_hashtbl[hashidx];
260 	while(a != NULL) {
261 	    memcpy((void *)(__ghscallgraph_arcbuffer +
262 		    arcbufferidx * ARCBUFFER_ARCSIZE),
263 		    (void *)a, ARCBUFFER_ARCSIZE);
264 	    arcbufferidx++;
265 
266 	    /* Check if the arc buffer is full */
267 	    if(arcbufferidx == __ghscallgraph_arcbuffer_elements) {
268 		/* Write out the arc buffer */
269 		if (HOSTIO_WRITE(fd, __ghscallgraph_arcbuffer,
270 			__ghscallgraph_arcbuffer_bytesize) !=
271 			__ghscallgraph_arcbuffer_bytesize) {
272 		    PERROR(writefailed);
273 		    goto closefile;
274 		}
275 		/* Start filling the arc buffer again */
276 		arcbufferidx = 0;
277 	    }
278 
279 	    a = a->next;
280 	}
281     }
282 
283     /* Check if any unwritten arcs remain in the arc buffer */
284     if(arcbufferidx != 0) {
285 	int32_t size = arcbufferidx * ARCBUFFER_ARCSIZE;
286 	/* Write out the arc buffer */
287 	if (HOSTIO_WRITE(fd, __ghscallgraph_arcbuffer, size) != size) {
288 	    PERROR(writefailed);
289 	}
290     }
291 
292 closefile:
293     HOSTIO_CLOSE(fd);
294 
295     /* Ideally, the code should free the arc buffer, hash table and
296      * arc pool, but __ghs_alloc() allocates through sbrk(), which
297      * doesn't allow that.
298      */
299 
300     /* do not clear __ghscallgraph_in_gcount, because we are finished
301      * profiling. It will be cleared in __ghs_prof_clear_callgraph().
302      */
303 }
304 
305 /**
306  * init_callgraph()
307  *
308  * Allocate memory to hold the call graph information. This is called
309  * by __ghs_indgcount() the first time __ghs_indgcount() executes.
310  *
311  * Returns 0 for success, -1 if the memory could not be allocated.
312  *
313  */
init_callgraph(void)314 static int32_t init_callgraph(void) {
315     __ghscallgraph_arcpool_count = 0;
316     __ghscallgraph_arcpool_next = 0;
317 
318     /* Compute the number of hash table entries and memory required */
319     __ghscallgraph_hashtbl_count = ((char *)TEXT_END -
320 	    (char *)TEXT_BEGIN + HASHDIVISOR-1)/HASHDIVISOR;
321     __ghscallgraph_memallocated =
322 	(unsigned long)(__ghscallgraph_hashtbl_count *
323 		sizeof(Arc *));
324 
325     /* Compute the number of arcs to allocate and memory required */
326     __ghscallgraph_arcpool_count = (unsigned long)((char *)TEXT_END -
327 		    (char *)TEXT_BEGIN + ARCDIVISOR-1)/ARCDIVISOR;
328 #if defined(ARCMULTIPLIER)
329     __ghscallgraph_arcpool_count *= ARCMULTIPLIER;
330 #endif
331     __ghscallgraph_memallocated +=
332 	(unsigned long)(__ghscallgraph_arcpool_count *
333 		sizeof(Arc));
334 
335     /* Compute the size of the arc buffer used in
336        __ghs_prof_dump_callgraph */
337     __ghscallgraph_arcbuffer_elements =
338 	(__ghscallgraph_arcpool_count < ARCBUFFER_MAX_ELEMENTS) ?
339 	__ghscallgraph_arcpool_count : ARCBUFFER_MAX_ELEMENTS;
340     __ghscallgraph_arcbuffer_bytesize =
341 	__ghscallgraph_arcbuffer_elements *
342 	ARCBUFFER_ARCSIZE;
343     __ghscallgraph_memallocated +=
344 	__ghscallgraph_arcbuffer_bytesize;
345 
346     /* Allocate memory for the hash table, arc pool, and arc buffer */
347     __ghscallgraph_hashtbl =
348 	(Arc **)__ghs_calloc(__ghscallgraph_memallocated , 1);
349 
350     if (!__ghscallgraph_hashtbl) {
351 	__ghscallgraph_memallocated = 0;
352 	__ghscallgraph_nomemory = 1;
353 	PERROR("gcount: could not allocate memory\n");
354 	return -1;
355     }
356 
357     /* Place the arc pool just after the hash table */
358     __ghscallgraph_arcpool = (Arc *)(__ghscallgraph_hashtbl +
359 	    __ghscallgraph_hashtbl_count);
360 
361     /* Place the arc buffer just after the arc pool */
362     __ghscallgraph_arcbuffer = (char *)(
363 	    __ghscallgraph_arcpool +
364 	    __ghscallgraph_arcpool_count);
365 
366     /* Arrange for __ghs_prof_dump_callgraph() to be called when the
367        program exits. */
368     if (!atexit_dump_callgraph.func) {
369 	atexit_dump_callgraph.func = __ghs_prof_dump_callgraph;
370 	__ghs_at_exit(&atexit_dump_callgraph);
371     }
372 
373     return 0;
374 }
375 
376 
377 /**
378  * __ghs_indgcount()
379  *
380  * Adds one call arc into the call graph. Called by the assembly
381  * routine __ghs_gcount(), which is in turn called in the prologue
382  * of functions compiled with the -pg (Call Graph) option.
383  *
384  * archead is an address in the caller of __ghs_gcount.
385  *
386  * arctail is a pointer to an address in the caller of the caller
387  * of __ghs_gcount().
388 
389  * The addresses actually are the return addresses passed to the callee
390  * and to __ghs_indgcount().
391  * The following pseudocode illustrates this.
392  *
393  * void caller() {
394  *   callee()
395  * after_callee:
396  * }
397  *
398  * void callee() {
399  *   __ghs_gcount(address to return to in caller);
400  * after_ghs_gcount:
401  * }
402  *
403  * void __ghs_gcount(address to return to in caller) {
404  *   __ghs_indgcount(address to return to in callee,
405  *     pointer to address to return to in caller);
406  * }
407  *
408  * Therefore the above sequence is equivalent to
409  * __ghs_gcount(after_callee) ->
410  * __ghs_indgcount(after_ghs_gcount, &after_callee)
411  *
412  */
__ghs_indgcount(void (* archead)(void),void (** parctail)(void))413 void __ghs_indgcount(void (*archead)(void), void (**parctail)(void))
414 {
415     Arc *arcptr, **arcnextptr;
416     void *arctail;
417     unsigned long hashidx;
418 #ifdef __disable_thread_safe_extensions
419     if (__ghscallgraph_in_gcount)
420 	return;
421     __ghscallgraph_in_gcount = 1;
422 #endif
423     if (__ghscallgraph_nomemory)
424 	return;
425 
426     if (!__ghscallgraph_hashtbl) {
427 	if (init_callgraph() != 0) {
428 	    /* Leave __ghscallgraph_in_gcount set; this module
429 	       is now inactive. */
430 	    return;
431 	}
432     }
433 
434     arctail = *parctail;
435     hashidx = ((char *)arctail - (char *)TEXT_BEGIN) / HASHDIVISOR;
436 
437     /* Verify that the caller (arctail) is in the hash table */
438     if(hashidx >= __ghscallgraph_hashtbl_count) {
439 	goto unlock;
440     }
441 
442     arcnextptr = &(__ghscallgraph_hashtbl[hashidx]);
443 
444     /* The linked list holds arcs in the order they were added */
445     for(;;) {
446 
447 	arcptr = *arcnextptr;
448 
449 	/* Check if there are no more arcs at earlier addresses */
450 	if(!arcptr) {
451 	    /* Add this arc after all other arcs (if any) */
452 	    break;
453 	}
454 
455 	if((arcf_t)arctail == arcptr->arctail) {
456 	    if((arcf_t)archead == arcptr->archead) {
457 		/* Increment the existing arc count in this location */
458 		arcptr->count++;
459 		goto unlock;
460 	    }
461 	}
462 
463 	/* Advance to the next arc in the list */
464 	arcnextptr = &(arcptr->next);
465     }
466 
467     if(__ghscallgraph_arcpool_next >=
468 	    __ghscallgraph_arcpool_count) {
469 	/* This is an error condition. It is reported in
470 	   __ghs_prof_dump_callgraph(). Set the next
471 	   index to one greater than the arc pool size
472 	   to distinguish the case where the arc pool
473 	   is exactly large enough from the case where
474 	   the arc pool is actually exhausted.
475 	 */
476 	__ghscallgraph_arcpool_next =
477 	    __ghscallgraph_arcpool_count + 1;
478 	goto unlock;
479     }
480 
481     /* Add new arc to the linked list in the hash table. */
482     arcptr = __ghscallgraph_arcpool + __ghscallgraph_arcpool_next;
483     arcptr->archead = (arcf_t)archead;
484     arcptr->arctail = (arcf_t)arctail;
485     arcptr->count = 1;
486     arcptr->next = *arcnextptr;
487     *arcnextptr = arcptr;
488     __ghscallgraph_arcpool_next++;
489 
490 unlock:
491 #ifdef __disable_thread_safe_extensions
492     __ghscallgraph_in_gcount = 0;
493 #endif
494     return;
495 }
496