Added reference counting the SilcStack.
[runtime.git] / lib / silcutil / silcstack.c
1 /*
2
3   silcstack.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2003 - 2008 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silcruntime.h"
21
22 /************************** Types and definitions ***************************/
23
24 /* The SilcStack context */
25 struct SilcStackStruct {
26   SilcStack parent;                           /* Parent stack */
27   SilcMutex lock;                             /* Stack lock */
28   SilcList stacks;                            /* List of stacks for childs */
29   SilcStackDataEntry stack;                   /* The allocated stack */
30   SilcStackFrame *frames;                     /* Allocated stack frames */
31   SilcStackFrame *frame;                      /* Current stack frame */
32   SilcStackOomHandler oom_handler;            /* OOM handler */
33   void *oom_context;                          /* OOM handler context */
34   SilcUInt32 stack_size;                      /* Default stack size */
35   SilcUInt32 alignment;                       /* Memory alignment */
36   SilcAtomic32 refcnt;                        /* Reference counter */
37 #ifdef SILC_DIST_INPLACE
38   /* Statistics */
39   SilcUInt32 snum_malloc;
40   SilcUInt32 sbytes_malloc;
41   SilcUInt32 snum_errors;
42 #endif /* SILC_DIST_INPLACE */
43 };
44
45 /************************ Static utility functions **************************/
46
47 /* Compute stack block index for the `size'. */
48
49 static SilcUInt32 silc_stack_get_index(SilcUInt32 size, SilcUInt32 *ret_bsize)
50 {
51   SilcUInt32 bsize, si;
52
53   if (size < SILC_STACK_DEFAULT_SIZE)
54     size = SILC_STACK_DEFAULT_SIZE;
55   si = 0;
56   bsize = SILC_STACK_DEFAULT_SIZE;
57   while (bsize < size) {
58     bsize <<= 1;
59     si++;
60   }
61
62   *ret_bsize = bsize;
63
64   return si;
65 }
66
67 /* Get stack from `stack' or allocate new one. */
68
69 static SilcStackDataEntry silc_stack_ref_stack(SilcStack stack,
70                                                SilcUInt32 size,
71                                                SilcUInt32 *ret_si,
72                                                SilcUInt32 *ret_bsize)
73 {
74   SilcStackDataEntry e;
75   SilcUInt32 si, bsize;
76
77   /* Get stack block index and block size for requested size */
78   si = silc_stack_get_index(size, &bsize);
79   *ret_si = si;
80   *ret_bsize = bsize;
81
82   SILC_ST_DEBUG(("Get stack block, si %d, size %lu, stack %p",
83                  si, bsize, stack));
84
85   silc_mutex_lock(stack->lock);
86
87   /* Get stack that has block that can house our size requirement. */
88   silc_list_start(stack->stacks);
89   while ((e = silc_list_get(stack->stacks))) {
90     if (!e->data[si])
91       continue;
92
93     silc_list_del(stack->stacks, e);
94     SILC_ST_DEBUG(("Got stack blocks %p from stack %p", e->data, stack));
95     silc_mutex_unlock(stack->lock);
96     return e;
97   }
98
99   silc_mutex_unlock(stack->lock);
100
101   /* If we are child, get block from parent */
102   if (stack->parent)
103     return silc_stack_ref_stack(stack->parent, size, ret_si, ret_bsize);
104
105   SILC_ST_DEBUG(("Allocate new stack blocks"));
106
107   /* Allocate new stack blocks */
108   e = silc_calloc(1, sizeof(*e));
109   if (!e)
110     return NULL;
111   e->data[si] = silc_malloc(bsize + SILC_STACK_ALIGN(sizeof(*e->data[0]),
112                                                      stack->alignment));
113   if (!e->data[si]) {
114     silc_free(e);
115     return NULL;
116   }
117   e->data[si]->bytes_left = bsize;
118   e->si = si;
119   e->bsize = bsize;
120
121   SILC_ST_DEBUG(("Got stack blocks %p from stack %p", e->data, stack));
122
123   return e;
124 }
125
126 /* Return the `data' back to the `stack'. */
127
128 static void silc_stack_unref_stack(SilcStack stack, SilcStackDataEntry e)
129 {
130   int i;
131
132   SILC_LOG_DEBUG(("Release stack blocks %p to stack %p, si %d",
133                   e->data, stack, e->si));
134
135   /* Release all blocks from allocations */
136   for (i = e->si; i < SILC_STACK_BLOCK_NUM; i++) {
137     if (!e->data[i])
138       continue;
139     if (!i)
140       e->data[i]->bytes_left = e->bsize;
141     else
142       e->data[i]->bytes_left = SILC_STACK_BLOCK_SIZE(stack, i);
143   }
144
145   silc_mutex_lock(stack->lock);
146   silc_list_add(stack->stacks, e);
147   silc_mutex_unlock(stack->lock);
148 }
149
150 /* Allocate memory from a specific stack block */
151
152 static void *silc_stack_alloc_block(SilcStack stack, SilcStackDataEntry e,
153                                     SilcUInt32 items, SilcUInt32 size)
154 {
155   SilcUInt32 asize;
156   void *ptr;
157
158   /* Get pointer and consume the stack block */
159   asize = SILC_STACK_ALIGN(items * size, stack->alignment);
160   ptr = SILC_STACK_DATA_EXT(e->data, e->si, e->bsize, stack->alignment);
161   e->data[e->si]->bytes_left -= asize;
162   memset(ptr, 0, items * size);
163
164   return ptr;
165 }
166
167 /***************************** SilcStack API ********************************/
168
169 /* Allocate the stack */
170
171 SilcStack silc_stack_alloc(SilcUInt32 stack_size, SilcStack parent)
172 {
173   SilcStack stack;
174   SilcStackDataEntry e;
175   SilcUInt32 si = 0, bsize = 0;
176
177   stack_size = stack_size ? stack_size : SILC_STACK_DEFAULT_SIZE;
178   if (stack_size < SILC_STACK_DEFAULT_SIZE)
179     stack_size = SILC_STACK_DEFAULT_SIZE;
180
181   /* Align by 8 */
182   stack_size += ((-stack_size) % 8);
183
184   if (parent) {
185     /* Get stack from parent.  The stack itself is allocated from the
186        parent (but does not consume parent's own stack). */
187     e = silc_stack_ref_stack(parent, stack_size, &si, &bsize);
188     if (!e)
189       return NULL;
190
191     /* Allocate stack from the returned stack.  We allocate ourselves from
192        our own stack. */
193     stack = silc_stack_alloc_block(parent, e, 1, sizeof(*stack));
194     if (!stack) {
195       silc_stack_unref_stack(parent, e);
196       return NULL;
197     }
198
199     stack->parent = parent;
200     stack->stack_size = stack_size;
201     stack->alignment = SILC_STACK_DEFAULT_ALIGN;
202     stack->oom_handler = parent->oom_handler;
203     stack->oom_context = parent->oom_context;
204     stack->lock = parent->lock;
205     silc_list_init(stack->stacks, struct SilcStackDataEntryStruct, next);
206
207     /* Allocate stack frames from the stack itself */
208     stack->frames = silc_stack_alloc_block(stack, e, SILC_STACK_BLOCK_NUM,
209                                            sizeof(*stack->frames));
210     if (!stack->frames) {
211       silc_stack_unref_stack(parent, e);
212       return NULL;
213     }
214
215     /* Reference parent */
216     SILC_LOG_DEBUG(("Reference stack %p, refcnt %d > %d", stack->parent,
217                     silc_atomic_get_int32(&stack->parent->refcnt),
218                     silc_atomic_get_int32(&stack->parent->refcnt) + 1));
219     silc_atomic_add_int32(&stack->parent->refcnt, 1);
220
221     /* Set the initial stack */
222     stack->stack = e;
223   } else {
224     /* Dynamically allocate new stack */
225     stack = silc_calloc(1, sizeof(*stack));
226     if (!stack)
227       return NULL;
228
229     stack->stack_size = stack_size;
230     stack->alignment = SILC_STACK_DEFAULT_ALIGN;
231     silc_list_init(stack->stacks, struct SilcStackDataEntryStruct, next);
232
233     /* Create initial stack */
234     stack->stack = silc_calloc(1, sizeof(*stack->stack));
235     if (!stack->stack) {
236       silc_free(stack);
237       return NULL;
238     }
239     stack->stack->data[0] =
240       silc_malloc(stack->stack_size +
241                   SILC_STACK_ALIGN(sizeof(*stack->stack->data[0]),
242                                    stack->alignment));
243     if (!stack->stack->data[0]) {
244       silc_free(stack->stack);
245       silc_free(stack);
246       return NULL;
247     }
248     stack->stack->data[0]->bytes_left = stack->stack_size;
249     stack->stack->si = 0;
250     stack->stack->bsize = stack->stack_size;
251
252     /* Allocate stack frames from the stack itself */
253     stack->frames = silc_stack_alloc_block(stack, stack->stack,
254                                            SILC_STACK_DEFAULT_NUM,
255                                            sizeof(*stack->frames));
256     if (!stack->frames) {
257       silc_free(stack->stack->data[0]);
258       silc_free(stack->stack);
259       silc_free(stack);
260       return NULL;
261     }
262
263     /* Allocate lock */
264     silc_mutex_alloc(&stack->lock);
265   }
266
267   silc_atomic_init32(&stack->refcnt, 1);
268
269   /* Use the allocated stack in first stack frame */
270   stack->frame = &stack->frames[0];
271   stack->frame->prev = NULL;
272   stack->frame->bytes_used = stack->stack_size;
273   stack->frame->sp = 1;
274   stack->frame->si = si;
275
276   SILC_LOG_DEBUG(("New stack %p, size %d bytes", stack, stack->stack_size));
277
278   return stack;
279 }
280
281 /* Frees the stack and all allocated memory */
282
283 void silc_stack_free(SilcStack stack)
284 {
285   SilcStackDataEntry e;
286   int i;
287
288   if (!stack)
289     return;
290
291   SILC_LOG_DEBUG(("Free stack %p, refcnt %d > %d", stack,
292                   silc_atomic_get_int32(&stack->refcnt),
293                   silc_atomic_get_int32(&stack->refcnt) - 1));
294
295   /* Unreference */
296   if (silc_atomic_sub_int32(&stack->refcnt, 1) > 0)
297     return;
298
299   if (!stack->parent) {
300     silc_list_start(stack->stacks);
301     while ((e = silc_list_get(stack->stacks))) {
302       for (i = 0; i < SILC_STACK_BLOCK_NUM; i++)
303         silc_free(e->data[i]);
304       silc_free(e);
305     }
306
307     for (i = 0; i < SILC_STACK_BLOCK_NUM; i++)
308       silc_free(stack->stack->data[i]);
309     silc_free(stack->stack);
310
311     if (stack->lock)
312       silc_mutex_free(stack->lock);
313
314     silc_atomic_uninit32(&stack->refcnt);
315
316     silc_free(stack);
317   } else {
318     /* Return all stack blocks to the parent */
319     silc_list_start(stack->stacks);
320     while ((e = silc_list_get(stack->stacks)))
321       silc_stack_unref_stack(stack->parent, e);
322
323     silc_stack_unref_stack(stack->parent, stack->stack);
324
325     /* Unreference parent */
326     silc_stack_free(stack->parent);
327   }
328 }
329
330 /* Push to next stack frame */
331
332 SilcUInt32 silc_stack_push(SilcStack stack, SilcStackFrame *frame)
333 {
334   if (!stack)
335     return 0;
336
337   if (!frame) {
338     if (stack->frame->sp >= SILC_STACK_ALIGN(stack->frame->sp,
339                                              SILC_STACK_DEFAULT_NUM)) {
340       SILC_LOG_DEBUG(("SilcStack %p running out of frames, cannot push",
341                       stack));
342       return stack->frame->sp;
343     }
344
345     frame = &stack->frames[stack->frame->sp];
346   }
347
348   /* Push */
349   frame->prev = stack->frame;
350   frame->sp = stack->frame->sp + 1;
351   frame->si = stack->frame->si;
352   frame->bytes_used = stack->stack->data[frame->si]->bytes_left;
353   stack->frame = frame;
354
355   SILC_ST_DEBUG(("Push %p: sp %d -> %d, si %d", stack, frame->prev->sp,
356                  frame->sp, frame->si));
357
358   return stack->frame->sp;
359 }
360
361 /* Pop to previous stack frame */
362
363 SilcUInt32 silc_stack_pop(SilcStack stack)
364 {
365   SilcUInt32 si;
366
367   if (!stack || !stack->frame->prev)
368     return 0;
369
370   /* Pop */
371   si = stack->frame->si;
372   while (si > stack->frame->prev->si) {
373     if (stack->stack->data[si])
374       stack->stack->data[si]->bytes_left = SILC_STACK_BLOCK_SIZE(stack, si);
375     si--;
376   }
377   stack->stack->data[si]->bytes_left = stack->frame->bytes_used;
378   stack->frame = stack->frame->prev;
379
380   SILC_ST_DEBUG(("Pop %p: sp %d -> %d, si %d", stack, stack->frame->sp + 1,
381                  stack->frame->sp, stack->frame->si));
382
383   return stack->frame->sp + 1;
384 }
385
386 /* Allocate memory.  Returns pointer to the memory or NULL on error. */
387
388 void *silc_stack_malloc(SilcStack stack, SilcUInt32 size)
389 {
390   void *ptr;
391   SilcUInt32 bsize, bsize2;
392   SilcUInt32 si = stack->frame->si;
393
394   SILC_STACK_STAT(stack, num_malloc, 1);
395   SILC_ST_DEBUG(("Allocating %d bytes from %p", size, stack));
396
397   if (silc_unlikely(!size)) {
398     SILC_LOG_DEBUG(("Allocation by zero (0)"));
399     silc_set_errno_nofail(SILC_ERR_ZERO_ALLOCATION);
400     SILC_STACK_STAT(stack, num_errors, 1);
401     return NULL;
402   }
403
404   if (silc_unlikely(size > SILC_STACK_MAX_ALLOC)) {
405     SILC_LOG_DEBUG(("Allocating too much"));
406     silc_set_errno_nofail(SILC_ERR_TOO_LARGE_ALLOCATION);
407     SILC_STACK_STAT(stack, num_errors, 1);
408     if (stack->oom_handler)
409       stack->oom_handler(stack, stack->oom_context);
410     return NULL;
411   }
412
413   /* Align properly  */
414   size = SILC_STACK_ALIGN(size, stack->alignment);
415
416   /* Compute the size of current stack block */
417   bsize = SILC_STACK_BLOCK_SIZE(stack, si);
418
419   /* See if there is space in the current stack block */
420   if (stack->stack->data[si]->bytes_left >= size) {
421     /* Get pointer to the memory */
422     ptr = SILC_STACK_DATA(stack, si, bsize);
423     stack->stack->data[si]->bytes_left -= size;
424     SILC_STACK_STAT(stack, bytes_malloc, size);
425     return ptr;
426   }
427
428   /* There is not enough space in this block.  Find the spot to stack
429      block that can handle this size memory. */
430   if (bsize < SILC_STACK_DEFAULT_SIZE)
431     bsize = SILC_STACK_DEFAULT_SIZE;
432   bsize += size;
433   bsize2 = SILC_STACK_DEFAULT_SIZE;
434   si = 0;
435   while (bsize2 < bsize) {
436     bsize2 <<= 1;
437     si++;
438   }
439   if (silc_unlikely(si >= SILC_STACK_BLOCK_NUM)) {
440     SILC_LOG_DEBUG(("Allocating too large block"));
441     silc_set_errno_nofail(SILC_ERR_TOO_LARGE_ALLOCATION);
442     SILC_STACK_STAT(stack, num_errors, 1);
443     if (stack->oom_handler)
444       stack->oom_handler(stack, stack->oom_context);
445     return NULL;
446   }
447
448   /* Allocate the block if it doesn't exist yet */
449   if (!stack->stack->data[si]) {
450     SILC_ST_DEBUG(("Allocating new stack block, %d bytes", bsize2));
451     stack->stack->data[si] =
452       silc_malloc(bsize2 +
453                   SILC_STACK_ALIGN(sizeof(**stack->stack->data),
454                                    stack->alignment));
455     if (silc_unlikely(!stack->stack->data[si])) {
456       SILC_STACK_STAT(stack, num_errors, 1);
457       if (stack->oom_handler)
458         stack->oom_handler(stack, stack->oom_context);
459       return NULL;
460     }
461     stack->stack->data[si]->bytes_left = bsize2;
462   }
463
464   /* Now return memory from this new block.  It is guaranteed that in this
465      block there is enough space for this memory. */
466   assert(stack->stack->data[si]->bytes_left >= size);
467   ptr = SILC_STACK_DATA(stack, si, bsize2);
468   stack->stack->data[si]->bytes_left -= size;
469   stack->frame->si = si;
470   SILC_STACK_STAT(stack, bytes_malloc, size);
471
472   return ptr;
473 }
474
475 /* Attempts to reallocate memory by changing the size of the `ptr' into
476    `size'.  This routine works only if the previous allocation to `stack'
477    was `ptr'.  If there is another memory allocation between allocating
478    `ptr' and this call this routine will return NULL.  NULL is also returned
479    if the `size' does not fit into the current block.  If NULL is returned
480    the old memory remains intact. */
481
482 void *silc_stack_realloc(SilcStack stack, SilcUInt32 old_size,
483                          void *ptr, SilcUInt32 size)
484 {
485   SilcUInt32 si = stack->frame->si;
486   SilcUInt32 bsize;
487   void *sptr;
488
489   if (!ptr)
490     return silc_stack_malloc(stack, size);
491
492   SILC_STACK_STAT(stack, num_malloc, 1);
493   SILC_ST_DEBUG(("Reallocating %d bytes (%d) from %p", size, old_size, stack));
494
495   if (silc_unlikely(!size || !old_size)) {
496     SILC_LOG_DEBUG(("Allocation by zero (0)"));
497     silc_set_errno_nofail(SILC_ERR_ZERO_ALLOCATION);
498     SILC_STACK_STAT(stack, num_errors, 1);
499     return NULL;
500   }
501
502   if (silc_unlikely(size > SILC_STACK_MAX_ALLOC)) {
503     SILC_LOG_DEBUG(("Allocating too much"));
504     silc_set_errno_nofail(SILC_ERR_TOO_LARGE_ALLOCATION);
505     SILC_STACK_STAT(stack, num_errors, 1);
506     if (stack->oom_handler)
507       stack->oom_handler(stack, stack->oom_context);
508     return NULL;
509   }
510
511   /* Align properly */
512   old_size = SILC_STACK_ALIGN(old_size, stack->alignment);
513
514   /* Compute the size of current stack block */
515   bsize = SILC_STACK_BLOCK_SIZE(stack, si);
516
517   /* Check that `ptr' is last allocation */
518   sptr = (unsigned char *)stack->stack->data[si] +
519     SILC_STACK_ALIGN(sizeof(**stack->stack->data), stack->alignment);
520   if (stack->stack->data[si]->bytes_left + old_size +
521       ((unsigned char *)ptr - (unsigned char *)sptr) != bsize) {
522     SILC_LOG_DEBUG(("Cannot reallocate"));
523     silc_set_errno_nofail(SILC_ERR_INVALID_ARGUMENT);
524     SILC_STACK_STAT(stack, num_errors, 1);
525     return NULL;
526   }
527
528   /* Now check that the new size fits to this block */
529   if (stack->stack->data[si]->bytes_left >= size) {
530     /* It fits, so simply return the old pointer */
531     size = SILC_STACK_ALIGN(size, stack->alignment);
532     stack->stack->data[si]->bytes_left -= (size - old_size);
533     SILC_STACK_STAT(stack, bytes_malloc, (size - old_size));
534     return ptr;
535   }
536
537   SILC_LOG_DEBUG(("Cannot reallocate in this block"));
538   silc_set_errno_reason_nofail(SILC_ERR_TOO_LARGE_ALLOCATION,
539                                "Cannot reallocate in this memory block");
540   SILC_STACK_STAT(stack, num_errors, 1);
541   return NULL;
542 }
543
544 /* Set OOM handler */
545
546 void silc_stack_set_oom_handler(SilcStack stack,
547                                 SilcStackOomHandler oom_handler,
548                                 void *context)
549 {
550   stack->oom_handler = oom_handler;
551   stack->oom_context = context;
552 }
553
554 /* Set default alignment */
555
556 void silc_stack_set_alignment(SilcStack stack, SilcUInt32 alignment)
557 {
558   SILC_LOG_DEBUG(("Set stack %p alignment to %d bytes", stack, alignment));
559   stack->alignment = alignment;
560 }
561
562 /* Get default alignment */
563
564 SilcUInt32 silc_stack_get_alignment(SilcStack stack)
565 {
566   return stack->alignment;
567 }
568
569 /* Purge stack */
570
571 SilcBool silc_stack_purge(SilcStack stack)
572 {
573   SilcStackDataEntry e;
574   SilcBool ret = FALSE;
575   int i;
576
577   SILC_LOG_DEBUG(("Purge stack %p", stack));
578
579   /* Go through the default stack */
580   for (i = SILC_STACK_BLOCK_NUM - 1; i > 3; i--) {
581     if (stack->stack->data[i] &&
582         stack->stack->data[i]->bytes_left == SILC_STACK_BLOCK_SIZE(stack, i)) {
583       SILC_LOG_DEBUG(("Purge %d bytes",
584                       SILC_STACK_BLOCK_SIZE(stack, i)));
585       silc_free(stack->stack->data[i]);
586       stack->stack->data[i] = NULL;
587       ret = TRUE;
588     }
589   }
590
591   silc_mutex_lock(stack->lock);
592
593   /* Remove one child stack */
594   if (silc_list_count(stack->stacks) > 2) {
595     silc_list_start(stack->stacks);
596     e = silc_list_get(stack->stacks);
597
598     SILC_LOG_DEBUG(("Remove stack blocks %p", e->data));
599     silc_list_del(stack->stacks, e);
600     ret = TRUE;
601
602     for (i = 0; i < SILC_STACK_BLOCK_NUM; i++)
603       silc_free(e->data[i]);
604     silc_free(e);
605   }
606
607   /* Go through the child stacks */
608   silc_list_start(stack->stacks);
609   while ((e = silc_list_get(stack->stacks))) {
610     for (i = SILC_STACK_BLOCK_NUM - 1; i > 3; i--) {
611       if (e->data[i]) {
612         SILC_LOG_DEBUG(("Purge %d bytes",
613                         SILC_STACK_BLOCK_SIZE(stack, i)));
614         silc_free(e->data[i]);
615         e->data[i] = NULL;
616         ret = TRUE;
617       }
618     }
619   }
620
621   silc_mutex_unlock(stack->lock);
622
623   return ret;
624 }
625
626 /* Set global stack */
627
628 void silc_stack_set_global(SilcStack stack)
629 {
630   SilcTls tls = silc_thread_get_tls();
631
632   if (!tls) {
633     /* Try to initialize Tls */
634     tls = silc_thread_tls_init();
635     SILC_VERIFY(tls);
636     if (!tls)
637       return;
638   }
639
640   tls->stack = stack;
641 }
642
643 /* Return global stack */
644
645 SilcStack silc_stack_get_global(void)
646 {
647   SilcTls tls = silc_thread_get_tls();
648
649   if (!tls)
650     return NULL;
651
652   return tls->stack;
653 }
654
655 #ifdef SILC_DIST_INPLACE
656 /* Statistics dumping. */
657
658 void silc_stack_stats(SilcStack stack)
659 {
660   SilcStackDataEntry e;
661   SilcUInt32 stack_size = 0;
662   int i, c = 0;
663
664   for (i = 0; i < SILC_STACK_BLOCK_NUM; i++) {
665     if (!stack->stack->data[i])
666       continue;
667     stack_size += SILC_STACK_BLOCK_SIZE(stack, i);
668     c++;
669   }
670
671   fprintf(stdout, "\nSilcStack %p statistics :\n\n", stack);
672   fprintf(stdout, "  Size of stack           : %u\n",
673           (unsigned int)stack_size);
674   fprintf(stdout, "  Stack alignment         : %d\n",
675           (int)stack->alignment);
676   fprintf(stdout, "  Number of allocs        : %u\n",
677           (unsigned int)stack->snum_malloc);
678   fprintf(stdout, "  Bytes allocated         : %u\n",
679           (unsigned int)stack->sbytes_malloc);
680   fprintf(stdout, "  Average alloc size      : %.2f\n",
681           (double)((double)stack->sbytes_malloc / (double)stack->snum_malloc));
682   fprintf(stdout, "  Number of alloc errors  : %u\n",
683           (unsigned int)stack->snum_errors);
684   fprintf(stdout, "  Number of frames        : %u\n",
685           (unsigned int)SILC_STACK_ALIGN(stack->frame->sp,
686                                          SILC_STACK_DEFAULT_NUM));
687   fprintf(stdout, "  Number of blocks        : %u\n", c);
688   fprintf(stdout, "  Number of stacks        : %d\n",
689           silc_list_count(stack->stacks));
690
691   silc_list_start(stack->stacks);
692   while ((e = silc_list_get(stack->stacks))) {
693     stack_size = 0;
694     c = 0;
695     for (i = 0; i < SILC_STACK_BLOCK_NUM; i++) {
696       if (!e->data[i])
697         continue;
698       stack_size += e->data[i]->bytes_left;
699       c++;
700     }
701     fprintf(stdout, "\n  Size of stack           : %u\n",
702             (unsigned int)stack_size);
703     fprintf(stdout, "  Number of blocks        : %u\n", c);
704   }
705 }
706 #endif /* SILC_DIST_INPLACE */