source: src/Common/Stats/Heap.cpp @ 0cb3479

Last change on this file since 0cb3479 was 3c55fcd, checked in by Andrew Beach <ajbeach@…>, 5 months ago

Added some notes about how to enable heap statistics. Reformated some of the related macro code to make it a bit more compact and hopefully clearer.

  • Property mode set to 100644
File size: 8.4 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// Heap.cpp --
8//
9// Author           : Thierry Delisle
10// Created On       : Thu May  3 16:16:10 2018
11// Last Modified By : Peter A. Buhr
12// Last Modified On : Fri May  4 17:27:31 2018
13// Update Count     : 28
14//
15
16#include <cassert>
17#include <cmath>
18#include <cstddef>
19#include <cstring>
20#include <iomanip>
21#include <iostream>
22
23// Most of the other statistics features are deactivated only by defining
24// NO_STATISTICS (or their NO_%_STATISTICS macro). However the heap has some
25// other compatability concerns and will disable itself in some cases.
26//
27// I do not claim to understand these cases. But TCMALLOC is often defined by
28// default and you can pass --disable-gprofiler to configure to remove it.
29
30#if defined(NO_STATISTICS) || defined(TCMALLOC) || defined(__SANITIZE_ADDRESS__)
31        #define NO_HEAP_STATISTICS
32#elif defined(__has_feature)
33        #if __has_feature(address_sanitizer)
34                #define NO_HEAP_STATISTICS
35        #endif
36#endif
37
38namespace Stats {
39        namespace Heap {
40#if defined( NO_HEAP_STATISTICS )
41                void newPass( const char * const ) {}
42
43                void print() {}
44#else
45                extern bool enabled;
46
47                struct StatBlock {
48                        const char * name  = nullptr;   ///< Name of this pass
49                        size_t mallocs     = 0;                 ///< Allocations in this pass
50                        size_t frees       = 0;                 ///< Frees in this pass
51                        size_t n_allocs    = 0;                 ///< Current number of live allocations
52                        size_t peak_allocs = 0;                 ///< Peak number of live allocations this pass
53                };
54
55                StatBlock    passes[100] = {{ "Pre-Parse", 0, 0, 0, 0 }};
56                const size_t passes_size = sizeof(passes) / sizeof(passes[0]);
57                size_t       passes_cnt = 1;
58
59                StatBlock    stacktrace_stats[100];
60                size_t       stacktrace_stats_count = 0;
61                bool         stacktrace_stats_enabled = true;
62
63                size_t       trace[1000];
64                const size_t stacktrace_max_depth = sizeof(trace) / sizeof(size_t);
65                size_t       stacktrace_depth;
66
67                size_t new_stacktrace_id(const char * const name) {
68                        stacktrace_stats[stacktrace_stats_count].name = name;
69                        return stacktrace_stats_count++;
70                }
71
72                void stacktrace_push(size_t id) {
73                        ++stacktrace_depth;
74                        assertf(stacktrace_depth < stacktrace_max_depth, "Stack trace too deep: increase size of array in Heap.cpp");
75                        trace[stacktrace_depth] = id;
76                }
77
78                void stacktrace_pop() {
79                        assertf(stacktrace_depth > 0, "Invalid stack tracing operation: trace is empty");
80                        --stacktrace_depth;
81                }
82
83                void newPass( const char * const name ) {
84                        passes[passes_cnt].name    = name;
85                        passes[passes_cnt].mallocs = 0;
86                        passes[passes_cnt].frees   = 0;
87                        passes[passes_cnt].n_allocs
88                                = passes[passes_cnt].peak_allocs
89                                = passes[passes_cnt-1].n_allocs;
90                        passes_cnt++;
91
92                        assertf(passes_cnt < passes_size, "Too many passes for Stats::Heap, increase the size of the array in Heap.cpp");
93                }
94
95                void print(size_t value, size_t total) {
96                        std::cerr << std::setw(12) << value;
97                        std::cerr << "(" << std::setw(3);
98                        std::cerr << (value == 0 ? 0 : value * 100 / total);
99                        std::cerr << "%) | ";
100                }
101
102                void print(const StatBlock& stat, size_t nc, size_t total_mallocs, size_t total_frees, size_t overall_peak) {
103                        std::cerr << std::setw(nc) << stat.name;
104                        std::cerr << " | ";
105
106                        print(stat.mallocs,     total_mallocs);
107                        print(stat.frees,       total_frees  );
108                        print(stat.peak_allocs, overall_peak );
109                        std::cerr << "\n";
110                }
111
112                void print(char c, size_t nc) {
113                        for(size_t i = 0; i < nc; i++) {
114                                std::cerr << c;
115                        }
116                        std::cerr << '\n';
117                }
118
119                void print() {
120                        if(!enabled) return;
121
122                        size_t nc = 0;
123                        size_t total_mallocs = 0;
124                        size_t total_frees   = 0;
125                        size_t overall_peak  = 0;
126                        for(size_t i = 0; i < passes_cnt; i++) {
127                                nc = std::max(nc, std::strlen(passes[i].name));
128                                total_mallocs += passes[i].mallocs;
129                                total_frees   += passes[i].frees;
130                                overall_peak = std::max(overall_peak, passes[i].peak_allocs);
131                        }
132                        size_t nct = nc + 65;
133
134                        const char * const title = "Heap Usage Statistic";
135                        print('=', nct);
136                        for(size_t i = 0; i < (nct - std::strlen(title)) / 2; i++) std::cerr << ' ';
137                        std::cerr << title << std::endl;
138                        print('-', nct);
139                        std::cerr << std::setw(nc) << "Pass";
140                        std::cerr << " |       Malloc Count |         Free Count |        Peak Allocs |" << std::endl;
141
142                        print('-', nct);
143                        for(size_t i = 0; i < passes_cnt; i++) {
144                                print(passes[i], nc, total_mallocs, total_frees, overall_peak);
145                        }
146
147                        print('-', nct);
148                        std::cerr << std::setw(nc) << "Trace";
149                        std::cerr << " |       Malloc Count |         Free Count |        Peak Allocs |" << std::endl;
150
151                        print('-', nct);
152                        for (size_t i = 0; i < stacktrace_stats_count; i++) {
153                                print(stacktrace_stats[i], nc, total_mallocs, total_frees, overall_peak);
154                        }
155                        print('-', nct);
156                        print({"Sum", total_mallocs, total_frees, 0, overall_peak},
157                                nc, total_mallocs, total_frees, overall_peak);
158
159                }
160
161#include <stdarg.h>
162#include <stddef.h>
163#include <stdio.h>
164#include <string.h>
165#include <unistd.h>
166#include <signal.h>
167                extern "C" {
168#include <dlfcn.h>
169#include <execinfo.h>
170                }
171
172        //=============================================================================================
173        // Interposing helpers
174        //=============================================================================================
175
176                typedef void (* generic_fptr_t)(void);
177                generic_fptr_t interpose_symbol( const char * symbol, const char * version ) {
178                        const char * error;
179
180                        static void * library;
181                        if ( ! library ) {
182#                               if defined( RTLD_NEXT )
183                                        library = RTLD_NEXT;
184#                               else
185                                        // missing RTLD_NEXT => must hard-code library name, assuming libstdc++
186                                        library = dlopen( "libc.so.6", RTLD_LAZY );
187                                        error = dlerror();
188                                        if ( error ) {
189                                                std::cerr << "interpose_symbol : failed to open libc, " << error << std::endl;
190                                                abort();
191                                        }
192#                               endif // RTLD_NEXT
193                        } // if
194
195                        generic_fptr_t fptr;
196
197#                       if defined( _GNU_SOURCE )
198                                if ( version ) {
199                                        fptr = (generic_fptr_t)dlvsym( library, symbol, version );
200                                } else {
201                                        fptr = (generic_fptr_t)dlsym( library, symbol );
202                                }
203#                       else
204                                fptr = (generic_fptr_t)dlsym( library, symbol );
205#                       endif // _GNU_SOURCE
206
207                        error = dlerror();
208                        if ( error ) {
209                                std::cerr << "interpose_symbol : internal error, " << error << std::endl;
210                                abort();
211                        }
212
213                        return fptr;
214                }
215
216                extern "C" {
217                        void * malloc( size_t size ) __attribute__((malloc));
218                        void * malloc( size_t size ) {
219                                static auto __malloc = reinterpret_cast<void * (*)(size_t)>(interpose_symbol( "malloc", nullptr ));
220                                if( enabled && passes_cnt > 0 ) {
221                                        passes[passes_cnt - 1].mallocs++;
222                                        passes[passes_cnt - 1].n_allocs++;
223                                        passes[passes_cnt - 1].peak_allocs
224                                                = std::max(passes[passes_cnt - 1].peak_allocs, passes[passes_cnt - 1].n_allocs);
225                                }
226
227                                if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
228                                        stacktrace_stats[trace[stacktrace_depth]].mallocs++;
229                                }
230                                return __malloc( size );
231                        }
232
233                        void free( void * ptr ) {
234                                static auto __free = reinterpret_cast<void   (*)(void *)>(interpose_symbol( "free", nullptr ));
235                                if( enabled && passes_cnt > 0 ) {
236                                        passes[passes_cnt - 1].frees++;
237                                        passes[passes_cnt - 1].n_allocs--;
238                                }
239                                if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
240                                        stacktrace_stats[trace[stacktrace_depth]].frees++;
241                                }
242                                return __free( ptr );
243                        }
244
245                        void * calloc( size_t nelem, size_t size ) {
246                                static auto __calloc = reinterpret_cast<void * (*)(size_t, size_t)>(interpose_symbol( "calloc", nullptr ));
247                                if( enabled && passes_cnt > 0 ) {
248                                        passes[passes_cnt - 1].mallocs++;
249                                        passes[passes_cnt - 1].n_allocs++;
250                                        passes[passes_cnt - 1].peak_allocs
251                                                = std::max(passes[passes_cnt - 1].peak_allocs, passes[passes_cnt - 1].n_allocs);
252                                }
253                                if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
254                                        stacktrace_stats[trace[stacktrace_depth]].mallocs++;
255                                }
256                                return __calloc( nelem, size );
257                        }
258
259                        void * realloc( void * ptr, size_t size ) {
260                                static auto __realloc = reinterpret_cast<void * (*)(void *, size_t)>(interpose_symbol( "realloc", nullptr ));
261                                void * s = __realloc( ptr, size );
262                                if ( enabled && s != ptr && passes_cnt > 0 ) {                  // did realloc get new storage ?
263                                        passes[passes_cnt - 1].mallocs++;
264                                        passes[passes_cnt - 1].frees++;
265                                } // if
266                                if ( stacktrace_stats_enabled && stacktrace_depth > 0) {
267                                        stacktrace_stats[trace[stacktrace_depth]].mallocs++;
268                                        stacktrace_stats[trace[stacktrace_depth]].frees++;
269                                }
270                                return s;
271                        }
272                }
273#endif
274        }
275}
Note: See TracBrowser for help on using the repository browser.