retire 64-bit conversion functions
[minix3.git] / lib / libsys / arch / i386 / profile.c
blob51c90f711581f59584b1295a991dcc7029d80f57
1 /*
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
7 * functions.
9 * Changes:
10 * 14 Aug, 2006 Created (Rogier Meurs)
13 #include <lib.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
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
40 * of procentry */
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)
50 static int init = 0;
51 unsigned hash = 0, x = 0;
52 int i = 0;
53 struct cprof_tbl_s *last;
54 char c;
55 u64_t start;
57 /* Procentry is not reentrant. */
58 if (cprof_locked) return; else cprof_locked = 1;
60 /* Read CPU cycle count into local variable. */
61 read_tsc_64(&start);
63 /* Run init code once after system boot. */
64 if (init == 0) {
65 cprof_init();
66 init++;
69 /* Announce once. */
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
79 * that requests it.
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);
88 init = -1;
91 /* Only continue if sane. */
92 if (control.err) return;
94 /* Check if kernel instructed to reset profiling data. */
95 if (control.reset) reset();
97 /* Increase stack. */
98 if (++cprof_stk_top == CPROF_STACK_SIZE) {
99 printf("CPROFILE error: stack overrun\n");
100 control.err |= CPROF_STACK_OVERRUN;
101 return;
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;
111 return;
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) {
128 hash ^= (x >> 24);
129 hash &= ~x;
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;
138 if (cprof_slot)
139 cprof_slot->calls++; /* found slot: update call counter */
140 else {
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;
145 return;
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;
152 /* Update index. */
153 if (idx[hash] == 0) {
154 /* No collision: simple update. */
155 idx[hash] = cprof_slot;
156 } else {
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);
167 cprof_locked = 0;
171 void procexit (char *UNUSED(name))
173 u64_t stop, spent;
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);
225 cprof_locked = 0;
229 static void cprof_init()
231 int i;
233 cpath[0] = '\0';
234 cpath_len = 0;
235 cprof_stk_top = -1;
236 control.reset = 0;
237 control.err = 0;
238 cprof_tbl_size = profile_get_tbl_size();
239 cprof_announce = profile_get_announce();
240 clear_tbl();
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));
252 static void reset()
254 clear_tbl();
255 control.reset = 0;
259 static void clear_tbl()
261 int i;
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);