1 /* -----------------------------------------------------------------------------
3 * (c) The GHC Team, 2001
6 * Retainer set implementation for retainer profiling (see RetainerProfile.c)
8 * ---------------------------------------------------------------------------*/
16 #include "RetainerSet.h"
18 #include "Profiling.h"
23 #define HASH_TABLE_SIZE 255
24 #define hash(hk) (hk % HASH_TABLE_SIZE)
25 static RetainerSet *hashTable[HASH_TABLE_SIZE];
27 static Arena *arena; // arena in which we store retainer sets
29 static int nextId; // id of next retainer set
31 /* -----------------------------------------------------------------------------
32 * rs_MANY is a distinguished retainer set, such that
34 * isMember(e, rs_MANY) = True
36 * addElement(e, rs) = rs_MANY, if rs->num >= maxRetainerSetSize
37 * addElement(e, rs_MANY) = rs_MANY
39 * The point of rs_MANY is to keep the total number of retainer sets
40 * from growing too large.
41 * -------------------------------------------------------------------------- */
42 RetainerSet rs_MANY = {
50 /* -----------------------------------------------------------------------------
51 * calculate the size of a RetainerSet structure
52 * -------------------------------------------------------------------------- */
54 sizeofRetainerSet( int elems )
56 return (sizeof(RetainerSet) + elems * sizeof(retainer));
59 /* -----------------------------------------------------------------------------
60 * Creates the first pool and initializes hashTable[].
61 * Frees all pools if any.
62 * -------------------------------------------------------------------------- */
64 initializeAllRetainerSet(void)
70 for (i = 0; i < HASH_TABLE_SIZE; i++)
72 nextId = 2; // Initial value must be positive, 2 is MANY.
75 /* -----------------------------------------------------------------------------
76 * Refreshes all pools for reuse and initializes hashTable[].
77 * -------------------------------------------------------------------------- */
79 refreshAllRetainerSet(void)
84 // first approach: completely refresh
88 for (i = 0; i < HASH_TABLE_SIZE; i++)
91 #endif /* FIRST_APPROACH */
94 /* -----------------------------------------------------------------------------
96 * -------------------------------------------------------------------------- */
98 closeAllRetainerSet(void)
103 /* -----------------------------------------------------------------------------
104 * Finds or creates if needed a singleton retainer set.
105 * -------------------------------------------------------------------------- */
107 singleton(retainer r)
112 hk = hashKeySingleton(r);
113 for (rs = hashTable[hash(hk)]; rs != NULL; rs = rs->link)
114 if (rs->num == 1 && rs->element[0] == r) return rs; // found it
117 rs = arenaAlloc( arena, sizeofRetainerSet(1) );
120 rs->link = hashTable[hash(hk)];
124 // The new retainer set is placed at the head of the linked list.
125 hashTable[hash(hk)] = rs;
130 /* -----------------------------------------------------------------------------
131 * Finds or creates a retainer set *rs augmented with r.
133 * r is not a member of rs, i.e., isMember(r, rs) returns rtsFalse.
136 * We could check if rs is NULL, in which case this function call
137 * reverts to singleton(). We do not choose this strategy because
138 * in most cases addElement() is invoked with non-NULL rs.
139 * -------------------------------------------------------------------------- */
141 addElement(retainer r, RetainerSet *rs)
144 nat nl; // Number of retainers in *rs Less than r
145 RetainerSet *nrs; // New Retainer Set
146 StgWord hk; // Hash Key
148 #ifdef DEBUG_RETAINER
149 // debugBelch("addElement(%p, %p) = ", r, rs);
153 ASSERT(rs->num <= RtsFlags.ProfFlags.maxRetainerSetSize);
155 if (rs == &rs_MANY || rs->num == RtsFlags.ProfFlags.maxRetainerSetSize) {
159 ASSERT(!isMember(r, rs));
161 for (nl = 0; nl < rs->num; nl++)
162 if (r < rs->element[nl]) break;
163 // Now nl is the index for r into the new set.
164 // Also it denotes the number of retainers less than r in *rs.
165 // Thus, compare the first nl retainers, then r itself, and finally the
166 // remaining (rs->num - nl) retainers.
168 hk = hashKeyAddElement(r, rs);
169 for (nrs = hashTable[hash(hk)]; nrs != NULL; nrs = nrs->link) {
170 // test *rs and *nrs for equality
173 if (rs->num + 1 != nrs->num) continue;
175 // compare the first nl retainers and find the first non-matching one.
176 for (i = 0; i < nl; i++)
177 if (rs->element[i] != nrs->element[i]) break;
178 if (i < nl) continue;
181 if (r != nrs->element[i]) continue; // i == nl
183 // compare the remaining retainers
184 for (; i < rs->num; i++)
185 if (rs->element[i] != nrs->element[i + 1]) break;
186 if (i < rs->num) continue;
188 #ifdef DEBUG_RETAINER
189 // debugBelch("%p\n", nrs);
191 // The set we are seeking already exists!
195 // create a new retainer set
196 nrs = arenaAlloc( arena, sizeofRetainerSet(rs->num + 1) );
197 nrs->num = rs->num + 1;
199 nrs->link = hashTable[hash(hk)];
201 for (i = 0; i < nl; i++) { // copy the first nl retainers
202 nrs->element[i] = rs->element[i];
204 nrs->element[i] = r; // copy r
205 for (; i < rs->num; i++) { // copy the remaining retainers
206 nrs->element[i + 1] = rs->element[i];
209 hashTable[hash(hk)] = nrs;
211 #ifdef DEBUG_RETAINER
212 // debugBelch("%p\n", nrs);
217 /* -----------------------------------------------------------------------------
218 * Call f() for each retainer set.
219 * -------------------------------------------------------------------------- */
221 traverseAllRetainerSet(void (*f)(RetainerSet *))
227 for (i = 0; i < HASH_TABLE_SIZE; i++)
228 for (rs = hashTable[i]; rs != NULL; rs = rs->link)
233 /* -----------------------------------------------------------------------------
234 * printRetainer() prints the full information on a given retainer,
235 * not a retainer set.
236 * -------------------------------------------------------------------------- */
237 #if defined(RETAINER_SCHEME_INFO)
238 // Retainer scheme 1: retainer = info table
240 printRetainer(FILE *f, retainer itbl)
242 fprintf(f, "%s[%s]", itbl->prof.closure_desc, itbl->prof.closure_type);
244 #elif defined(RETAINER_SCHEME_CCS)
245 // Retainer scheme 2: retainer = cost centre stack
247 printRetainer(FILE *f, retainer ccs)
251 #elif defined(RETAINER_SCHEME_CC)
252 // Retainer scheme 3: retainer = cost centre
254 printRetainer(FILE *f, retainer cc)
256 fprintf(f,"%s.%s", cc->module, cc->label);
260 /* -----------------------------------------------------------------------------
261 * printRetainerSetShort() should always display the same output for
262 * a given retainer set regardless of the time of invocation.
263 * -------------------------------------------------------------------------- */
264 #ifdef SECOND_APPROACH
265 #if defined(RETAINER_SCHEME_INFO)
266 // Retainer scheme 1: retainer = info table
268 printRetainerSetShort(FILE *f, RetainerSet *rs)
270 #define MAX_RETAINER_SET_SPACE 24
271 char tmp[MAX_RETAINER_SET_SPACE + 1];
277 tmp[MAX_RETAINER_SET_SPACE] = '\0';
279 // No blank characters are allowed.
280 sprintf(tmp + 0, "(%d)", -(rs->id));
282 ASSERT(size < MAX_RETAINER_SET_SPACE);
284 for (j = 0; j < rs->num; j++) {
285 if (j < rs->num - 1) {
286 strncpy(tmp + size, rs->element[j]->prof.closure_desc, MAX_RETAINER_SET_SPACE - size);
288 if (size == MAX_RETAINER_SET_SPACE)
290 strncpy(tmp + size, ",", MAX_RETAINER_SET_SPACE - size);
292 if (size == MAX_RETAINER_SET_SPACE)
296 strncpy(tmp + size, rs->element[j]->prof.closure_desc, MAX_RETAINER_SET_SPACE - size);
297 // size = strlen(tmp);
302 #elif defined(RETAINER_SCHEME_CC)
303 // Retainer scheme 3: retainer = cost centre
305 printRetainerSetShort(FILE *f, RetainerSet *rs)
307 #define MAX_RETAINER_SET_SPACE 24
308 char tmp[MAX_RETAINER_SET_SPACE + 1];
313 #elif defined(RETAINER_SCHEME_CCS)
314 // Retainer scheme 2: retainer = cost centre stack
316 printRetainerSetShort(FILE *f, RetainerSet *rs)
318 #define MAX_RETAINER_SET_SPACE 24
319 char tmp[MAX_RETAINER_SET_SPACE + 1];
325 tmp[MAX_RETAINER_SET_SPACE] = '\0';
327 // No blank characters are allowed.
328 sprintf(tmp + 0, "(%d)", -(rs->id));
330 ASSERT(size < MAX_RETAINER_SET_SPACE);
332 for (j = 0; j < rs->num; j++) {
333 if (j < rs->num - 1) {
334 strncpy(tmp + size, rs->element[j]->cc->label, MAX_RETAINER_SET_SPACE - size);
336 if (size == MAX_RETAINER_SET_SPACE)
338 strncpy(tmp + size, ",", MAX_RETAINER_SET_SPACE - size);
340 if (size == MAX_RETAINER_SET_SPACE)
344 strncpy(tmp + size, rs->element[j]->cc->label, MAX_RETAINER_SET_SPACE - size);
345 // size = strlen(tmp);
350 #elif defined(RETAINER_SCHEME_CC)
351 // Retainer scheme 3: retainer = cost centre
353 printRetainerSetShort(FILE *f, retainerSet *rs)
355 #define MAX_RETAINER_SET_SPACE 24
356 char tmp[MAX_RETAINER_SET_SPACE + 1];
362 tmp[MAX_RETAINER_SET_SPACE] = '\0';
364 // No blank characters are allowed.
365 sprintf(tmp + 0, "(%d)", -(rs->id));
367 ASSERT(size < MAX_RETAINER_SET_SPACE);
369 for (j = 0; j < rs->num; j++) {
370 if (j < rs->num - 1) {
371 strncpy(tmp + size, rs->element[j]->label,
372 MAX_RETAINER_SET_SPACE - size);
374 if (size == MAX_RETAINER_SET_SPACE)
376 strncpy(tmp + size, ",", MAX_RETAINER_SET_SPACE - size);
378 if (size == MAX_RETAINER_SET_SPACE)
382 strncpy(tmp + size, rs->element[j]->label,
383 MAX_RETAINER_SET_SPACE - size);
384 // size = strlen(tmp);
389 #define MAX_RETAINER_SET_SPACE 24
391 // 1. 32 > MAX_RETAINER_SET_SPACE + 1 (1 for '\0')
392 // 2. (MAX_RETAINER_SET_SPACE - DOT_NUMBER ) characters should be enough for
393 // printing one natural number (plus '(' and ')').
400 // No blank characters are allowed.
401 sprintf(tmp + 0, "(%d)", -(rs->id));
403 ASSERT(size < MAX_RETAINER_SET_SPACE - DOT_NUMBER);
405 for (j = 0; j < rs->num; j++) {
406 ts = strlen(rs->element[j]->label);
407 if (j < rs->num - 1) {
408 if (size + ts + 1 > MAX_RETAINER_SET_SPACE - DOT_NUMBER) {
409 sprintf(tmp + size, "...");
412 sprintf(tmp + size, "%s,", rs->element[j]->label);
416 if (size + ts > MAX_RETAINER_SET_SPACE - DOT_NUMBER) {
417 sprintf(tmp + size, "...");
420 sprintf(tmp + size, "%s", rs->element[j]->label);
427 #endif /* RETAINER_SCHEME_CC */
428 #endif /* SECOND_APPROACH */
430 /* -----------------------------------------------------------------------------
431 * Dump the contents of each retainer set into the log file at the end
432 * of the run, so the user can find out for a given retainer set ID
433 * the full contents of that set.
434 * --------------------------------------------------------------------------- */
435 #ifdef SECOND_APPROACH
437 outputAllRetainerSet(FILE *prof_file)
441 RetainerSet *rs, **rsArray, *tmp;
443 // find out the number of retainer sets which have had a non-zero cost at
444 // least once during retainer profiling
446 for (i = 0; i < HASH_TABLE_SIZE; i++)
447 for (rs = hashTable[i]; rs != NULL; rs = rs->link) {
452 if (numSet == 0) // retainer profiling was not done at all.
456 rsArray = stgMallocBytes(numSet * sizeof(RetainerSet *),
457 "outputAllRetainerSet()");
459 // prepare for sorting
461 for (i = 0; i < HASH_TABLE_SIZE; i++)
462 for (rs = hashTable[i]; rs != NULL; rs = rs->link) {
471 // sort rsArray[] according to the id of each retainer set
472 for (i = numSet - 1; i > 0; i--) {
473 for (j = 0; j <= i - 1; j++) {
474 // if (-(rsArray[j]->id) < -(rsArray[j + 1]->id))
475 if (rsArray[j]->id < rsArray[j + 1]->id) {
477 rsArray[j] = rsArray[j + 1];
478 rsArray[j + 1] = tmp;
483 fprintf(prof_file, "\nRetainer sets created during profiling:\n");
484 for (i = 0;i < numSet; i++) {
485 fprintf(prof_file, "SET %u = {", -(rsArray[i]->id));
486 for (j = 0; j < rsArray[i]->num - 1; j++) {
487 printRetainer(prof_file, rsArray[i]->element[j]);
488 fprintf(prof_file, ", ");
490 printRetainer(prof_file, rsArray[i]->element[j]);
491 fprintf(prof_file, "}\n");
496 #endif /* SECOND_APPROACH */
498 #endif /* PROFILING */