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