2 * profile.c - library functions for call profiling
4 * For processes that were compiled using ACK with the -Rcem-p option,
5 * procentry and procexit will be called on entry and exit of their
6 * functions. Procentry/procexit are implemented here as generic library
10 * 14 Aug, 2006 Created (Rogier Meurs)
18 #include <minix/profile.h>
19 #include <minix/sysutil.h>
20 #include <minix/u64.h>
21 #include <minix/minlib.h>
23 static char cpath
[CPROF_CPATH_MAX_LEN
]; /* current call path string */
24 static int cpath_len
; /* current call path len */
25 static struct cprof_tbl_s
*cprof_slot
; /* slot of current function */
26 struct stack_s
{ /* stack entry */
27 int cpath_len
; /* call path len */
28 struct cprof_tbl_s
*slot
; /* table slot */
29 u64_t start_1
; /* count @ begin of procentry */
30 u64_t start_2
; /* count @ end of procentry */
31 u64_t spent_deeper
; /* spent in called functions */
33 static struct stack_s cprof_stk
[CPROF_STACK_SIZE
]; /* stack */
34 static int cprof_stk_top
; /* top of stack */
35 EXTERN
struct cprof_tbl_s cprof_tbl
[]; /* hash table */
36 static int cprof_tbl_size
; /* nr of slots */
37 static struct cprof_tbl_s
*idx
[CPROF_INDEX_SIZE
]; /* index to table */
38 static struct cprof_ctl_s control
; /* for comms with kernel */
39 static int cprof_announce
; /* announce on n-th execution
41 static int cprof_locked
; /* for reentrancy */
43 static void cprof_init(void);
44 static void reset(void);
45 static void clear_tbl(void);
48 void procentry (char *name
)
51 unsigned hash
= 0, x
= 0;
53 struct cprof_tbl_s
*last
;
57 /* Procentry is not reentrant. */
58 if (cprof_locked
) return; else cprof_locked
= 1;
60 /* Read CPU cycle count into local variable. */
63 /* Run init code once after system boot. */
70 if (init
> -1 && init
++ == cprof_announce
) {
71 /* Tell kernel about control structure and table locations.
73 * In userspace processes, the library function profile_register
74 * will be used. This function does a kernel call (sys_profbuf) to
75 * announce to the kernel the location of the control struct and
76 * hash table. The control struct is used by the kernel to write
77 * a flag if resetting of the table is requested. The location of
78 * the table is needed to copy the information to the user process
81 * Kernelspace processes don't use the library function but have
82 * their own implemention that executes logic similar to sys_profbuf.
83 * The reason for this is that the kernel is non-reentrant, therefore
84 * a kernelspace process is not able to do a kernel call itself since
85 * this would cause a deadlock.
87 profile_register((void *) &control
, (void *) &cprof_tbl
);
91 /* Only continue if sane. */
92 if (control
.err
) return;
94 /* Check if kernel instructed to reset profiling data. */
95 if (control
.reset
) reset();
98 if (++cprof_stk_top
== CPROF_STACK_SIZE
) {
99 printf("CPROFILE error: stack overrun\n");
100 control
.err
|= CPROF_STACK_OVERRUN
;
104 /* Save initial cycle count on stack. */
105 cprof_stk
[cprof_stk_top
].start_1
= start
;
107 /* Check available call path len. */
108 if (cpath_len
+ strlen(name
) + 1 > CPROF_CPATH_MAX_LEN
) {
109 printf("CPROFILE error: call path overrun\n");
110 control
.err
|= CPROF_CPATH_OVERRUN
;
114 /* Save previous call path length on stack. */
115 cprof_stk
[cprof_stk_top
].cpath_len
= cpath_len
;
117 /* Generate new call path string and length.*/
118 if (cprof_stk_top
> 0) /* Path is space separated. */
119 cpath
[cpath_len
++] = ' ';
120 while ((c
= *(name
++)) != '\0') /* Append function name. */
121 cpath
[cpath_len
++] = c
;
122 cpath
[cpath_len
] = '\0'; /* Null-termination. */
124 /* Calculate hash for call path string (algorithm: ELF). */
125 for (i
=0; i
<cpath_len
; i
++) {
126 hash
= (hash
<< 4) + cpath
[i
];
127 if ((x
= hash
& 0xF0000000L
) != 0) {
132 hash
%= CPROF_INDEX_SIZE
;
134 /* Look up the slot for this call path in the hash table. */
135 for (cprof_slot
= idx
[hash
]; cprof_slot
!= 0; cprof_slot
= cprof_slot
->next
)
136 if (strcmp(cprof_slot
->cpath
, cpath
) == 0) break;
139 cprof_slot
->calls
++; /* found slot: update call counter */
141 /* Not found: insert path into hash table. */
142 if (control
.slots_used
== cprof_tbl_size
) {
143 printf("CPROFILE error: table overrun\n");
144 control
.err
|= CPROF_TABLE_OVERRUN
;
147 /* Set values for new slot. */
148 cprof_slot
= &cprof_tbl
[control
.slots_used
++];
149 strlcpy(cprof_slot
->cpath
, cpath
, sizeof(cprof_slot
->cpath
));
150 cprof_slot
->calls
= 1;
153 if (idx
[hash
] == 0) {
154 /* No collision: simple update. */
155 idx
[hash
] = cprof_slot
;
157 /* Collision: update last in chain. */
158 for (last
= idx
[hash
]; last
->next
!= 0; last
= last
->next
);
159 last
->next
= cprof_slot
;
162 /* Save slot on stack. */
163 cprof_stk
[cprof_stk_top
].slot
= cprof_slot
;
165 /* Again save CPU cycle count on stack. */
166 read_tsc_64(&cprof_stk
[cprof_stk_top
].start_2
);
171 void procexit (char *UNUSED(name
))
174 u32_t tsc_lo
, tsc_hi
;
176 /* Procexit is not reentrant. */
177 if (cprof_locked
) return; else cprof_locked
= 1;
179 /* First thing: read CPU cycle count into local variable. */
180 read_tsc(&tsc_hi
, &tsc_lo
);
181 stop
= make64(tsc_lo
, tsc_hi
);
183 /* Only continue if sane. */
184 if (control
.err
) return;
186 /* Update cycle count for this call path. Exclude time spent in procentry/
187 * procexit by using measurements taken at end of procentry and begin of
188 * procexit (the "small" difference). This way, only the call overhead for
189 * the procentry/procexit functions will be attributed to this call path,
190 * not the procentry/procexit cycles.
193 /* Calculate "small" difference. */
194 spent
= sub64(stop
, cprof_stk
[cprof_stk_top
].start_2
);
195 cprof_stk
[cprof_stk_top
].slot
->cycles
=
196 add64(cprof_stk
[cprof_stk_top
].slot
->cycles
,
197 sub64(spent
, cprof_stk
[cprof_stk_top
].spent_deeper
));
199 /* Clear spent_deeper for call level we're leaving. */
200 cprof_stk
[cprof_stk_top
].spent_deeper
= ((u64_t
)(0));
202 /* Adjust call path string and stack. */
203 cpath_len
= cprof_stk
[cprof_stk_top
].cpath_len
;
204 cpath
[cpath_len
] = '\0';
206 /* Update spent_deeper for call level below. Include time spent in
207 * procentry/procexit by using measurements taken at begin of procentry
208 * and end of procexit (the "big" difference). This way the time spent in
209 * procentry/procexit will be included in spent_deeper and therefore, since
210 * this value is substracted from the lower call level, it will not be
211 * attributed to any call path. This way, pollution of the statistics
212 * because of procentry/procexit is kept to a minimum.
215 /* Read CPU cycle count. */
216 read_tsc(&tsc_hi
, &tsc_lo
);
217 stop
= make64(tsc_lo
, tsc_hi
);
219 /* Calculate "big" difference. */
220 spent
= sub64(stop
, cprof_stk
[cprof_stk_top
].start_1
);
221 cprof_stk_top
--; /* decrease stack */
222 if (cprof_stk_top
>= 0) /* don't update non-existent level -1 */
223 cprof_stk
[cprof_stk_top
].spent_deeper
=
224 add64(cprof_stk
[cprof_stk_top
].spent_deeper
, spent
);
229 static void cprof_init()
238 cprof_tbl_size
= profile_get_tbl_size();
239 cprof_announce
= profile_get_announce();
242 for (i
=0; i
<CPROF_STACK_SIZE
; i
++) {
243 cprof_stk
[i
].cpath_len
= 0;
244 cprof_stk
[i
].slot
= 0;
245 cprof_stk
[i
].start_1
= ((u64_t
)(0));
246 cprof_stk
[i
].start_2
= ((u64_t
)(0));
247 cprof_stk
[i
].spent_deeper
= ((u64_t
)(0));
259 static void clear_tbl()
263 /* Reset profiling table. */
264 control
.slots_used
= 0;
265 for (i
=0; i
<CPROF_INDEX_SIZE
; i
++) idx
[i
] = 0; /* clear index */
266 for (i
=0; i
<cprof_tbl_size
; i
++) { /* clear table */
267 memset(cprof_tbl
[i
].cpath
, '\0', CPROF_CPATH_MAX_LEN
);
268 cprof_tbl
[i
].next
= 0;
269 cprof_tbl
[i
].calls
= 0;
270 cprof_tbl
[i
].cycles
= make64(0, 0);