blob: 4741b484690477613cffbd8ed84dbe9c925b4658 [file] [log] [blame]
/**********************************************************************
*
* File: CmpMemoryMonitor.cpp
* Description: The memory monitor records and logs memory usage for
* different phases in compiler/optimizer.
*
* Created: June 2007
* Language: C++
*
*
// @@@ 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 @@@
**********************************************************************/
#include "CmpMemoryMonitor.h"
#include "NADefaults.h"
#include "SchemaDB.h"
// Global pointer to the CmpMemoryMonitor that encapsulates of
// Memory Usage Information in SQL/MX compiler/optimizer.
THREAD_P CmpMemoryMonitor *cmpMemMonitor = NULL;
// Constructor for MemoryUsage
MemoryUsage::MemoryUsage(const char *phaseName, CollHeap *heap)
{
phaseName_ = new(heap) char[strlen(phaseName)+1];
strcpy(phaseName_, phaseName);
stmtHBegin_ = stmtHEnd_ = stmtHAllocSize_ = stmtHHighWaterMark_ = 0;
cxtHBegin_ = cxtHEnd_ = cxtHAllocSize_ = cxtHHighWaterMark_ = 0;
heap_ = heap;
}
// Another constructor for MemoryUsage
MemoryUsage::MemoryUsage(const char *phaseName,
size_t stmtHBegin, size_t stmtHEnd,
Int32 stmtHAllocSize, size_t stmtHHighWaterMark,
size_t cxtHBegin, size_t cxtHEnd,
Int32 cxtHAllocSize, size_t cxtHHighWaterMark,
CollHeap *heap)
{
phaseName_ = new(heap) char[strlen(phaseName)+1];
strcpy(phaseName_, phaseName);
stmtHBegin_ = stmtHBegin;
stmtHEnd_ = stmtHEnd;
stmtHAllocSize_ = stmtHAllocSize;
stmtHHighWaterMark_ = stmtHHighWaterMark;
cxtHBegin_ = cxtHBegin;
cxtHEnd_ = cxtHEnd;
cxtHAllocSize_ = cxtHAllocSize;
cxtHHighWaterMark_ = cxtHHighWaterMark;
heap_ = heap;
}
// Update the phase name with additional info.
// There are case (such the optimizer task count) that needs to updated
// to reflect the correct values that embedded in the phase name.
void MemoryUsage::updatePhaseName(char *updatedPhaseName)
{
if (updatedPhaseName)
{
NADELETEBASIC(phaseName_, heap_);
phaseName_ = new(heap_) char[strlen(updatedPhaseName)+1];
strcpy(phaseName_, updatedPhaseName);
}
}
// Record the memory usage information at the beginning of the phase.
void MemoryUsage::enter(CollHeap *otherHeap)
{
if (otherHeap)
{
// otherHeap is passed hesre in a special case for NATable when
// it uses its own heap instead of statement heap.
stmtHBegin_ = otherHeap->getAllocSize();
stmtHHighWaterMark_ = otherHeap->getHighWaterMark();
}
else
{
stmtHBegin_ = CmpCommon::statementHeap()->getAllocSize();
stmtHHighWaterMark_ = CmpCommon::statementHeap()->getHighWaterMark();
}
cxtHBegin_ = CmpCommon::contextHeap()->getAllocSize();
cxtHHighWaterMark_ = CmpCommon::contextHeap()->getHighWaterMark();
}
// Record the memory usage information at the end of the phase and
// compute the difference to get actual allocations.
void MemoryUsage::exit(CollHeap *otherHeap)
{
if (otherHeap)
{
// otherHeap is passed hesre in a special case for NATable when
// it uses its own heap instead of statement heap.
stmtHEnd_ = otherHeap->getAllocSize();
stmtHHighWaterMark_ = otherHeap->getHighWaterMark();
}
else
{
stmtHEnd_ = CmpCommon::statementHeap()->getAllocSize();
stmtHHighWaterMark_ = CmpCommon::statementHeap()->getHighWaterMark();
}
cxtHEnd_ = CmpCommon::contextHeap()->getAllocSize();
cxtHHighWaterMark_ = CmpCommon::contextHeap()->getHighWaterMark();
// Compute allocations during a phase.
stmtHAllocSize_ = (Int32)stmtHEnd_ - (Int32)stmtHBegin_;
cxtHAllocSize_ = (Int32)cxtHEnd_ - (Int32)cxtHBegin_;
}
// Log memory usage for a phase.
void MemoryUsage::logMemoryUsage(ofstream *logFilestream)
{
if (logFilestream)
{
if (strlen(phaseName_) > 23)
{
(*logFilestream) << phaseName_ << ": " << endl;
(*logFilestream).width(23); (*logFilestream) << "" << " ";
}
else
{
(*logFilestream).width(23); (*logFilestream) << phaseName_ << ": ";
}
(*logFilestream).width(11); (*logFilestream) << stmtHAllocSize_ << " ";
(*logFilestream).width(15); (*logFilestream) << stmtHHighWaterMark_ << " ";
(*logFilestream).width(11); (*logFilestream) << cxtHAllocSize_ << " ";
(*logFilestream).width(15); (*logFilestream) << cxtHHighWaterMark_ << endl;
}
else
{
// If output logfile is not specified, use std out.
cout.width(23); cout << phaseName_ << ": ";
cout.width(11); cout << stmtHAllocSize_ << " ";
cout.width(15); cout << stmtHHighWaterMark_ << " ";
cout.width(11); cout << cxtHAllocSize_ << " ";
cout.width(15); cout << cxtHHighWaterMark_ << endl;
}
}
// This operator is a must since we are using NAHashDictionary.
NABoolean MemoryUsage::operator==(const MemoryUsage& mu)
{
if (strcmp(phaseName_, mu.phaseName_) ||
stmtHBegin_ != mu.stmtHBegin_ ||
stmtHEnd_ != mu.stmtHEnd_ ||
stmtHAllocSize_ != mu.stmtHAllocSize_ ||
stmtHHighWaterMark_ != mu.stmtHHighWaterMark_ ||
cxtHBegin_ != mu.cxtHBegin_ ||
cxtHEnd_ != mu.cxtHEnd_ ||
cxtHAllocSize_ != mu.cxtHAllocSize_ ||
cxtHHighWaterMark_ != mu.cxtHHighWaterMark_)
return FALSE;
return TRUE;
}
// Constructor
CmpMemoryMonitor::CmpMemoryMonitor(CollHeap *heap)
{
isMemMonitor_ = FALSE;
isMemMonitorInDetail_ = FALSE;
isLogInstantly_ = FALSE;
logFilename_ = NULL;
logFilestream_ = NULL;
queryText_ = NULL;
hd_MemoryUsage_ = NULL;
memUsageList_ = NULL;
heap_ = heap;
}
// Hash fucntion used for storing memory usage information.
ULng32 cmmHashFunc_NAString(const NAString& str)
{
return (ULng32) NAString::hash(str);
}
void CmpMemoryMonitor::setIsMemMonitor(NABoolean isMemMonitor)
{
isMemMonitor_ = isMemMonitor;
if (isMemMonitor_ && !hd_MemoryUsage_)
// Create a new NAHashDictionary if it hasn't been already created.
hd_MemoryUsage_ = new(heap_) NAHashDictionary<NAString, MemoryUsage>
(&cmmHashFunc_NAString, 101, TRUE, heap_);
if (isMemMonitor_ && !memUsageList_)
// Create a new NAList if it hasn't been already created.
memUsageList_ = new(heap_) NAList<MemoryUsage *>(heap_);
}
NABoolean CmpMemoryMonitor::getIsMemMonitor()
{
return isMemMonitor_;
}
void CmpMemoryMonitor::setIsMemMonitorInDetail(NABoolean isMemMonitorInDetail)
{
if (isMemMonitorInDetail)
// if isMemMonitorInDetail is being set then set isMemMonitor_ as well.
isMemMonitor_ = isMemMonitorInDetail;
isMemMonitorInDetail_ = isMemMonitorInDetail;
if (isMemMonitor_ && !hd_MemoryUsage_)
// Create a new NAHashDictionary if it hasn't been already created.
hd_MemoryUsage_ = new(heap_) NAHashDictionary<NAString, MemoryUsage>
(&cmmHashFunc_NAString, 101, TRUE, heap_);
if (isMemMonitor_ && !memUsageList_)
// Create a new NAList if it hasn't been already created.
memUsageList_ = new(heap_) NAList<MemoryUsage *>(heap_);
}
NABoolean CmpMemoryMonitor::getIsMemMonitorInDetail()
{
return isMemMonitorInDetail_;
}
void CmpMemoryMonitor::setLogFilename(const char *logFilename)
{
if(logFilename)
{
// Clean up file name and stream since we are changing the
// output filename.
fileCleanUp();
setIsMemMonitor(TRUE);
logFilename_ = new(heap_) char[strlen(logFilename)+1];
strcpy(logFilename_, logFilename);
// Initialize the log file and write appropriate headers.
ofstream outLogFile(logFilename_);
outLogFile.width(19); outLogFile << "";
outLogFile << "Memory Usage Information for SQL/MX Compiler" << endl;
outLogFile.width(23); outLogFile << "" << " ";
outLogFile.width(11); outLogFile << "StmtH Alloc" << " ";
outLogFile.width(15); outLogFile << "StmtH HgWtrMark" << " ";
outLogFile.width(11); outLogFile << "CxtH Alloc" << " ";
outLogFile.width(15); outLogFile << "CxtH HgWtrMark" << endl;
outLogFile.close();
// Now open the file stream to append memery usage data as we get it.
logFilestream_ = new(heap_) ofstream(logFilename_, ios::app);
}
}
char* CmpMemoryMonitor::getLogFilename()
{
return logFilename_;
}
void CmpMemoryMonitor::setIsLogInstantly(NABoolean isLogInstantly)
{
isLogInstantly_ = isLogInstantly;
}
NABoolean CmpMemoryMonitor::getIsLogInstantly()
{
return isLogInstantly_;
}
// Capture Query Text of a query that is being monitored for
// memory usage.
void CmpMemoryMonitor::setQueryText(char *queryText)
{
queryText_ = new(heap_) char[strlen(queryText)+1];
strcpy(queryText_, queryText);
if (isLogInstantly_)
{
if (logFilestream_)
(*logFilestream_) << "Query: \n" << queryText_ << endl << endl;
else
cout << "Query: \n" << queryText_ << endl << endl;
}
}
char* CmpMemoryMonitor::getQueryText()
{
return queryText_;
}
// Cleanup file information when another filename is being used.
void CmpMemoryMonitor::fileCleanUp()
{
// Delete memory allocated for the filename.
if (logFilename_)
{
NADELETEBASIC(logFilename_, heap_);
logFilename_ = NULL;
}
// Delete memory allocated for the output file stream and call
// the destructor for ofstream.
if (logFilestream_)
{
logFilestream_->close();
NADELETE(logFilestream_, ofstream, heap_);
logFilestream_ = NULL;
}
}
// Cleanup after each statement
void CmpMemoryMonitor::cleanupPerStatement()
{
// Delete queryText_
if (queryText_)
{
NADELETEBASIC(queryText_, heap_);
queryText_ = NULL;
}
// Clear NAHashDictionary hd_MemoryUsage_.
if(hd_MemoryUsage_)
hd_MemoryUsage_->clear();
// Clear NAList muiList_.
if(memUsageList_)
memUsageList_->clear();
}
// Log memory usge for all phases at the end of monitoring
// memory usage.
void CmpMemoryMonitor::logMemoryUsageAll()
{
// If isLogInstantly_ is set, the information is already logged.
if (!isLogInstantly_)
{
if (queryText_)
{
if (logFilestream_)
(*logFilestream_) << "Query: \n" << queryText_ << endl << endl;
else
cout << "Query: \n" << queryText_ << endl;
}
for (CollIndex i=0; i<memUsageList_->entries(); i++)
(*memUsageList_)[i]->logMemoryUsage(logFilestream_);
}
}
// Record memory usage for a given phase at the start.
void CmpMemoryMonitor::enter(const char *phaseName,
CollHeap *otherHeap)
{
NAString *key_phaseName = new(heap_) NAString(phaseName, heap_);
MemoryUsage *val_memUsage = new(heap_) MemoryUsage(phaseName, heap_);
val_memUsage->enter(otherHeap);
// Insert MemoryUsage into list. This list will be used while
// writing to the file or std out to keep the order of the phases
// as they were encountered.
memUsageList_->insert(val_memUsage);
if (!hd_MemoryUsage_->contains(key_phaseName))
// Insert MemoryUsage into hash dictionary. This hash dictionary
// will be used to search for a memory usage for a given phase to
// update memory usage information. This will very helpful if
// memory is monitored more often (such as for each optimizer task
// or after every few optimizer tasks).
NAString *check = hd_MemoryUsage_->insert(key_phaseName, val_memUsage);
}
// Record memory usage for a given phase at the end.
void CmpMemoryMonitor::exit(const char *phaseName,
CollHeap *otherHeap,
char *updatedPhaseName)
{
NAString key_phaseName(phaseName, heap_);
MemoryUsage *val_memUsage = hd_MemoryUsage_->getFirstValue(&key_phaseName);
if (val_memUsage)
{
if(updatedPhaseName)
val_memUsage->updatePhaseName(updatedPhaseName);
val_memUsage->exit(otherHeap);
if (isLogInstantly_)
val_memUsage->logMemoryUsage(logFilestream_);
}
}
// Record query text.
void MonitorMemoryUsage_QueryText(char *queryText)
{
if (cmpMemMonitor->getIsMemMonitor())
cmpMemMonitor->setQueryText(queryText);
}
// Wrapper function to record memory usage at the beginning of the
// phase using CmpMemoryMonitor.
void MonitorMemoryUsage_Enter(const char *phaseName,
CollHeap *otherHeap,
NABoolean isDetail)
{
// Log memory usage information only when the query text is set.
// If the query text is not set, it may be due to one of the
// memory monitoring CQDs that set isMemMoniotor_ caused the
// recording of memory usage information. So we discard that info.
if (cmpMemMonitor->getQueryText())
{
if(isDetail)
{
// isDetail_ is set, so record memory usage info.
if (cmpMemMonitor->getIsMemMonitorInDetail())
cmpMemMonitor->enter(phaseName, otherHeap);
}
else
{
if (cmpMemMonitor->getIsMemMonitor())
cmpMemMonitor->enter(phaseName, otherHeap);
}
}
}
// Wrapper function to record memory usage at the end of the phase using
// CmpMemoryMonitor.
void MonitorMemoryUsage_Exit(const char *phaseName,
CollHeap *otherHeap,
char *updatedPhaseName,
NABoolean isDetail)
{
// Log memory usage information only when the query text is set.
// If the query text is not set, it may be due to one of the
// memory monitoring CQDs that set isMemMoniotor_ caused the
// recording of memory usage information. So we discard that info.
if (cmpMemMonitor->getQueryText())
{
if(isDetail)
{
// isDetail_ is set, so record memory usage info.
if (cmpMemMonitor->getIsMemMonitorInDetail())
cmpMemMonitor->exit(phaseName, otherHeap, updatedPhaseName);
}
else
{
if (cmpMemMonitor->getIsMemMonitor())
cmpMemMonitor->exit(phaseName, otherHeap, updatedPhaseName);
}
}
}
// Log all memory usage information.
void MonitorMemoryUsage_LogAll()
{
// Log memory usage information only when the query text is set.
// If the query text is not set, it may be due to one of the
// memory monitoring CQDs that set isMemMoniotor_ caused the
// recording of memory usage information. So we discard that info.
if (cmpMemMonitor->getQueryText())
{
if (cmpMemMonitor->getIsMemMonitor())
cmpMemMonitor->logMemoryUsageAll();
}
}