source: XIOS/dev/branch_yushan_merged/src/memtrack.cpp @ 1134

Last change on this file since 1134 was 1134, checked in by yushan, 7 years ago

branch merged with trunk r1130

  • 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.2 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            #pragma omp threadprivate(ourFirstNode)
71   
72        private:    // member variables
73            BlockHeader *myPrevNode;
74            BlockHeader *myNextNode;
75            size_t myRequestedSize;
76            char const *myFilename;
77            int myLineNum;
78            char const *myTypeName;
79           
80            size_t stackSize ;
81            void* stackArray[20] ;
82
83        public:     // members
84            BlockHeader(size_t requestedSize);
85            ~BlockHeader();
86       
87            size_t GetRequestedSize() const { return myRequestedSize; }
88            char const *GetFilename() const { return myFilename; }
89            int GetLineNum() const { return myLineNum; }
90            char const *GetTypeName() const { return myTypeName; }
91       
92            void Stamp(char const *filename, int lineNum, char const *typeName);
93            void backTrace(void) ;
94       
95            static void AddNode(BlockHeader *node);
96            static void RemoveNode(BlockHeader *node);
97            static size_t CountBlocks();
98            static void GetBlocks(BlockHeader **blockHeaderPP);
99            static bool TypeGreaterThan(BlockHeader *header1, BlockHeader *header2);
100    };
101
102    /* ---------------------------------------- BlockHeader static member variables */
103
104    BlockHeader *BlockHeader::ourFirstNode = NULL;
105
106    /* ---------------------------------------- BlockHeader constructor */
107
108    BlockHeader::BlockHeader(size_t requestedSize)
109    {
110        myPrevNode = NULL;
111        myNextNode = NULL;
112        myRequestedSize = requestedSize;
113        myFilename = "[unknown]";
114        myLineNum = 0;
115        myTypeName = "[unknown]";
116        stackSize=backtrace(stackArray,20) ;
117    }
118
119    /* ---------------------------------------- BlockHeader destructor */
120
121    BlockHeader::~BlockHeader()
122    {
123    }
124       
125    /* ---------------------------------------- BlockHeader Stamp */
126
127    void BlockHeader::Stamp(char const *filename, int lineNum, char const *typeName)
128    {
129        myFilename = filename;
130        myLineNum = lineNum;
131        myTypeName = typeName;
132    }
133
134    void BlockHeader::backTrace(void)
135    {   
136       
137//        oss<<"addr2line -C -f -i -s -e ../bin/test_client.exe " ;
138        char *addr ;
139        char buffer[20] ;
140        addr=buffer ;
141        for(int i=0;i<stackSize;i++) 
142        {
143          std::ostringstream oss ;
144          oss<<stackArray[i] ;
145          strcpy(addr,oss.str().c_str()) ;
146          addr2line("/proc/self/exe",&addr,1) ;
147        }
148    }
149    /* ---------------------------------------- BlockHeader AddNode */
150
151    void BlockHeader::AddNode(BlockHeader *node)
152    {
153        assert(node != NULL);
154        assert(node->myPrevNode == NULL);
155        assert(node->myNextNode == NULL);
156
157        // If we have at least one node in the list ...       
158        if (ourFirstNode != NULL)
159        {
160            // ... make the new node the first node's predecessor.
161            assert(ourFirstNode->myPrevNode == NULL);
162            ourFirstNode->myPrevNode = node;
163        }
164
165        // Make the first node the new node's succesor.
166        node->myNextNode = ourFirstNode;
167
168        // Make the new node the first node.
169        ourFirstNode = node;
170    }
171
172    /* ---------------------------------------- BlockHeader RemoveNode */
173
174    void BlockHeader::RemoveNode(BlockHeader *node)
175    {
176        assert(node != NULL);
177        assert(ourFirstNode != NULL);
178
179        // If target node is the first node in the list...
180        if (ourFirstNode == node)
181        {
182            // ... make the target node's successor the first node.
183            assert(ourFirstNode->myPrevNode == NULL);
184            ourFirstNode = node->myNextNode;
185        }
186       
187        // Link target node's predecessor, if any, to its successor.
188        if (node->myPrevNode != NULL)
189        {
190            node->myPrevNode->myNextNode = node->myNextNode;
191        }
192       
193        // Link target node's successor, if any, to its predecessor.
194        if (node->myNextNode != NULL)
195        {
196            node->myNextNode->myPrevNode = node->myPrevNode;
197        }
198
199        // Clear target node's previous and next pointers.
200        node->myPrevNode = NULL;
201        node->myNextNode = NULL;
202    }
203
204    /* ---------------------------------------- BlockHeader CountBlocks */
205
206    size_t BlockHeader::CountBlocks()
207    {
208        size_t count = 0;
209        BlockHeader *currNode = ourFirstNode;
210        while (currNode != NULL)
211        {
212            count++;
213            currNode = currNode->myNextNode;
214        }
215        return count;
216    }
217
218    /* ---------------------------------------- BlockHeader GetBlocks */
219
220    void BlockHeader::GetBlocks(BlockHeader **blockHeaderPP)
221    {
222        BlockHeader *currNode = ourFirstNode;
223        while (currNode != NULL)
224        {
225            *blockHeaderPP = currNode;
226            blockHeaderPP++;
227            currNode = currNode->myNextNode;
228        }
229    }
230
231    /* ---------------------------------------- BlockHeader TypeGreaterThan */
232
233    bool BlockHeader::TypeGreaterThan(BlockHeader *header1, BlockHeader *header2)
234    {
235        return (strcmp(header1->myTypeName, header2->myTypeName) > 0);
236    }
237
238    /* ------------------------------------------------------------ */
239    /* ---------------------- class Signature --------------------- */
240    /* ------------------------------------------------------------ */
241
242    class Signature
243    {
244        private:    // constants
245            static const unsigned int SIGNATURE1 = 0xCAFEBABE;
246            static const unsigned int SIGNATURE2 = 0xFACEFACE;
247       
248        private:    // member variables
249            unsigned int mySignature1;
250            unsigned int mySignature2;
251           
252        public:        // construction/destruction
253            Signature() : mySignature1(SIGNATURE1), mySignature2(SIGNATURE2) {};
254            ~Signature() { mySignature1 = 0; mySignature2 = 0; }
255           
256        public:        // static member functions
257            static bool IsValidSignature(const Signature *pProspectiveSignature)
258            {
259                try
260                {
261                    if (pProspectiveSignature->mySignature1 != SIGNATURE1) return false;
262                    if (pProspectiveSignature->mySignature2 != SIGNATURE2) return false;
263                    return true;
264                }
265                catch (...)
266                {
267                    return false;
268                }
269            }
270    };
271
272    /* ------------------------------------------------------------ */
273    /* -------------------- address conversion -------------------- */
274    /* ------------------------------------------------------------ */
275
276    /* We divide the memory blocks we allocate into two "chunks", the
277     * "prolog chunk" where we store information about the allocation,
278     * and the "user chunk" which we return to the caller to use.
279     */
280
281    /* ---------------------------------------- alignment */
282
283    const size_t ALIGNMENT = 4;
284
285    /* If "value" (a memory size or offset) falls on an alignment boundary,
286     * then just return it.  Otherwise return the smallest number larger
287     * than "value" that falls on an alignment boundary.
288     */   
289
290    #define PAD_TO_ALIGNMENT_BOUNDARY(value) \
291        ((value) + ((ALIGNMENT - ((value) % ALIGNMENT)) % ALIGNMENT))
292
293    /* ---------------------------------------- chunk structs */
294   
295    /* We declare incomplete structures for each chunk, just to
296     * provide type safety.
297     */
298
299    struct PrologChunk;
300    struct UserChunk;
301
302    /* ---------------------------------------- chunk sizes and offsets */
303
304    const size_t SIZE_BlockHeader = PAD_TO_ALIGNMENT_BOUNDARY(sizeof(BlockHeader));
305    const size_t SIZE_Signature = PAD_TO_ALIGNMENT_BOUNDARY(sizeof(Signature));
306
307    const size_t OFFSET_BlockHeader = 0;
308    const size_t OFFSET_Signature = OFFSET_BlockHeader + SIZE_BlockHeader;
309    const size_t OFFSET_UserChunk = OFFSET_Signature + SIZE_Signature;
310   
311    const size_t SIZE_PrologChunk = OFFSET_UserChunk;
312
313    /* ---------------------------------------- GetUserAddress */
314
315    static UserChunk *GetUserAddress(PrologChunk *pProlog)
316    {
317        char *pchProlog = reinterpret_cast<char *>(pProlog);
318        char *pchUser = pchProlog + OFFSET_UserChunk;
319        UserChunk *pUser = reinterpret_cast<UserChunk *>(pchUser);
320        return pUser;
321    }
322
323    /* ---------------------------------------- GetPrologAddress */
324
325    static PrologChunk *GetPrologAddress(UserChunk *pUser)
326    {
327        char *pchUser = reinterpret_cast<char *>(pUser);
328        char *pchProlog = pchUser - OFFSET_UserChunk;
329        PrologChunk *pProlog = reinterpret_cast<PrologChunk *>(pchProlog);
330        return pProlog;
331    }
332
333    /* ---------------------------------------- GetHeaderAddress */
334
335    static BlockHeader *GetHeaderAddress(PrologChunk *pProlog)
336    {
337        char *pchProlog = reinterpret_cast<char *>(pProlog);
338        char *pchHeader = pchProlog + OFFSET_BlockHeader;
339        BlockHeader *pHeader = reinterpret_cast<BlockHeader *>(pchHeader);
340        return pHeader;
341    }
342
343    /* ---------------------------------------- GetSignatureAddress */
344
345    static Signature *GetSignatureAddress(PrologChunk *pProlog)
346    {
347        char *pchProlog = reinterpret_cast<char *>(pProlog);
348        char *pchSignature = pchProlog + OFFSET_Signature;
349        Signature *pSignature = reinterpret_cast<Signature *>(pchSignature);
350        return pSignature;
351    }
352
353    /* ------------------------------------------------------------ */
354    /* -------------- memory allocation and stamping -------------- */
355    /* ------------------------------------------------------------ */
356
357    /* ---------------------------------------- TrackMalloc */
358   
359    void *TrackMalloc(size_t size)
360    {
361        // Allocate the memory, including space for the prolog.
362        PrologChunk *pProlog = (PrologChunk *)malloc(SIZE_PrologChunk + size);
363       
364        // If the allocation failed, then return NULL.
365        if (pProlog == NULL) return NULL;
366       
367        // Use placement new to construct the block header in place.
368        BlockHeader *pBlockHeader = new (pProlog) BlockHeader(size);
369       
370        // Link the block header into the list of extant block headers.
371        BlockHeader::AddNode(pBlockHeader);
372       
373        // Use placement new to construct the signature in place.
374        Signature *pSignature = new (GetSignatureAddress(pProlog)) Signature;
375       
376        // Get the offset to the user chunk and return it.
377        UserChunk *pUser = GetUserAddress(pProlog);
378       
379        return pUser;
380    }
381
382    /* ---------------------------------------- TrackFree */
383   
384    void TrackFree(void *p)
385    {
386        // It's perfectly valid for "p" to be null; return if it is.
387        if (p == NULL) return;
388   
389        // Get the prolog address for this memory block.
390        UserChunk *pUser = reinterpret_cast<UserChunk *>(p);   
391        PrologChunk *pProlog = GetPrologAddress(pUser);
392       
393        // Check the signature, and if it's invalid, return immediately.
394        Signature *pSignature = GetSignatureAddress(pProlog);
395        if (!Signature::IsValidSignature(pSignature)) return;
396       
397        // Destroy the signature.
398        pSignature->~Signature();
399        pSignature = NULL;
400
401        // Unlink the block header from the list and destroy it.
402        BlockHeader *pBlockHeader = GetHeaderAddress(pProlog);
403        BlockHeader::RemoveNode(pBlockHeader);
404        pBlockHeader->~BlockHeader();
405        pBlockHeader = NULL;
406
407        // Free the memory block.   
408        free(pProlog);
409    }
410
411    /* ---------------------------------------- TrackStamp */
412
413    void TrackStamp(void *p, const MemStamp &stamp, char const *typeName)
414    {
415        // Get the header and signature address for this pointer.
416        UserChunk *pUser = reinterpret_cast<UserChunk *>(p);
417        PrologChunk *pProlog = GetPrologAddress(pUser);
418        BlockHeader *pHeader = GetHeaderAddress(pProlog);
419        Signature *pSignature = GetSignatureAddress(pProlog);
420
421        // If the signature is not valid, then return immediately.
422        if (!Signature::IsValidSignature(pSignature)) return;
423
424        // "Stamp" the information onto the header.
425        pHeader->Stamp(stamp.filename, stamp.lineNum, typeName);
426    }
427
428    /* ---------------------------------------- TrackDumpBlocks */
429
430    void TrackDumpBlocks()
431    {
432        // Get an array of pointers to all extant blocks.
433        size_t numBlocks = BlockHeader::CountBlocks();
434        BlockHeader **ppBlockHeader =
435            (BlockHeader **)calloc(numBlocks, sizeof(*ppBlockHeader));
436        BlockHeader::GetBlocks(ppBlockHeader);
437
438        // Dump information about the memory blocks.
439        printf("\n");
440        printf("=====================\n");
441        printf("Current Memory Blocks\n");
442        printf("=====================\n");
443        printf("\n");
444        for (size_t i = 0; i < numBlocks; i++)
445        {
446            BlockHeader *pBlockHeader = ppBlockHeader[i];
447            char const *typeName = pBlockHeader->GetTypeName();
448            size_t size = pBlockHeader->GetRequestedSize();
449            char const *fileName = pBlockHeader->GetFilename();
450            int lineNum = pBlockHeader->GetLineNum();
451            printf("*** #%-6d %5d bytes %-50s\n", i, size, typeName);
452            printf("... %s:%d\n", fileName, lineNum);
453            pBlockHeader->backTrace();
454        }
455
456        // Clean up.
457        free(ppBlockHeader);
458    }
459
460    /* ---------------------------------------- struct MemDigest */
461
462    struct MemDigest
463    {
464        char const *typeName;
465        int blockCount;
466        size_t totalSize;
467
468        static bool TotalSizeGreaterThan(const MemDigest &md1, const MemDigest &md2)
469            { return md1.totalSize > md2.totalSize; }
470    };
471
472
473    /* ---------------------------------------- SummarizeMemoryUsageForType */
474
475    static void SummarizeMemoryUsageForType(
476        MemDigest *pMemDigest,
477        BlockHeader **ppBlockHeader,
478        size_t startPost,
479        size_t endPost
480    )
481    {
482        pMemDigest->typeName = ppBlockHeader[startPost]->GetTypeName();
483        pMemDigest->blockCount = 0;
484        pMemDigest->totalSize = 0;
485        for (size_t i = startPost; i < endPost; i++)
486        {
487            pMemDigest->blockCount++;
488            pMemDigest->totalSize += ppBlockHeader[i]->GetRequestedSize();
489            assert(strcmp(ppBlockHeader[i]->GetTypeName(), pMemDigest->typeName) == 0);
490        }
491    }
492
493    /* ---------------------------------------- TrackListMemoryUsage */
494
495    void TrackListMemoryUsage()
496    {
497        // If there are no allocated blocks, then return now.
498        size_t numBlocks = BlockHeader::CountBlocks();
499        if (numBlocks == 0) return;
500
501        // Get an array of pointers to all extant blocks.
502        BlockHeader **ppBlockHeader =
503            (BlockHeader **)calloc(numBlocks, sizeof(*ppBlockHeader));
504        BlockHeader::GetBlocks(ppBlockHeader);
505
506        // Sort the blocks by type name.
507        std::sort(
508            ppBlockHeader,
509            ppBlockHeader + numBlocks,
510            BlockHeader::TypeGreaterThan
511        );
512
513        // Find out how many unique types we have.
514        size_t numUniqueTypes = 1;
515        for (size_t i = 1; i < numBlocks; i++)
516        {
517            char const *prevTypeName = ppBlockHeader[i - 1]->GetTypeName();
518            char const *currTypeName = ppBlockHeader[i]->GetTypeName();
519            if (strcmp(prevTypeName, currTypeName) != 0) numUniqueTypes++;
520        }
521
522        // Create an array of "digests" summarizing memory usage by type.
523        size_t startPost = 0;
524        size_t uniqueTypeIndex = 0;
525        MemDigest *pMemDigestArray =
526            (MemDigest *)calloc(numUniqueTypes, sizeof(*pMemDigestArray));
527        for (size_t i = 1; i <= numBlocks; i++)    // yes, less than or *equal* to
528        {
529            char const *prevTypeName = ppBlockHeader[i - 1]->GetTypeName();
530            char const *currTypeName = (i < numBlocks) ? ppBlockHeader[i]->GetTypeName() : "";
531            if (strcmp(prevTypeName, currTypeName) != 0)
532            {
533                size_t endPost = i;
534                SummarizeMemoryUsageForType(
535                    pMemDigestArray + uniqueTypeIndex,
536                    ppBlockHeader,
537                    startPost,
538                    endPost
539                );
540                startPost = endPost;
541                uniqueTypeIndex++;
542            }
543        }
544        assert(uniqueTypeIndex = numUniqueTypes);
545
546        // Sort the digests by total memory usage.
547        std::sort(
548            pMemDigestArray,
549            pMemDigestArray + numUniqueTypes,
550            MemDigest::TotalSizeGreaterThan
551        );
552
553        // Compute the grand total memory usage.
554        size_t grandTotalNumBlocks = 0;
555        size_t grandTotalSize = 0;
556        for (size_t i = 0; i < numUniqueTypes; i++)
557        {
558            grandTotalNumBlocks += pMemDigestArray[i].blockCount;
559            grandTotalSize += pMemDigestArray[i].totalSize;
560        }
561
562        // Dump the memory usage statistics.
563        printf("\n");
564        printf("-----------------------\n");
565        printf("Memory Usage Statistics\n");
566        printf("-----------------------\n");
567        printf("\n");
568        printf("%-50s%5s  %5s %7s %s \n", "allocated type", "blocks", "", "bytes", "");
569        printf("%-50s%5s  %5s %7s %s \n", "--------------", "------", "", "-----", "");
570
571        for (size_t i = 0; i < numUniqueTypes; i++)
572        {
573            MemDigest *pMD = pMemDigestArray + i;
574            size_t blockCount = pMD->blockCount;
575            double blockCountPct = 100.0 * blockCount / grandTotalNumBlocks;
576            size_t totalSize = pMD->totalSize;
577            double totalSizePct = 100.0 * totalSize / grandTotalSize;
578
579            printf(
580                "%-50s %5d %5.1f%% %7d %5.1f%%\n",
581                pMD->typeName,
582                blockCount,
583                blockCountPct,
584                totalSize,
585                totalSizePct
586            );
587        }
588        printf("%-50s %5s %5s  %7s %s \n", "--------", "-----", "", "-------", "");
589        printf("%-50s %5d %5s  %7d %s \n", "[totals]", grandTotalNumBlocks, "", grandTotalSize, "");
590
591        // Clean up.
592        free(ppBlockHeader);
593        free(pMemDigestArray);
594    }
595
596}    // namespace MemTrack
597
598/* ------------------------------------------------------------ */
599/* ---------------------- new and delete ---------------------- */
600/* ------------------------------------------------------------ */
601
602/* ---------------------------------------- operator new */
603
604void *operator new(size_t size)
605{
606    void *p = MemTrack::TrackMalloc(size);
607    if (p == NULL) throw std::bad_alloc();
608    return p;
609}
610
611/* ---------------------------------------- operator delete */
612
613void operator delete(void *p)
614{
615    MemTrack::TrackFree(p);
616}
617
618/* ---------------------------------------- operator new[] */
619
620void *operator new[](size_t size)
621{
622    void *p = MemTrack::TrackMalloc(size);
623    if (p == NULL) throw std::bad_alloc();
624    return p;
625}
626
627/* ---------------------------------------- operator delete[] */
628
629void operator delete[](void *p)
630{
631    MemTrack::TrackFree(p);
632}
633
634#endif
Note: See TracBrowser for help on using the repository browser.