source: XIOS/trunk/src/memtrack.cpp @ 501

Last change on this file since 501 was 501, checked in by ymipsl, 8 years ago

Add licence copyright to all file ond directory src using the command :
svn propset -R copyright -F header_licence src

XIOS is now officialy under CeCILL licence

YM

  • Property copyright set to
    Software name : XIOS (Xml I/O Server)
    http://forge.ipsl.jussieu.fr/ioserver
    Creation date : January 2009
    Licence : CeCCIL version2
    see license file in root directory : Licence_CeCILL_V2-en.txt
    or http://www.cecill.info/licences/Licence_CeCILL_V2-en.html
    Holder : CEA/LSCE (Laboratoire des Sciences du CLimat et de l'Environnement)
    CNRS/IPSL (Institut Pierre Simon Laplace)
    Project Manager : Yann Meurdesoif
    yann.meurdesoif@cea.fr
  • Property svn:eol-style set to native
File size: 21.1 KB
Line 
1#ifdef XIOS_MEMTRACK
2/*
3Copyright (c) 2002, 2008 Curtis Bartley
4All rights reserved.
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions
7are met:
8
9- Redistributions of source code must retain the above copyright
10notice, this list of conditions and the following disclaimer.
11
12- Redistributions in binary form must reproduce the above copyright
13notice, this list of conditions and the following disclaimer in the
14documentation and/or other materials provided with the
15distribution.
16
17- Neither the name of Curtis Bartley nor the names of any other
18contributors may be used to endorse or promote products derived
19from this software without specific prior written permission.
20
21THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
30STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
32OF THE POSSIBILITY OF SUCH DAMAGE.
33*/
34
35/* ---------------------------------------- includes */
36
37#include <assert.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <algorithm>
41#include <cstring>
42#include <new>
43#include <iostream>
44#include <sstream>
45#include <string>
46#include <execinfo.h>
47
48#include "memtrack.hpp"
49#undef new    // IMPORTANT!
50
51extern "C"
52{
53  void addr2line(const char *file_name, char** addr, int naddr) ;
54}
55/* ------------------------------------------------------------ */
56/* -------------------- namespace MemTrack -------------------- */
57/* ------------------------------------------------------------ */
58
59namespace MemTrack
60{
61
62    /* ------------------------------------------------------------ */
63    /* --------------------- class BlockHeader -------------------- */
64    /* ------------------------------------------------------------ */
65
66    class BlockHeader
67    {
68        private:    // static member variables
69            static BlockHeader *ourFirstNode;
70   
71        private:    // member variables
72            BlockHeader *myPrevNode;
73            BlockHeader *myNextNode;
74            size_t myRequestedSize;
75            char const *myFilename;
76            int myLineNum;
77            char const *myTypeName;
78           
79            size_t stackSize ;
80            void* stackArray[20] ;
81
82        public:     // members
83            BlockHeader(size_t requestedSize);
84            ~BlockHeader();
85       
86            size_t GetRequestedSize() const { return myRequestedSize; }
87            char const *GetFilename() const { return myFilename; }
88            int GetLineNum() const { return myLineNum; }
89            char const *GetTypeName() const { return myTypeName; }
90       
91            void Stamp(char const *filename, int lineNum, char const *typeName);
92            void backTrace(void) ;
93       
94            static void AddNode(BlockHeader *node);
95            static void RemoveNode(BlockHeader *node);
96            static size_t CountBlocks();
97            static void GetBlocks(BlockHeader **blockHeaderPP);
98            static bool TypeGreaterThan(BlockHeader *header1, BlockHeader *header2);
99    };
100
101    /* ---------------------------------------- BlockHeader static member variables */
102
103    BlockHeader *BlockHeader::ourFirstNode = NULL;
104
105    /* ---------------------------------------- BlockHeader constructor */
106
107    BlockHeader::BlockHeader(size_t requestedSize)
108    {
109        myPrevNode = NULL;
110        myNextNode = NULL;
111        myRequestedSize = requestedSize;
112        myFilename = "[unknown]";
113        myLineNum = 0;
114        myTypeName = "[unknown]";
115        stackSize=backtrace(stackArray,20) ;
116    }
117
118    /* ---------------------------------------- BlockHeader destructor */
119
120    BlockHeader::~BlockHeader()
121    {
122    }
123       
124    /* ---------------------------------------- BlockHeader Stamp */
125
126    void BlockHeader::Stamp(char const *filename, int lineNum, char const *typeName)
127    {
128        myFilename = filename;
129        myLineNum = lineNum;
130        myTypeName = typeName;
131    }
132
133    void BlockHeader::backTrace(void)
134    {   
135       
136//        oss<<"addr2line -C -f -i -s -e ../bin/test_client.exe " ;
137        char *addr ;
138        char buffer[20] ;
139        addr=buffer ;
140        for(int i=0;i<stackSize;i++) 
141        {
142          std::ostringstream oss ;
143          oss<<stackArray[i] ;
144          strcpy(addr,oss.str().c_str()) ;
145          addr2line("/proc/self/exe",&addr,1) ;
146        }
147    }
148    /* ---------------------------------------- BlockHeader AddNode */
149
150    void BlockHeader::AddNode(BlockHeader *node)
151    {
152        assert(node != NULL);
153        assert(node->myPrevNode == NULL);
154        assert(node->myNextNode == NULL);
155
156        // If we have at least one node in the list ...       
157        if (ourFirstNode != NULL)
158        {
159            // ... make the new node the first node's predecessor.
160            assert(ourFirstNode->myPrevNode == NULL);
161            ourFirstNode->myPrevNode = node;
162        }
163
164        // Make the first node the new node's succesor.
165        node->myNextNode = ourFirstNode;
166
167        // Make the new node the first node.
168        ourFirstNode = node;
169    }
170
171    /* ---------------------------------------- BlockHeader RemoveNode */
172
173    void BlockHeader::RemoveNode(BlockHeader *node)
174    {
175        assert(node != NULL);
176        assert(ourFirstNode != NULL);
177
178        // If target node is the first node in the list...
179        if (ourFirstNode == node)
180        {
181            // ... make the target node's successor the first node.
182            assert(ourFirstNode->myPrevNode == NULL);
183            ourFirstNode = node->myNextNode;
184        }
185       
186        // Link target node's predecessor, if any, to its successor.
187        if (node->myPrevNode != NULL)
188        {
189            node->myPrevNode->myNextNode = node->myNextNode;
190        }
191       
192        // Link target node's successor, if any, to its predecessor.
193        if (node->myNextNode != NULL)
194        {
195            node->myNextNode->myPrevNode = node->myPrevNode;
196        }
197
198        // Clear target node's previous and next pointers.
199        node->myPrevNode = NULL;
200        node->myNextNode = NULL;
201    }
202
203    /* ---------------------------------------- BlockHeader CountBlocks */
204
205    size_t BlockHeader::CountBlocks()
206    {
207        size_t count = 0;
208        BlockHeader *currNode = ourFirstNode;
209        while (currNode != NULL)
210        {
211            count++;
212            currNode = currNode->myNextNode;
213        }
214        return count;
215    }
216
217    /* ---------------------------------------- BlockHeader GetBlocks */
218
219    void BlockHeader::GetBlocks(BlockHeader **blockHeaderPP)
220    {
221        BlockHeader *currNode = ourFirstNode;
222        while (currNode != NULL)
223        {
224            *blockHeaderPP = currNode;
225            blockHeaderPP++;
226            currNode = currNode->myNextNode;
227        }
228    }
229
230    /* ---------------------------------------- BlockHeader TypeGreaterThan */
231
232    bool BlockHeader::TypeGreaterThan(BlockHeader *header1, BlockHeader *header2)
233    {
234        return (strcmp(header1->myTypeName, header2->myTypeName) > 0);
235    }
236
237    /* ------------------------------------------------------------ */
238    /* ---------------------- class Signature --------------------- */
239    /* ------------------------------------------------------------ */
240
241    class Signature
242    {
243        private:    // constants
244            static const unsigned int SIGNATURE1 = 0xCAFEBABE;
245            static const unsigned int SIGNATURE2 = 0xFACEFACE;
246       
247        private:    // member variables
248            unsigned int mySignature1;
249            unsigned int mySignature2;
250           
251        public:        // construction/destruction
252            Signature() : mySignature1(SIGNATURE1), mySignature2(SIGNATURE2) {};
253            ~Signature() { mySignature1 = 0; mySignature2 = 0; }
254           
255        public:        // static member functions
256            static bool IsValidSignature(const Signature *pProspectiveSignature)
257            {
258                try
259                {
260                    if (pProspectiveSignature->mySignature1 != SIGNATURE1) return false;
261                    if (pProspectiveSignature->mySignature2 != SIGNATURE2) return false;
262                    return true;
263                }
264                catch (...)
265                {
266                    return false;
267                }
268            }
269    };
270
271    /* ------------------------------------------------------------ */
272    /* -------------------- address conversion -------------------- */
273    /* ------------------------------------------------------------ */
274
275    /* We divide the memory blocks we allocate into two "chunks", the
276     * "prolog chunk" where we store information about the allocation,
277     * and the "user chunk" which we return to the caller to use.
278     */
279
280    /* ---------------------------------------- alignment */
281
282    const size_t ALIGNMENT = 4;
283
284    /* If "value" (a memory size or offset) falls on an alignment boundary,
285     * then just return it.  Otherwise return the smallest number larger
286     * than "value" that falls on an alignment boundary.
287     */   
288
289    #define PAD_TO_ALIGNMENT_BOUNDARY(value) \
290        ((value) + ((ALIGNMENT - ((value) % ALIGNMENT)) % ALIGNMENT))
291
292    /* ---------------------------------------- chunk structs */
293   
294    /* We declare incomplete structures for each chunk, just to
295     * provide type safety.
296     */
297
298    struct PrologChunk;
299    struct UserChunk;
300
301    /* ---------------------------------------- chunk sizes and offsets */
302
303    const size_t SIZE_BlockHeader = PAD_TO_ALIGNMENT_BOUNDARY(sizeof(BlockHeader));
304    const size_t SIZE_Signature = PAD_TO_ALIGNMENT_BOUNDARY(sizeof(Signature));
305
306    const size_t OFFSET_BlockHeader = 0;
307    const size_t OFFSET_Signature = OFFSET_BlockHeader + SIZE_BlockHeader;
308    const size_t OFFSET_UserChunk = OFFSET_Signature + SIZE_Signature;
309   
310    const size_t SIZE_PrologChunk = OFFSET_UserChunk;
311
312    /* ---------------------------------------- GetUserAddress */
313
314    static UserChunk *GetUserAddress(PrologChunk *pProlog)
315    {
316        char *pchProlog = reinterpret_cast<char *>(pProlog);
317        char *pchUser = pchProlog + OFFSET_UserChunk;
318        UserChunk *pUser = reinterpret_cast<UserChunk *>(pchUser);
319        return pUser;
320    }
321
322    /* ---------------------------------------- GetPrologAddress */
323
324    static PrologChunk *GetPrologAddress(UserChunk *pUser)
325    {
326        char *pchUser = reinterpret_cast<char *>(pUser);
327        char *pchProlog = pchUser - OFFSET_UserChunk;
328        PrologChunk *pProlog = reinterpret_cast<PrologChunk *>(pchProlog);
329        return pProlog;
330    }
331
332    /* ---------------------------------------- GetHeaderAddress */
333
334    static BlockHeader *GetHeaderAddress(PrologChunk *pProlog)
335    {
336        char *pchProlog = reinterpret_cast<char *>(pProlog);
337        char *pchHeader = pchProlog + OFFSET_BlockHeader;
338        BlockHeader *pHeader = reinterpret_cast<BlockHeader *>(pchHeader);
339        return pHeader;
340    }
341
342    /* ---------------------------------------- GetSignatureAddress */
343
344    static Signature *GetSignatureAddress(PrologChunk *pProlog)
345    {
346        char *pchProlog = reinterpret_cast<char *>(pProlog);
347        char *pchSignature = pchProlog + OFFSET_Signature;
348        Signature *pSignature = reinterpret_cast<Signature *>(pchSignature);
349        return pSignature;
350    }
351
352    /* ------------------------------------------------------------ */
353    /* -------------- memory allocation and stamping -------------- */
354    /* ------------------------------------------------------------ */
355
356    /* ---------------------------------------- TrackMalloc */
357   
358    void *TrackMalloc(size_t size)
359    {
360        // Allocate the memory, including space for the prolog.
361        PrologChunk *pProlog = (PrologChunk *)malloc(SIZE_PrologChunk + size);
362       
363        // If the allocation failed, then return NULL.
364        if (pProlog == NULL) return NULL;
365       
366        // Use placement new to construct the block header in place.
367        BlockHeader *pBlockHeader = new (pProlog) BlockHeader(size);
368       
369        // Link the block header into the list of extant block headers.
370        BlockHeader::AddNode(pBlockHeader);
371       
372        // Use placement new to construct the signature in place.
373        Signature *pSignature = new (GetSignatureAddress(pProlog)) Signature;
374       
375        // Get the offset to the user chunk and return it.
376        UserChunk *pUser = GetUserAddress(pProlog);
377       
378        return pUser;
379    }
380
381    /* ---------------------------------------- TrackFree */
382   
383    void TrackFree(void *p)
384    {
385        // It's perfectly valid for "p" to be null; return if it is.
386        if (p == NULL) return;
387   
388        // Get the prolog address for this memory block.
389        UserChunk *pUser = reinterpret_cast<UserChunk *>(p);   
390        PrologChunk *pProlog = GetPrologAddress(pUser);
391       
392        // Check the signature, and if it's invalid, return immediately.
393        Signature *pSignature = GetSignatureAddress(pProlog);
394        if (!Signature::IsValidSignature(pSignature)) return;
395       
396        // Destroy the signature.
397        pSignature->~Signature();
398        pSignature = NULL;
399
400        // Unlink the block header from the list and destroy it.
401        BlockHeader *pBlockHeader = GetHeaderAddress(pProlog);
402        BlockHeader::RemoveNode(pBlockHeader);
403        pBlockHeader->~BlockHeader();
404        pBlockHeader = NULL;
405
406        // Free the memory block.   
407        free(pProlog);
408    }
409
410    /* ---------------------------------------- TrackStamp */
411
412    void TrackStamp(void *p, const MemStamp &stamp, char const *typeName)
413    {
414        // Get the header and signature address for this pointer.
415        UserChunk *pUser = reinterpret_cast<UserChunk *>(p);
416        PrologChunk *pProlog = GetPrologAddress(pUser);
417        BlockHeader *pHeader = GetHeaderAddress(pProlog);
418        Signature *pSignature = GetSignatureAddress(pProlog);
419
420        // If the signature is not valid, then return immediately.
421        if (!Signature::IsValidSignature(pSignature)) return;
422
423        // "Stamp" the information onto the header.
424        pHeader->Stamp(stamp.filename, stamp.lineNum, typeName);
425    }
426
427    /* ---------------------------------------- TrackDumpBlocks */
428
429    void TrackDumpBlocks()
430    {
431        // Get an array of pointers to all extant blocks.
432        size_t numBlocks = BlockHeader::CountBlocks();
433        BlockHeader **ppBlockHeader =
434            (BlockHeader **)calloc(numBlocks, sizeof(*ppBlockHeader));
435        BlockHeader::GetBlocks(ppBlockHeader);
436
437        // Dump information about the memory blocks.
438        printf("\n");
439        printf("=====================\n");
440        printf("Current Memory Blocks\n");
441        printf("=====================\n");
442        printf("\n");
443        for (size_t i = 0; i < numBlocks; i++)
444        {
445            BlockHeader *pBlockHeader = ppBlockHeader[i];
446            char const *typeName = pBlockHeader->GetTypeName();
447            size_t size = pBlockHeader->GetRequestedSize();
448            char const *fileName = pBlockHeader->GetFilename();
449            int lineNum = pBlockHeader->GetLineNum();
450            printf("*** #%-6d %5d bytes %-50s\n", i, size, typeName);
451            printf("... %s:%d\n", fileName, lineNum);
452            pBlockHeader->backTrace();
453        }
454
455        // Clean up.
456        free(ppBlockHeader);
457    }
458
459    /* ---------------------------------------- struct MemDigest */
460
461    struct MemDigest
462    {
463        char const *typeName;
464        int blockCount;
465        size_t totalSize;
466
467        static bool TotalSizeGreaterThan(const MemDigest &md1, const MemDigest &md2)
468            { return md1.totalSize > md2.totalSize; }
469    };
470
471
472    /* ---------------------------------------- SummarizeMemoryUsageForType */
473
474    static void SummarizeMemoryUsageForType(
475        MemDigest *pMemDigest,
476        BlockHeader **ppBlockHeader,
477        size_t startPost,
478        size_t endPost
479    )
480    {
481        pMemDigest->typeName = ppBlockHeader[startPost]->GetTypeName();
482        pMemDigest->blockCount = 0;
483        pMemDigest->totalSize = 0;
484        for (size_t i = startPost; i < endPost; i++)
485        {
486            pMemDigest->blockCount++;
487            pMemDigest->totalSize += ppBlockHeader[i]->GetRequestedSize();
488            assert(strcmp(ppBlockHeader[i]->GetTypeName(), pMemDigest->typeName) == 0);
489        }
490    }
491
492    /* ---------------------------------------- TrackListMemoryUsage */
493
494    void TrackListMemoryUsage()
495    {
496        // If there are no allocated blocks, then return now.
497        size_t numBlocks = BlockHeader::CountBlocks();
498        if (numBlocks == 0) return;
499
500        // Get an array of pointers to all extant blocks.
501        BlockHeader **ppBlockHeader =
502            (BlockHeader **)calloc(numBlocks, sizeof(*ppBlockHeader));
503        BlockHeader::GetBlocks(ppBlockHeader);
504
505        // Sort the blocks by type name.
506        std::sort(
507            ppBlockHeader,
508            ppBlockHeader + numBlocks,
509            BlockHeader::TypeGreaterThan
510        );
511
512        // Find out how many unique types we have.
513        size_t numUniqueTypes = 1;
514        for (size_t i = 1; i < numBlocks; i++)
515        {
516            char const *prevTypeName = ppBlockHeader[i - 1]->GetTypeName();
517            char const *currTypeName = ppBlockHeader[i]->GetTypeName();
518            if (strcmp(prevTypeName, currTypeName) != 0) numUniqueTypes++;
519        }
520
521        // Create an array of "digests" summarizing memory usage by type.
522        size_t startPost = 0;
523        size_t uniqueTypeIndex = 0;
524        MemDigest *pMemDigestArray =
525            (MemDigest *)calloc(numUniqueTypes, sizeof(*pMemDigestArray));
526        for (size_t i = 1; i <= numBlocks; i++)    // yes, less than or *equal* to
527        {
528            char const *prevTypeName = ppBlockHeader[i - 1]->GetTypeName();
529            char const *currTypeName = (i < numBlocks) ? ppBlockHeader[i]->GetTypeName() : "";
530            if (strcmp(prevTypeName, currTypeName) != 0)
531            {
532                size_t endPost = i;
533                SummarizeMemoryUsageForType(
534                    pMemDigestArray + uniqueTypeIndex,
535                    ppBlockHeader,
536                    startPost,
537                    endPost
538                );
539                startPost = endPost;
540                uniqueTypeIndex++;
541            }
542        }
543        assert(uniqueTypeIndex = numUniqueTypes);
544
545        // Sort the digests by total memory usage.
546        std::sort(
547            pMemDigestArray,
548            pMemDigestArray + numUniqueTypes,
549            MemDigest::TotalSizeGreaterThan
550        );
551
552        // Compute the grand total memory usage.
553        size_t grandTotalNumBlocks = 0;
554        size_t grandTotalSize = 0;
555        for (size_t i = 0; i < numUniqueTypes; i++)
556        {
557            grandTotalNumBlocks += pMemDigestArray[i].blockCount;
558            grandTotalSize += pMemDigestArray[i].totalSize;
559        }
560
561        // Dump the memory usage statistics.
562        printf("\n");
563        printf("-----------------------\n");
564        printf("Memory Usage Statistics\n");
565        printf("-----------------------\n");
566        printf("\n");
567        printf("%-50s%5s  %5s %7s %s \n", "allocated type", "blocks", "", "bytes", "");
568        printf("%-50s%5s  %5s %7s %s \n", "--------------", "------", "", "-----", "");
569
570        for (size_t i = 0; i < numUniqueTypes; i++)
571        {
572            MemDigest *pMD = pMemDigestArray + i;
573            size_t blockCount = pMD->blockCount;
574            double blockCountPct = 100.0 * blockCount / grandTotalNumBlocks;
575            size_t totalSize = pMD->totalSize;
576            double totalSizePct = 100.0 * totalSize / grandTotalSize;
577
578            printf(
579                "%-50s %5d %5.1f%% %7d %5.1f%%\n",
580                pMD->typeName,
581                blockCount,
582                blockCountPct,
583                totalSize,
584                totalSizePct
585            );
586        }
587        printf("%-50s %5s %5s  %7s %s \n", "--------", "-----", "", "-------", "");
588        printf("%-50s %5d %5s  %7d %s \n", "[totals]", grandTotalNumBlocks, "", grandTotalSize, "");
589
590        // Clean up.
591        free(ppBlockHeader);
592        free(pMemDigestArray);
593    }
594
595}    // namespace MemTrack
596
597/* ------------------------------------------------------------ */
598/* ---------------------- new and delete ---------------------- */
599/* ------------------------------------------------------------ */
600
601/* ---------------------------------------- operator new */
602
603void *operator new(size_t size)
604{
605    void *p = MemTrack::TrackMalloc(size);
606    if (p == NULL) throw std::bad_alloc();
607    return p;
608}
609
610/* ---------------------------------------- operator delete */
611
612void operator delete(void *p)
613{
614    MemTrack::TrackFree(p);
615}
616
617/* ---------------------------------------- operator new[] */
618
619void *operator new[](size_t size)
620{
621    void *p = MemTrack::TrackMalloc(size);
622    if (p == NULL) throw std::bad_alloc();
623    return p;
624}
625
626/* ---------------------------------------- operator delete[] */
627
628void operator delete[](void *p)
629{
630    MemTrack::TrackFree(p);
631}
632
633#endif
Note: See TracBrowser for help on using the repository browser.