| /********************************************************************** |
| // @@@ START COPYRIGHT @@@ |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| // |
| // @@@ END COPYRIGHT @@@ |
| **********************************************************************/ |
| /* -*-C++-*- |
| **************************************************************************** |
| * |
| * File: HeapLog.cpp |
| * |
| * Description: Track object allocations and deallocations. |
| * |
| * Created: 3/1/99 |
| * Language: C++ |
| * |
| * |
| * |
| **************************************************************************** |
| */ |
| |
| #include <iostream> |
| #include <iomanip> |
| #include <sstream> |
| #include <assert.h> |
| #include <limits.h> |
| #include <string.h> |
| |
| #include "HeapLog.h" |
| #include "HeapLogImpl.h" |
| |
| using namespace std; |
| |
| #define HEAP_NUM_BASE 19 |
| #define HEAP_NUM_RESERVED 9 |
| #define HEAP_NUM_NULL -1 |
| #define FETCH_EOF 1 |
| // ----------------------------------------------------------------------- |
| // Initialize static members. |
| // ----------------------------------------------------------------------- |
| HeapLog *HeapLogRoot::log = NULL; |
| Lng32 HeapLogRoot::track = FALSE; |
| Lng32 HeapLogRoot::trackDealloc = FALSE; |
| Lng32 HeapLogRoot::maxHeapNum = HEAP_NUM_RESERVED; |
| |
| static void BreakNow() |
| { |
| DebugBreak(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Break to show call stack when the break condition is satisfied. |
| // ----------------------------------------------------------------------- |
| static void BreakOnLeak( const Lng32 heapNum |
| , const Lng32 indx |
| , const Lng32 size |
| ) |
| { |
| static Lng32 object_index = -1; |
| static Lng32 object_size = -1; |
| static Lng32 heap_id = -1; |
| // ------------------------------------------------------------------ |
| // To catch memory leaks one by one: |
| // 1. Set or enable a breakpoint at the if-statement. |
| // |
| // 2. When the breakpoint is hit, modify object_index, object_size, |
| // and/or heap_id for locating the first leak in a prior leak report. |
| // |
| // 3. Disable the breakpoint and resume the execution. |
| // |
| // 4. When the code invokes debugger again, check if |
| // heapNum matches HEAP_ID, indx approximates to OBJECT_INDEX, and |
| // size matches OBJECT_SIZE from that in the leak report. |
| // If so, the call stack shows the corresponding leak location. |
| // |
| // 5. Modify object_index, object_size, and/or heap_id for |
| // locating the next leak. |
| // |
| // 6. Repeat 4 and 5. |
| // ------------------------------------------------------------------ |
| if ((object_index >= 0) && |
| (indx >= object_index) && |
| (size == object_size || object_size < 0) && |
| (heapNum == heap_id || heap_id < 0)) |
| { |
| BreakNow(); // invokes debugger |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Add a log entry to track allocations. |
| // ----------------------------------------------------------------------- |
| void HeapLogRoot::addEntry( void * addr |
| , Lng32 size |
| , Lng32 &heapNum |
| , const char *heapName |
| ) |
| { |
| if ((heapNum >= MAX_NUM_HEAPS) || |
| (log != NULL && |
| log->disableLevel_ > 0)) |
| return; |
| |
| if (log == NULL) |
| initLog(); |
| |
| if (heapNum < 0) |
| { // assign a unique ID. |
| heapNum = assignHeapNum(); |
| } |
| |
| assert(heapNum > 0); |
| Lng32 indx = log->addEntry(addr, size, heapNum, heapName); |
| BreakOnLeak(heapNum, indx, size); |
| |
| return; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Reset a log entry associated with this object addr. |
| // Called when an object is deleted. |
| // ----------------------------------------------------------------------- |
| void HeapLogRoot::deleteEntry( void * addr |
| , Lng32 heapNum |
| ) |
| { |
| if ((heapNum < 0) || |
| (heapNum >= MAX_NUM_HEAPS) || |
| (log == NULL) || |
| (log->disableLevel_ > 0)) |
| return; |
| |
| // Locate the log segment for the heap. |
| HeapLogSegment &seg = log->header_[heapNum]; |
| if (seg.object_ == NULL) |
| return; |
| |
| // Find the entry with matching addr. |
| Lng32 i; |
| if (seg.deleted_ == -1) |
| { |
| for (i = seg.last_; i >= 0; i--) |
| if ((seg.object_[i].size > 0) && |
| (seg.object_[i].addr == addr)) |
| goto cont; |
| } |
| else |
| { // Search the neighborhood of last deleted entry |
| // indicated by seg.deleted_ |
| Lng32 left = seg.deleted_; |
| Lng32 right = seg.deleted_ + 1; |
| Lng32 limit; |
| Lng32 RANGE = 3; |
| |
| while((left >= 0) || (right <= seg.last_)) |
| { |
| if (left >= 0) |
| { // search the left side. |
| limit = left - RANGE; |
| if (limit < 0) |
| limit = 0; |
| for (i = left; i >= limit; i--) |
| if ((seg.object_[i].size > 0) && |
| (seg.object_[i].addr == addr)) |
| goto cont; |
| left = limit - 1; |
| } |
| if (right <= seg.last_) |
| { // search the right side. |
| limit = right + RANGE; |
| if (limit > seg.last_) |
| limit = seg.last_; |
| for (i = right; i <= limit; i++) |
| if ((seg.object_[i].size > 0) && |
| (seg.object_[i].addr == addr)) |
| goto cont; |
| right = limit + 1; |
| } |
| RANGE = 50; |
| } // while |
| } |
| // not found |
| return; |
| |
| cont: |
| // Found. |
| // Negate the size. |
| // Remember the entry in seg.deleted_. |
| seg.object_[i].size *= -1; |
| assert(seg.object_[i].size < 0); |
| |
| seg.usageCount_--; |
| seg.totalSize_ += seg.object_[i].size; |
| seg.deleted_ = i; |
| |
| if (seg.usageCount_ > seg.last_ * 0.5) |
| return; |
| |
| // Compress the log segment when usage rate drops below 0.5. |
| Lng32 freePos = 0; |
| for (i = 0; i <= seg.last_; i++) |
| { |
| if (seg.object_[i].size <= 0) |
| continue; |
| |
| if (freePos < i) |
| { // Copy and free slot i. |
| seg.object_[freePos] = seg.object_[i]; |
| seg.object_[i].size *= -1; |
| } |
| freePos++; |
| } |
| if (freePos < seg.last_) |
| seg.last_ = freePos; |
| // Reposition the last deleted entry. |
| seg.deleted_ = seg.last_ >> 2; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Delete a log segment associated with a heap. |
| // Called when a heap is deleted or reinitialized. |
| // ----------------------------------------------------------------------- |
| void HeapLogRoot::deleteLogSegment(Lng32 heapNum, NABoolean setfree) |
| { |
| if ((heapNum < 0) || |
| (heapNum >= MAX_NUM_HEAPS) || |
| (log == NULL)) |
| return; |
| |
| HeapLogSegment &seg = log->header_[heapNum]; |
| if (seg.object_ != NULL) |
| { |
| HEAPLOG_OFF(); |
| delete [] seg.object_; |
| HEAPLOG_ON(); |
| seg.object_ = NULL; |
| } |
| seg.slotCount_ = 0; |
| seg.usageCount_ = 0; |
| seg.last_ = -1; |
| seg.deleted_ = -1; |
| seg.totalSize_ = 0; |
| // Check if the entry can be reassigned to other heaps. |
| if (setfree) |
| seg.free_ = TRUE; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Session control the heap log. |
| // ----------------------------------------------------------------------- |
| void HeapLogRoot::control(HeapControlEnum option) |
| { |
| #ifndef NA_DEBUG_HEAPLOG |
| track = |
| trackDealloc = FALSE; |
| return; |
| #endif |
| switch(option) { |
| case LOG_START: |
| { |
| track = |
| trackDealloc = TRUE; |
| initLog(); |
| return; |
| } |
| case LOG_DELETE_ONLY: |
| { |
| track = FALSE; |
| trackDealloc = TRUE; |
| return; |
| } |
| case LOG_RESET_START: |
| { |
| reset(); |
| track = |
| trackDealloc = TRUE; |
| initLog(); |
| return; |
| } |
| case LOG_DISABLE: |
| { |
| track = |
| trackDealloc = FALSE; |
| return; |
| } |
| case LOG_RESET: |
| { |
| reset(); |
| return; |
| } |
| case LOG_RESET_DISABLE: |
| { |
| reset(); |
| track = |
| trackDealloc = FALSE; |
| return; |
| } |
| default: |
| break; |
| } |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Do session control based on syntax clauses. |
| // ----------------------------------------------------------------------- |
| void HeapLogRoot::control2(ULng32 flags, ULng32 mask) |
| { |
| if ((flags & mask) == 0) |
| { |
| control(LOG_RESET_DISABLE); |
| return; |
| } |
| if (flags & LeakDescribe::FLAG_CONTINUE) |
| { |
| control(LOG_START); |
| return; |
| } |
| if (flags & LeakDescribe::FLAG_OFF) |
| { |
| control(LOG_RESET_DISABLE); |
| return; |
| } |
| control(LOG_RESET_START); |
| return; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Disable logging. |
| // ----------------------------------------------------------------------- |
| void HeapLogRoot::disable(NABoolean b) |
| { |
| if (log == NULL) |
| return; |
| if (b) |
| log->disableLevel_++; |
| else |
| log->disableLevel_--; |
| assert(log->disableLevel_ >= 0); |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Display heap log. |
| // Open a console window if none. |
| // If prompt is TRUE, prompt the user to enter a character n, c, or o. |
| // ----------------------------------------------------------------------- |
| void HeapLogRoot::display( NABoolean prompt |
| , Lng32 sqlci |
| ) |
| { |
| } |
| |
| // --------------------------------------------------------------------- |
| // Called by ARKCMP to allocate buffer. |
| // --------------------------------------------------------------------- |
| Lng32 HeapLogRoot::getPackSize() |
| { |
| Lng32 heapCount = 0; |
| Lng32 objCount = 0; |
| if (log == NULL) |
| return 8; |
| for (Int32 i = 0; i <= maxHeapNum ; i++) |
| { |
| if (log->header_[i].usageCount_ > 0) |
| { |
| heapCount++; |
| objCount += log->header_[i].usageCount_; |
| } |
| } |
| // must be in multiple of 8. |
| Lng32 size = (heapCount * 3 + objCount + 25) * HeapLog::DISPLAY_LEN; |
| return size; |
| } |
| |
| // --------------------------------------------------------------------- |
| // Pack log data into a flat buffer. |
| // --------------------------------------------------------------------- |
| void HeapLogRoot::pack(char *buf, ULng32 flags) |
| { |
| if (HeapLogRoot::log == NULL || |
| ((flags & LeakDescribe::FLAG_ARKCMP) == 0) |
| ) |
| { // eof, pack null chars. |
| buf[0] = buf[1] = '\0'; |
| control2(flags, LeakDescribe::FLAG_ARKCMP); |
| return; |
| } |
| |
| if (flags & LeakDescribe::FLAG_PROMPT) |
| { // Display log data and prompt the user. |
| display(TRUE, 0 /* arkcmp */); |
| // eof |
| buf[0] = buf[1] = '\0'; |
| return; |
| } |
| |
| // Pack all lines, each of which is terminated with '\0'. |
| Lng32 len = 0; |
| log->status_ = HeapLog::PHASE_1; |
| while(log->fetchLine(&buf[len], 0/*arkcmp*/) != FETCH_EOF) |
| { |
| len += strlen(&buf[len]) + 1; |
| } |
| buf[len++] = '\0'; |
| buf[len] = '\0'; |
| control2(flags, LeakDescribe::FLAG_ARKCMP); |
| return; |
| } |
| |
| // --------------------------------------------------------------------- |
| // Fetch log line by line called by executor. |
| // Optionally prompt the user for input. |
| // return FETCH_EOF if eof else 0. |
| // --------------------------------------------------------------------- |
| Lng32 HeapLogRoot::fetchLine( char *buf |
| , ULng32 flags |
| , char *packdata /*=NULL*/ |
| , Lng32 datalen /*=0*/ |
| ) |
| { |
| if ((flags & (LeakDescribe::FLAG_SQLCI | LeakDescribe::FLAG_ARKCMP)) == 0) |
| { // neither sqlci nor arkcmp. Done. |
| control(LOG_RESET_DISABLE); |
| return FETCH_EOF; |
| } |
| |
| // Display report in ARKCMP window. |
| if ((flags & LeakDescribe::FLAG_PROMPT) && |
| !(flags & LeakDescribe::FLAG_SQLCI)) |
| return FETCH_EOF; |
| |
| if (log == NULL) |
| initLog(); |
| |
| log->fetchInit(flags, packdata, datalen); |
| if (flags & LeakDescribe::FLAG_PROMPT) |
| { // Display log data and prompt the user. |
| display(TRUE, 1 /* sqlci */); |
| // eof |
| *(short*)buf = 0; |
| return FETCH_EOF; |
| } |
| |
| Lng32 error = log->fetchLine(&buf[2], 1 /*sqlci*/); |
| *((short*)buf) = strlen(&buf[2]); |
| if (error == FETCH_EOF) |
| control2(flags, LeakDescribe::FLAG_SQLCI); |
| return error; |
| } |
| |
| // --------------------------------------------------------------------- |
| // Called by heap constructor. |
| // --------------------------------------------------------------------- |
| Lng32 HeapLogRoot::assignHeapNum() |
| { |
| if (log == NULL) |
| { // Haven't started logging. |
| if (maxHeapNum >= HEAP_NUM_BASE) |
| return HEAP_NUM_NULL; |
| else |
| { // up to HEAP_NUM_BASE |
| ++maxHeapNum; |
| return maxHeapNum; |
| } |
| } |
| // Find a free entry. |
| for(Lng32 i = log->currHeapNum_ + 1; i < MAX_NUM_HEAPS; i++) |
| { |
| if (log->header_[i].free_) |
| { |
| log->currHeapNum_ = i; |
| log->header_[i].free_ = FALSE; |
| if (i > maxHeapNum) |
| maxHeapNum = i; |
| assert(i > HEAP_NUM_RESERVED); |
| return i; |
| } |
| } |
| log->currHeapNum_ = maxHeapNum = MAX_NUM_HEAPS - 1; |
| log->overflow_ = TRUE; |
| return MAX_NUM_HEAPS; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Init log after logging starts. |
| // ----------------------------------------------------------------------- |
| void HeapLogRoot::initLog() |
| { |
| if (log != NULL) |
| return; |
| Lng32 old = track; |
| track = FALSE; |
| log = new HeapLog; |
| track = old; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Reset log. |
| // ----------------------------------------------------------------------- |
| void HeapLogRoot::reset() |
| { |
| if (log == NULL) |
| return; |
| NABoolean setFree = log->overflow_; |
| log->currHeapNum_ = HEAP_NUM_BASE; |
| log->objIndex_ = -1; |
| log->disableLevel_ = 0; |
| log->overflow_ = FALSE; |
| log->close(); |
| |
| // Find the maximun heap number that is still in-use. |
| for (Lng32 i = 0; i < MAX_NUM_HEAPS ; i++) |
| { |
| if (log->header_[i].free_) |
| continue; |
| deleteLogSegment(i, (i <= HEAP_NUM_BASE ? FALSE : setFree)); |
| maxHeapNum = i; |
| } |
| if (maxHeapNum < HEAP_NUM_RESERVED) |
| maxHeapNum = HEAP_NUM_RESERVED; |
| if (setFree) |
| maxHeapNum = HEAP_NUM_BASE; |
| } |
| |
| // --------------------------------------------------------------------- |
| // Constructor and destructor. |
| // Each log segment has a corresponding heap. |
| // --------------------------------------------------------------------- |
| HeapLogSegment::HeapLogSegment() |
| : object_(NULL), |
| slotCount_(0), usageCount_(0), |
| last_(-1), deleted_(-1), |
| totalSize_(0), |
| free_(TRUE) |
| { |
| name_[0] = '\0'; |
| } |
| |
| HeapLogSegment::~HeapLogSegment() |
| { |
| HEAPLOG_OFF(); |
| delete [] object_; |
| HEAPLOG_ON(); |
| |
| object_ = NULL; |
| slotCount_ = 0; |
| usageCount_ = 0; |
| last_ = -1; |
| totalSize_ = 0; |
| free_ = TRUE; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Constructor. |
| // ----------------------------------------------------------------------- |
| HeapLog::HeapLog() |
| : currHeapNum_(HEAP_NUM_BASE), |
| objIndex_(-1), |
| disableLevel_(0), |
| overflow_(FALSE), |
| |
| heading_(NULL), |
| h_(0), |
| s_(0), |
| objCount_(0), |
| status_(PHASE_CLOSED), |
| packdata_(NULL), |
| datalen_(0), |
| currlen_(0) |
| { |
| // heading text for display. |
| static const char *headings[11] = { |
| |
| "ARKCMP Process\n==============\n\n", |
| "SQLCI Process\n=============\n\n", |
| "Objects not Deallocated:\n------------------------\n\n", |
| " HEAP_ID OBJECT_INDEX OBJECT_SIZE (BYTES)", |
| " ------------ ------------- -------------------\n", |
| "Summary of Leaks:\n-----------------\n\n", |
| " HEAP_ID LEAK_COUNT LEAK_SIZE (BYTES) HEAP_NAME", |
| " ------------ ------------- ------------------- -----------------------\n", |
| "\n*** Error: The total number of heaps in ARKCMP exceeds 5000 limit.", |
| "\n*** Error: The total number of heaps in SQLCI exceeds 5000 limit.", |
| "\nPlease reduce the number statements within a tracking session and retry." |
| }; |
| |
| heading_ = headings; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Add a log entry. |
| // Fill in heapNum, object index, object size, object addr. |
| // Called when a new object is allocated in a heap. |
| // ----------------------------------------------------------------------- |
| Lng32 HeapLog::addEntry( void * addr |
| , Lng32 size |
| , Lng32 heapNum |
| , const char *heapName |
| ) |
| { |
| HeapLogSegment &seg = header_[heapNum]; |
| if (seg.object_ == NULL) |
| { |
| HEAPLOG_OFF(); |
| seg.object_ = new HeapLogEntry[INITIAL_OBJECT_SLOTS]; |
| HEAPLOG_ON(); |
| |
| if (heapName != NULL) |
| { |
| strncpy(seg.name_, heapName, 24); |
| seg.name_[24] = 0; |
| } |
| else |
| seg.name_[0] = '\0'; |
| seg.slotCount_ = INITIAL_OBJECT_SLOTS; |
| seg.usageCount_ = 0; |
| seg.last_ = -1; |
| } |
| seg.free_ = FALSE; |
| objIndex_++; |
| seg.usageCount_++; |
| seg.last_++; |
| if (seg.last_ >= seg.slotCount_) |
| { // resize the log segment. |
| HEAPLOG_OFF(); |
| HeapLogEntry *pNewLog = seg.object_; |
| seg.object_ = new HeapLogEntry[seg.slotCount_ * 2]; |
| for (Int32 i = 0; i < seg.slotCount_; i++) |
| seg.object_[i] = pNewLog[i]; |
| delete [] pNewLog; |
| HEAPLOG_ON(); |
| seg.slotCount_ *= 2; |
| } |
| |
| seg.object_[seg.last_].indx = objIndex_; |
| seg.object_[seg.last_].size = size; |
| seg.object_[seg.last_].addr = addr; |
| seg.totalSize_ += size; |
| |
| return objIndex_; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Prepare to fetch packdata returned from arkcmp. |
| // ----------------------------------------------------------------------- |
| Lng32 HeapLog::fetchInit( ULng32 flags |
| , char *packdata |
| , Lng32 datalen |
| ) |
| { |
| if (status_ != PHASE_CLOSED) |
| // fetchInit has been performed. |
| return 0; |
| |
| if ((flags & LeakDescribe::FLAG_SQLCI) == 0) |
| HeapLogRoot::control(LOG_RESET_DISABLE); |
| |
| if (datalen && strlen(packdata) > 0) |
| { |
| packdata_ = packdata; |
| datalen_ = datalen; |
| } |
| |
| status_ = PHASE_1; |
| return 0; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Reset some data upon FETCH_EOF. |
| // ----------------------------------------------------------------------- |
| void HeapLog::close() |
| { |
| packdata_ = NULL; |
| datalen_ = 0; |
| currlen_ = 0; |
| |
| s_ = h_ = 0; |
| status_ = PHASE_CLOSED; |
| objCount_ = 0; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Fetch log data line by line. Each line is terminated by '\0'. |
| // return FETCH_EOF if eof, else 0. |
| // ----------------------------------------------------------------------- |
| Lng32 HeapLog::fetchLine( char *buf |
| , Lng32 sqlci |
| ) |
| { |
| assert(status_ != PHASE_CLOSED); |
| |
| if (datalen_ > 0) |
| { // Fetch log data from ARKCMP. |
| Lng32 s = strlen(packdata_) + 1; |
| if (currlen_ >= datalen_ || |
| s == 1) |
| { // eof |
| *buf = '\0'; |
| close(); |
| status_ = PHASE_1; |
| } |
| else |
| { |
| strncpy(buf, packdata_, s); |
| packdata_ += s; |
| currlen_ += s; |
| return 0; |
| } |
| } |
| |
| ostringstream oss; |
| if (objCount_ == 0 && status_ != PHASE_EOF) |
| { // No data to report. |
| for (Int32 i = 0; i <= HeapLogRoot::maxHeapNum; i++) |
| objCount_ += header_[i].usageCount_; |
| if (objCount_ == 0) |
| if (overflow_) |
| { |
| oss.str().clear(); |
| oss << heading_[8+sqlci] << heading_[10] << ends; |
| strncpy(buf, oss.str().c_str(), DISPLAY_LEN*2-1); |
| buf[DISPLAY_LEN*2-1] = 0; |
| status_ = PHASE_EOF; |
| return 0; |
| } |
| else |
| { |
| *buf = '\0'; |
| close(); |
| return FETCH_EOF; |
| } |
| } |
| |
| HeapLogSegment *seg; |
| NABoolean first; |
| |
| // Return log data for executor. |
| switch(status_) |
| { |
| case PHASE_1: // Heading |
| oss.str().clear(); |
| oss << heading_[sqlci] |
| << heading_[2] |
| << heading_[3] |
| << ends; |
| strncpy(buf, oss.str().c_str(), DISPLAY_LEN*3-1); |
| buf[DISPLAY_LEN*3-1] = 0; |
| status_ = PHASE_2; |
| return 0; |
| break; |
| |
| case PHASE_2: // Data for individual heaps. |
| while (h_ <= HeapLogRoot::maxHeapNum) |
| { |
| seg = &header_[h_]; |
| |
| if (seg->usageCount_ == 0) |
| { |
| h_++; |
| continue; |
| } |
| |
| first = (s_ == 0); |
| while (s_ <= seg->last_) |
| { |
| if (seg->object_[s_].size > 0) |
| { |
| oss.str().clear(); |
| oss << (first ? heading_[4] : "") |
| << setw(14) << h_ |
| << setw(15) << seg->object_[s_].indx |
| << setw(21) << seg->object_[s_].size |
| << ends; |
| strncpy(buf, oss.str().c_str(), DISPLAY_LEN*2-1); |
| buf[DISPLAY_LEN*2-1] = 0; |
| if (s_ < seg->last_) |
| s_++; |
| else |
| { |
| s_ = 0; |
| h_++; |
| } |
| return 0; |
| } |
| s_++; |
| } |
| |
| s_ = 0; |
| h_++; |
| } |
| status_ = PHASE_3; |
| oss.str().clear(); |
| oss << heading_[4] |
| << ends; |
| strncpy(buf, oss.str().c_str(), DISPLAY_LEN-1); |
| buf[DISPLAY_LEN-1] = 0; |
| return 0; |
| |
| case PHASE_3: // Summary of Leaks. |
| oss.str().clear(); |
| oss << heading_[5] |
| << heading_[6] |
| << ends; |
| strncpy(buf, oss.str().c_str(), DISPLAY_LEN*2-1); |
| buf[DISPLAY_LEN*2-1] = 0; |
| status_ = PHASE_4; |
| h_ = 0; |
| return 0; |
| |
| case PHASE_4: |
| while(h_ <= HeapLogRoot::maxHeapNum) |
| { // Summary of each heap. |
| seg = &header_[h_]; |
| if (seg->usageCount_ == 0) |
| { |
| h_++; |
| continue; |
| } |
| oss.str().clear(); |
| oss << heading_[7] |
| << setw(14) << h_ |
| << setw(15) << seg->usageCount_ |
| << setw(21) << seg->totalSize_ |
| << " " |
| << seg->name_ |
| << ends; |
| strncpy(buf, oss.str().c_str(), DISPLAY_LEN*2-1); |
| buf[DISPLAY_LEN*2-1] = 0; |
| h_++; |
| return 0; |
| } |
| case PHASE_5: |
| { // Total |
| Lng32 heapCount = 0; |
| Lng32 totalSize = 0; |
| for (Lng32 i = 0; i <= HeapLogRoot::maxHeapNum; i++) |
| { |
| seg = &header_[i]; |
| if (seg->usageCount_ == 0) |
| continue; |
| heapCount++; |
| totalSize += seg->totalSize_; |
| } |
| oss.str().clear(); |
| oss << heading_[7] |
| << " Total: " |
| << setw(5) << heapCount |
| << setw(15) << objCount_ |
| << setw(21) << totalSize << "\n" |
| << (overflow_ ? heading_[8+sqlci] : "") |
| << (overflow_ ? heading_[10] : "") |
| << ends; |
| strncpy(buf, oss.str().c_str(), DISPLAY_LEN*4-1); |
| buf[DISPLAY_LEN*4-1] = 0; |
| status_ = PHASE_EOF; |
| return 0; |
| } |
| default: |
| close(); |
| return FETCH_EOF; |
| } |
| return 0; |
| } |
| |