blob: e066de6fd5466f5f84e9c333e996655371635b4a [file] [log] [blame]
/**********************************************************************
// @@@ 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 @@@
**********************************************************************/
// Implementation for the classes listed in SystemParameters.h
#define pdctctlz_h_dct_get_by_name_ // so that we only get dct_get_by_name
#define pdctctlz_h_including_section // from pdctctlz.h
#define pdctctlz_h_
//
// $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
// $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
//
// !!! ATTENTION !!!
//
// $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
// $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
//
// For some reason, due (I believe) to strange circular dependencies, by
// adding the compiler directive <#include "NAStringDef.h"> and using
// NAString objects, the linker spits out some extremely bizarre
// (completely inexplicable) errors while building tdm_sqlcli.dll.
//
// So : the code below was rewritten to remove all usage of NAString
// objects.
//
//
// $$$ NB: ENSCRIBE ERROR CODES NOT CHECKED !!!
//
// Currently, the class NAClusterInfo calls a private method
// ::filloutDisks(), which calls a few subroutines from Enscribe.
// However, these calls ignore the return values from Enscribe, and this
// is clearly incorrect behavior. Someday (soon ...?) we need to change
// the signature of the NAClusterInfo ctors so that the user of this class
// can say whether we should handle (i.e., longjmp on) fatal errors
// returned from Enscribe. Given this parameter to the method
// ::filloutDisks(), we then will know whether we need to check these
// return error codes or not.
#include "NAClusterInfo.h"
#include "NADefaults.h"
#include "CompException.h"
#include <cextdecs/cextdecs.h>
#include <limits.h>
//str
#include <string.h>
//#include "NAStringDef.h"
//str
#include <stdio.h>
#include "guardian/pdctctlz.h"
#include "nsk/nskport.h"
#include "NodeMap.h"
#include "NATable.h"
#include "SchemaDB.h"
#include "ComRtUtils.h"
#include "OptimizerSimulator.h"
// Global pointer to OptimizerSimulator
#include "CmpErrors.h"
#include "seabed/ms.h"
#include <cstdlib>
#include <sys/stat.h>
THREAD_P NABoolean gIsStaticCompiler = FALSE;
void SetStaticCompiler(NABoolean isStaticCompiler)
{
gIsStaticCompiler = isStaticCompiler;
}
NABoolean IsStaticCompiler()
{
return gIsStaticCompiler;
}
//------------------------------------------------------------------------
// Global pointer to cluster information is initially null and remains so
// until actually required.
//------------------------------------------------------------------------
THREAD_P NAClusterInfo* gpClusterInfo = NULL;
//<pb>
//==============================================================================
// Set up global pointer to cluster information if this hasn't been done
// previously.
//
// Input:
// none
//
// Output:
// none
//
// Return:
// none
//
//==============================================================================
void setUpClusterInfo(CollHeap* heap)
{
#ifndef NDEBUG
if (getenv("NO_SERVICES")) // KSKSKS
return; // KSKSKS
#endif
//-------------------------------------------------------
// Set up cluster information based on hardware platform.
//-------------------------------------------------------
if (OSIM_runningSimulation() && !OSIM_ClusterInfoInitialized())
{
switch (CURRCONTEXT_OPTSIMULATOR->getCaptureSysType())
{
case OptimizerSimulator::OSIM_LINUX:
if(gpClusterInfo) NADELETEBASIC(gpClusterInfo, heap);
gpClusterInfo = new (heap) NAClusterInfoLinux (heap);
break;
default:
CMPASSERT(0); // Case not handled
break;
}
}
else if(OSIM_runningInCaptureMode() && !OSIM_ClusterInfoInitialized())
{
if(gpClusterInfo) NADELETEBASIC(gpClusterInfo, heap);
gpClusterInfo = new (heap) NAClusterInfoLinux (heap);
}
else
{
//-------------------------------------------
// Return now if cluster info already set up.
//-------------------------------------------
if (!gpClusterInfo)
gpClusterInfo = new (heap) NAClusterInfoLinux (heap);
}
}
//<pb>
static ULng32 intHashFunc(const Int32& Int)
{
return (ULng32)Int;
}
//============================================================================
// Methods for class NAClusterInfo; it provides information about the cluster
// in which we are running.
//============================================================================
//============================================================================
// NAClusterInfo constructor.
//
// Input: heap pointer(should always be context heap
//
//
// Output: Retrieves information for the local cluster. This includes information
// regarding its CPUs as well as for the dp2s. All these information will be cached
// in the appropriate structure.
//
// Return:
// none
//
//==============================================================================
NAClusterInfo::NAClusterInfo(CollHeap * heap)
: heap_(heap),
cpuArray_(heap),
inTestMode_(FALSE)
{
OptimizerSimulator::osimMode mode = OptimizerSimulator::OFF;
if(CURRCONTEXT_OPTSIMULATOR &&
!CURRCONTEXT_OPTSIMULATOR->isCallDisabled(9))
mode = CURRCONTEXT_OPTSIMULATOR->getOsimMode();
// Check for OSIM mode
switch (mode)
{
case OptimizerSimulator::OFF:
case OptimizerSimulator::LOAD:
case OptimizerSimulator::CAPTURE:
{
Int32 dummyClusterNum;
// Hash Map to store NodeName and NoideIds
nodeNameToNodeIdMap_ = new (heap) NAHashDictionary<NAString, Int32>
(NAString::hash, 101, TRUE, heap_);
nodeIdToNodeNameMap_ = new(heap) NAHashDictionary<Int32, NAString>
(&intHashFunc, 101,TRUE,heap);
NADefaults::getNodeAndClusterNumbers(localSMP_ , dummyClusterNum);
Int32 nodeCount = 0;
Int32 nodeMax = 0;
MS_Mon_Node_Info_Entry_Type *nodeInfo = NULL;
// Get the number of nodes to know how much info space to allocate
Int32 error = msg_mon_get_node_info(&nodeCount, 0, NULL);
CMPASSERT(error == 0);
CMPASSERT(nodeCount > 0);
// Allocate the space for node info entries
nodeInfo = (MS_Mon_Node_Info_Entry_Type *) new(heap)
char[nodeCount * sizeof(MS_Mon_Node_Info_Entry_Type)];
CMPASSERT(nodeInfo);
// Get the node info
memset(nodeInfo, 0, sizeof(nodeInfo));
nodeMax = nodeCount;
error = msg_mon_get_node_info(&nodeCount, nodeMax, nodeInfo);
CMPASSERT(error == 0);
for (Int32 i = 0; i < nodeCount; i++)
{
if (nodeInfo[i].spare_node)
continue;
// The zone type must either be an aggregation node or storage node
// to be included in the list of CPUs.
if ((nodeInfo[i].type & MS_Mon_ZoneType_Aggregation) != 0 ||
((nodeInfo[i].type & MS_Mon_ZoneType_Storage) != 0 ))
{
cpuArray_.insertAt(cpuArray_.entries(),
nodeInfo[i].nid);
// store nodeName-nodeId pairs
NAString *key_nodeName = new (heap_) NAString(nodeInfo[i].node_name, heap_);
size_t pos = key_nodeName->index('.');
if (pos && pos != NA_NPOS)
key_nodeName->remove(pos);
#ifdef _DEBUG
else {
// The node names for virtual nodes seen with workstations are of
// format <nodeName>:0, <nodeName>:1 etc. In debug mode, we work with
// such node names by removing all substrings starting at ':' and
// insert the node name into the nodeIdToNodeNameMap_.
pos = key_nodeName->index(':');
if (pos && pos != NA_NPOS)
key_nodeName->remove(pos);
}
#endif
Int32 *val_nodeId = new Int32(nodeInfo[i].nid);
nodeNameToNodeIdMap_->insert(key_nodeName, val_nodeId);
// store nodeId->nadeName
//share the same memory with nodeNameToNodeIdMap_
nodeIdToNodeNameMap_->insert(val_nodeId, key_nodeName);
}
}
NADELETEBASIC(nodeInfo, heap);
break;
}
case OptimizerSimulator::SIMULATE:
nodeIdToNodeNameMap_ = NULL;
cpuArray_.clear();
//load NAClusterInfo from OSIM file
simulateNAClusterInfo();
break;
default:
// The OSIM must run under OFF (normal), CAPTURE or SIMULATE mode.
OSIM_errorMessage("Invalid OSIM mode - It must be OFF or CAPTURE or SIMULATE.");
break;
}
} // NAClusterInfo::NAClusterInfo()
NAClusterInfo::~NAClusterInfo()
{
if (nodeNameToNodeIdMap_)
{
nodeNameToNodeIdMap_->clear();
delete nodeNameToNodeIdMap_;
}
if(nodeIdToNodeNameMap_)
{
nodeIdToNodeNameMap_->clear();
delete nodeIdToNodeNameMap_;
}
}
Lng32
NAClusterInfo::mapNodeNameToNodeNum(const NAString &keyNodeName) const
{
if ( nodeNameToNodeIdMap_->contains(&keyNodeName) )
{
Int32 *nodeValue = nodeNameToNodeIdMap_->getFirstValue(&keyNodeName);
return *nodeValue;
}
else return ANY_NODE;
} // NodeMap::getNodeNmber
NABoolean NAClusterInfo::NODE_ID_TO_NAME(Int32 nodeId, char *nodeName, short maxLen, short *actualLen)
{
//Currently, this method behaves as same as NODENUMBER_TO_NODENAME_(),
//which always returns "\\NSK", the only reason for doing this is to
//avoid diff in regression test and core file dumped when exiting sqlci.(don't know why.)
NODENUMBER_TO_NODENAME_(nodeId, nodeName, maxLen, actualLen);
return TRUE;
//Following code may be used in future to provide real node id to name map.
*actualLen = 0;
if (nodeIdToNodeNameMap_->contains(&nodeId))
{
NAString * value = nodeIdToNodeNameMap_->getFirstValue(&nodeId);
*actualLen = value->length();
strncpy(nodeName, value->data(), maxLen < (*actualLen) ? maxLen : (*actualLen));
return TRUE;
}
return FALSE;
}
Int32
NAClusterInfo::numOfPhysicalSMPs()
{
return cpuArray_.entries();
}
Int32
NAClusterInfo::numOfSMPs()
{
Int32 result = cpuArray_.entries();
// This is temporary patch for PARALLEL_NUM_ESPS issue. This CQD should
// be used in many places for costing, NodeMap allocation, synthesizing
// physProperties and so on. But currently it is used only in
// RelRoot::createContextForAChild() creating lots of discrepansies in
// the code. Sept. 2006
// Get the value as a token code, no errmsg if not a keyword.
if ( (CmpCommon::getDefault(COMP_BOOL_136) == DF_ON) AND
(CmpCommon::getDefault(PARALLEL_NUM_ESPS, 0) != DF_SYSTEM)
)
{
// -------------------------------------------------------------------
// A value for PARALLEL_NUM_ESPS exists. Use it for the count of cpus
// but don't exceed the number of cpus available in the cluster.
// -------------------------------------------------------------------
result = MINOF(result,
(Int32)(ActiveSchemaDB()->getDefaults().getAsLong(PARALLEL_NUM_ESPS)));
}
return result;
} // NAClusterInfo::numOfSMPs()
// Returns total number of CPUs (including down CPUs)
Lng32 NAClusterInfo::getTotalNumberOfCPUs()
{
Lng32 cpuCount = cpuArray_.entries();
#ifndef NDEBUG
if ( inTestMode() ) {
NADefaults & defs = ActiveSchemaDB()->getDefaults();
cpuCount = (Int32)(defs.getAsLong(POS_TEST_NUM_NODES));
}
#endif
//
return cpuCount;
}
void NAClusterInfo::cleanupPerStatement()
{
}
void NAClusterInfo::initializeForOSIMCapture()
{
}
NAClusterInfoLinux::NAClusterInfoLinux(CollHeap * heap) : NAClusterInfo(heap),
nid_(0), pid_(0)
{
OptimizerSimulator::osimMode mode = OptimizerSimulator::OFF;
if(CURRCONTEXT_OPTSIMULATOR &&
!CURRCONTEXT_OPTSIMULATOR->isCallDisabled(9))
mode = CURRCONTEXT_OPTSIMULATOR->getOsimMode();
// Check for OSIM mode
switch (mode)
{
case OptimizerSimulator::OFF:
case OptimizerSimulator::CAPTURE:
case OptimizerSimulator::LOAD:
determineLinuxSysInfo();
// For CAPTURE mode, the data will be captured later in CmpMain::compile()
break;
case OptimizerSimulator::SIMULATE:
// Simulate the NAClusterInfo.
simulateNAClusterInfoLinux();
break;
default:
// The OSIM must run under OFF (normal), CAPTURE or SIMULATE mode.
OSIM_errorMessage("Invalid OSIM mode - It must be OFF or CAPTURE or SIMULATE.");
break;
}
}
NAClusterInfoLinux::~NAClusterInfoLinux()
{
}
Int32 NAClusterInfoLinux::processorFrequency() const
{
return frequency_;
}
float NAClusterInfoLinux::ioTransferRate() const
{
return iorate_;
}
float NAClusterInfoLinux::seekTime() const
{
return seekTime_;
}
Int32 NAClusterInfoLinux::cpuArchitecture() const
{
return CPU_ARCH_UNKNOWN;
}
size_t NAClusterInfoLinux::numberOfCpusPerSMP() const
{
return numCPUcoresPerNode_;
}
size_t NAClusterInfoLinux::pageSize() const
{
return pageSize_;
}
// Return the physical memory available in kilobytes
size_t NAClusterInfoLinux::physicalMemoryAvailable() const
{
// NSK returns the total memory available so we do the same thing
// on Linux. This allows the plans to stay constant even as
// the amount of memory fluctuates.
return totalMemoryAvailable_;
}
size_t NAClusterInfoLinux::totalMemoryAvailable() const
{
return totalMemoryAvailable_;
}
size_t NAClusterInfoLinux::virtualMemoryAvailable()
{
// Just return a constant (like NSK does).
return 256000000/1024;
}
#define LINUX_DEFAULT_FREQ 3000
#define LINUX_IO_RATE 75.0
#define LINUX_SEEK_RATE 0.0038
void NAClusterInfoLinux::determineLinuxSysInfo()
{
// Set the page size in killobytes and determine how much memory
// is available on this node (in kilobytes).
pageSize_ = (size_t)sysconf(_SC_PAGESIZE) / 1024U;
totalMemoryAvailable_ = pageSize_ * (size_t)sysconf(_SC_PHYS_PAGES);
numCPUcoresPerNode_ = sysconf(_SC_NPROCESSORS_ONLN);
frequency_ = 0.0;
// Read the CPU frequency from the sysfs filesystem.
ifstream infoFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
if (infoFile.fail()) {
// This code should log a warning.
// use /proc/cpuinfo
char var[256];
ifstream cpuInfoFile("/proc/cpuinfo");
const char* freqToken = "cpu MHz";
Lng32 freqTokenLen = strlen(freqToken);
while(cpuInfoFile.good())
{
// Read the variable name from the file.
cpuInfoFile.getline(var, sizeof(var), ':'); // read the token part
Lng32 len = strlen(var);
if(len >= freqTokenLen && !strncmp(var, freqToken, freqTokenLen))
{
cpuInfoFile >> frequency_;
break;
}
cpuInfoFile.getline(var, sizeof(var)); // read the value part
}
if ( frequency_ == 0.0 )
// Use the default frequency
frequency_ = LINUX_DEFAULT_FREQ;
} else {
ULng32 freqUlongVal;
infoFile >> freqUlongVal;
frequency_ = freqUlongVal / 1000;
infoFile.close();
}
// These should be determined programmatically, but are hard-coded for now.
iorate_ = LINUX_IO_RATE;
seekTime_ = LINUX_SEEK_RATE;
}
//============================================================================
// This method writes the information related to the NAClusterInfoLinux class
// to a logfile called "NAClusterInfo.txt".
//============================================================================
void NAClusterInfoLinux::captureOSInfo(ofstream & nacllinuxfile) const
{
nacllinuxfile << "frequency_: " << frequency_ << endl
<< "iorate_: " << iorate_ << endl
<< "seekTime_: "<< seekTime_ << endl
<< "pageSize_: " << pageSize_ << endl
<< "totalMemoryAvailable_: " << totalMemoryAvailable_ << endl
<< "numCPUcoresPerNode_: " << numCPUcoresPerNode_ << endl;
}