Files
mars-flaim/sql/src/flmstat.cpp
ahodgkinson a4912ad9f6 Changed license to LGPL.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@1012 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2007-01-23 11:59:09 +00:00

1454 lines
35 KiB
C++

//------------------------------------------------------------------------------
// Desc: This file contains routines for updating FLAIM statistics.
// Tabs: 3
//
// Copyright (c) 1997-2007 Novell, Inc. All Rights Reserved.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version 2.1
// of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, contact Novell, Inc.
//
// To contact Novell about this file by physical or electronic mail,
// you may find current contact information at www.novell.com.
//
// $Id$
//------------------------------------------------------------------------------
#include "flaimsys.h"
FSTATIC RCODE flmStatGetDbByName(
SFLM_STATS * pFlmStats,
const char * pszDbName,
FLMUINT uiLowStart,
SFLM_DB_STATS ** ppDbStatsRV,
FLMUINT * puiDBAllocSeqRV,
FLMUINT * puiDbTblPosRV);
FSTATIC void flmUpdateLFileStats(
SFLM_LFILE_STATS * pDest,
SFLM_LFILE_STATS * pSrc);
FSTATIC RCODE flmUpdateDbStats(
SFLM_DB_STATS * pDest,
SFLM_DB_STATS * pSrc);
FSTATIC FLMUINT flmDaysInMonth(
FLMUINT uiYear,
FLMUINT uiMonth);
FSTATIC void flmAdjustTime(
F_TMSTAMP * pTime,
FLMINT iStartPoint);
/****************************************************************************
Desc: This routine returns a pointer to a particular database's
statistics block.
****************************************************************************/
FSTATIC RCODE flmStatGetDbByName(
SFLM_STATS * pFlmStats,
const char * pszDbName,
FLMUINT uiLowStart,
SFLM_DB_STATS ** ppDbStatsRV,
FLMUINT * puiDBAllocSeqRV,
FLMUINT * puiDbTblPosRV)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiTblSize;
SFLM_DB_STATS * pDbStatTbl;
FLMUINT uiLow;
FLMUINT uiMid = 0;
FLMUINT uiHigh;
FLMINT iCmp = 0;
FLMUINT uiNewSize;
FLMUINT uiElement;
char * pszTmpDbName = NULL;
// If there is a database array, search it first.
if (((pDbStatTbl = pFlmStats->pDbStats) != NULL) &&
((uiTblSize = pFlmStats->uiNumDbStats) != 0))
{
for (uiHigh = --uiTblSize, uiLow = uiLowStart ; ; ) // Optimize: reduce uiTblSize
{
uiMid = (uiLow + uiHigh) >> 1; // (uiLow + uiHigh) / 2
// See if we found a match.
#ifdef FLM_UNIX
if ((iCmp = f_strcmp( pszDbName,
pDbStatTbl [uiMid].pszDbName)) == 0)
#else
if ((iCmp = f_stricmp( pszDbName,
pDbStatTbl [uiMid].pszDbName)) == 0)
#endif
{
// Found match.
*ppDbStatsRV = &pDbStatTbl [uiMid];
if (puiDBAllocSeqRV)
{
*puiDBAllocSeqRV = pFlmStats->uiDBAllocSeq;
}
if (puiDbTblPosRV)
{
*puiDbTblPosRV = uiMid;
}
goto Exit;
}
// Check if we are done - where uiLow equals uiHigh.
if (uiLow >= uiHigh)
{
// Item not found.
break;
}
if (iCmp < 0)
{
if (uiMid == uiLowStart)
{
break; // Way too high?
}
uiHigh = uiMid - 1; // Too high
}
else
{
if (uiMid == uiTblSize)
{
break; // Done - Hit the top
}
uiLow = uiMid + 1; // Too low
}
}
}
// If the array is full, or was never allocated, allocate a new one.
if (pFlmStats->uiDbStatArraySize <= pFlmStats->uiNumDbStats)
{
if (!pFlmStats->pDbStats)
{
uiNewSize = INIT_DB_STAT_ARRAY_SIZE;
}
else
{
uiNewSize = pFlmStats->uiDbStatArraySize +
DB_STAT_ARRAY_INCR_SIZE;
}
if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( SFLM_DB_STATS) * uiNewSize),
&pDbStatTbl)))
{
goto Exit;
}
// Save whatever was in the old table, if any.
if (pFlmStats->pDbStats && pFlmStats->uiNumDbStats)
{
f_memcpy( pDbStatTbl, pFlmStats->pDbStats,
(FLMINT)(sizeof( SFLM_DB_STATS) * pFlmStats->uiNumDbStats));
}
if (pFlmStats->pDbStats)
{
f_free( &pFlmStats->pDbStats);
}
pFlmStats->uiDBAllocSeq++;
pFlmStats->pDbStats = pDbStatTbl;
pFlmStats->uiDbStatArraySize = uiNewSize;
}
// Allocate space for the database name
if (RC_BAD( rc = f_alloc( f_strlen( pszDbName) + 1, &pszTmpDbName)))
{
goto Exit;
}
// Insert the item into the array.
if (iCmp != 0)
{
uiElement = pFlmStats->uiNumDbStats;
// If our new database number is greater than database number of the
// element pointed to by uiMid, increment uiMid so that the new
// database number will be inserted after it instead of before it.
if (iCmp > 0)
{
uiMid++;
}
// Move everything up in the array, including the slot pointed to
// by uiMid.
while (uiElement > uiMid)
{
f_memcpy( &pDbStatTbl [uiElement], &pDbStatTbl [uiElement - 1],
sizeof( SFLM_DB_STATS));
uiElement--;
}
f_memset( &pDbStatTbl [uiMid], 0, sizeof( SFLM_DB_STATS));
}
f_strcpy( pszTmpDbName, pszDbName);
pDbStatTbl [uiMid].pszDbName = pszTmpDbName;
pszTmpDbName = NULL;
pFlmStats->uiNumDbStats++;
*ppDbStatsRV = &pDbStatTbl [uiMid];
if (puiDBAllocSeqRV)
{
*puiDBAllocSeqRV = pFlmStats->uiDBAllocSeq;
}
if (puiDbTblPosRV)
{
*puiDbTblPosRV = uiMid;
}
Exit:
if (pszTmpDbName)
{
f_free( &pszTmpDbName);
}
return( rc);
}
/****************************************************************************
Desc: This routine returns a pointer to a particular database's
statistics block.
****************************************************************************/
RCODE flmStatGetDb(
SFLM_STATS * pFlmStats,
F_Database * pDatabase,
FLMUINT uiLowStart,
SFLM_DB_STATS ** ppDbStatsRV,
FLMUINT * puiDBAllocSeqRV,
FLMUINT * puiDbTblPosRV)
{
if (!pFlmStats)
{
*ppDbStatsRV = NULL;
if (puiDBAllocSeqRV)
{
*puiDBAllocSeqRV = 0;
}
if (puiDbTblPosRV)
{
*puiDbTblPosRV = 0;
}
return( NE_SFLM_OK);
}
return( flmStatGetDbByName( pFlmStats, pDatabase->getDbNamePtr(),
uiLowStart, ppDbStatsRV, puiDBAllocSeqRV,
puiDbTblPosRV));
}
/****************************************************************************
Desc: This routine returns a pointer to a particular logical file in a
particular database's statistics block.
****************************************************************************/
RCODE flmStatGetLFile(
SFLM_DB_STATS * pDbStats,
FLMUINT uiLFileNum,
eLFileType eLfType,
FLMUINT uiLowStart,
SFLM_LFILE_STATS ** ppLFileStatsRV,
FLMUINT * puiLFileAllocSeqRV,
FLMUINT * puiLFileTblPosRV)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiTblSize;
SFLM_LFILE_STATS * pLFileStatTbl;
SFLM_LFILE_STATS * pLFileCurrStat;
FLMUINT uiLow;
FLMUINT uiMid = 0;
FLMUINT uiHigh;
FLMINT iCmp = 0;
FLMUINT uiNewSize;
FLMUINT uiElement;
if (!pDbStats)
{
*ppLFileStatsRV = NULL;
if (puiLFileAllocSeqRV)
{
*puiLFileAllocSeqRV = 0;
}
if (puiLFileTblPosRV)
{
*puiLFileTblPosRV = 0;
}
goto Exit;
}
// If there is a database array, search it first.
if (((pLFileStatTbl = pDbStats->pLFileStats) != NULL) &&
((uiTblSize = pDbStats->uiNumLFileStats) != 0))
{
for (uiHigh = --uiTblSize, uiLow = uiLowStart ; ; ) // Optimize: reduce uiTblSize
{
uiMid = (uiLow + uiHigh) >> 1; // (uiLow + uiHigh) / 2
// See if we found a match.
pLFileCurrStat = &pLFileStatTbl [uiMid];
if (uiLFileNum < pLFileCurrStat->uiLFileNum)
{
iCmp = -1;
}
else if (uiLFileNum > pLFileCurrStat->uiLFileNum)
{
iCmp = 1;
}
else if (eLfType < pLFileCurrStat->eLfType)
{
iCmp = -1;
}
else if (eLfType > pLFileCurrStat->eLfType)
{
iCmp = 1;
}
else
{
// Found match.
*ppLFileStatsRV = pLFileCurrStat;
if (puiLFileAllocSeqRV)
{
*puiLFileAllocSeqRV = pDbStats->uiLFileAllocSeq;
}
if (puiLFileTblPosRV)
{
*puiLFileTblPosRV = uiMid;
}
goto Exit;
}
// Check if we are done - where uiLow equals uiHigh.
if (uiLow >= uiHigh)
{
// Item not found.
break;
}
if (iCmp < 0)
{
if (uiMid == uiLowStart)
{
break; // Way too high?
}
uiHigh = uiMid - 1; // Too high
}
else
{
if (uiMid == uiTblSize)
{
break; // Done - Hit the top
}
uiLow = uiMid + 1; // Too low
}
}
}
// If the array is full, or was never allocated, allocate a new one.
if (pDbStats->uiLFileStatArraySize <= pDbStats->uiNumLFileStats)
{
if (!pDbStats->pLFileStats)
{
uiNewSize = INIT_LFILE_STAT_ARRAY_SIZE;
}
else
{
uiNewSize = pDbStats->uiLFileStatArraySize +
LFILE_STAT_ARRAY_INCR_SIZE;
}
if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( SFLM_LFILE_STATS) * uiNewSize),
&pLFileStatTbl)))
{
goto Exit;
}
// Save whatever was in the old table, if any.
if ((pDbStats->pLFileStats) &&
(pDbStats->uiNumLFileStats))
{
f_memcpy( pLFileStatTbl, pDbStats->pLFileStats,
(FLMUINT)(sizeof( SFLM_LFILE_STATS) *
pDbStats->uiNumLFileStats));
}
if (pDbStats->pLFileStats)
{
f_free( &pDbStats->pLFileStats);
}
pDbStats->uiLFileAllocSeq++;
pDbStats->pLFileStats = pLFileStatTbl;
pDbStats->uiLFileStatArraySize = uiNewSize;
}
// Insert the item into the array.
if (iCmp != 0)
{
uiElement = pDbStats->uiNumLFileStats;
// If our new database number is greater than database number of the
// element pointed to by uiMid, increment uiMid so that the new
// database number will be inserted after it instead of before it.
if (iCmp > 0)
{
uiMid++;
}
// Move everything up in the array, including the slot pointed to
// by uiMid.
while (uiElement > uiMid)
{
f_memcpy( &pLFileStatTbl [uiElement], &pLFileStatTbl [uiElement - 1],
sizeof( SFLM_LFILE_STATS));
uiElement--;
}
f_memset( &pLFileStatTbl [uiMid], 0, sizeof( SFLM_LFILE_STATS));
}
pLFileStatTbl [uiMid].uiLFileNum = uiLFileNum;
pLFileStatTbl [uiMid].eLfType = eLfType;
pDbStats->uiNumLFileStats++;
*ppLFileStatsRV = &pLFileStatTbl [uiMid];
if (puiLFileAllocSeqRV)
{
*puiLFileAllocSeqRV = pDbStats->uiLFileAllocSeq;
}
if (puiLFileTblPosRV)
{
*puiLFileTblPosRV = uiMid;
}
Exit:
return( rc);
}
/****************************************************************************
Desc: This routine returns a pointer to a particular LFILE's
statistics block.
****************************************************************************/
SFLM_LFILE_STATS * F_Db::getLFileStatPtr(
LFILE * pLFile)
{
if (!pLFile)
{
return( (SFLM_LFILE_STATS *)NULL);
}
if ((!m_pLFileStats) ||
(m_uiLFileAllocSeq !=
m_pDbStats->uiLFileAllocSeq) ||
(m_pLFileStats->uiLFileNum != pLFile->uiLfNum))
{
if (RC_BAD( flmStatGetLFile( m_pDbStats, pLFile->uiLfNum,
pLFile->eLfType, 0, &m_pLFileStats,
&m_uiLFileAllocSeq, NULL)))
{
m_pLFileStats = NULL;
m_uiLFileAllocSeq = 0;
}
}
return( m_pLFileStats);
}
/****************************************************************************
Desc: This routine resets the statistics in a FLM_STAT structure.
****************************************************************************/
void flmStatReset(
SFLM_STATS * pStats,
FLMBOOL bFree)
{
FLMUINT uiDb;
SFLM_DB_STATS * pDbStats;
FLMUINT uiLFile;
SFLM_LFILE_STATS * pLFile;
if ((pDbStats = pStats->pDbStats) != NULL)
{
for (uiDb = 0; uiDb < pStats->uiNumDbStats; uiDb++, pDbStats++)
{
if ((pLFile = pDbStats->pLFileStats) != NULL)
{
if (bFree)
{
f_free( &pDbStats->pLFileStats);
}
else
{
for (uiLFile = 0;
uiLFile < pDbStats->uiNumLFileStats;
uiLFile++, pLFile++)
{
FLMUINT uiSaveLFileNum = pLFile->uiLFileNum;
eLFileType eSaveLfType = pLFile->eLfType;
f_memset( pLFile, 0, sizeof( SFLM_LFILE_STATS));
pLFile->uiLFileNum = uiSaveLFileNum;
pLFile->eLfType = eSaveLfType;
}
}
}
if (!bFree)
{
const char * pszSaveDbName;
FLMUINT uiSaveLFileAllocSeq = pDbStats->uiLFileAllocSeq;
SFLM_LFILE_STATS * pSaveLFileStats = pDbStats->pLFileStats;
FLMUINT uiSaveLFileStatArraySize =
pDbStats->uiLFileStatArraySize;
FLMUINT uiSaveNumLFileStats = pDbStats->uiNumLFileStats;
pszSaveDbName = pDbStats->pszDbName;
f_memset( pDbStats, 0, sizeof( SFLM_DB_STATS));
pDbStats->pszDbName = pszSaveDbName;
pDbStats->uiLFileAllocSeq = uiSaveLFileAllocSeq;
pDbStats->pLFileStats = pSaveLFileStats;
pDbStats->uiLFileStatArraySize = uiSaveLFileStatArraySize;
pDbStats->uiNumLFileStats = uiSaveNumLFileStats;
}
else
{
f_free( &pDbStats->pszDbName);
}
}
if (bFree)
{
f_free( &pStats->pDbStats);
}
}
if ((bFree) || (!pDbStats))
{
pStats->pDbStats = NULL;
pStats->uiDbStatArraySize = 0;
pStats->uiNumDbStats = 0;
}
pStats->uiStartTime = 0;
pStats->uiStopTime = 0;
if (pStats->bCollectingStats)
{
f_timeGetSeconds( &pStats->uiStartTime);
}
}
/****************************************************************************
Desc: This routine updates statistics from one SFLM_RTRANS_STATS structure
into another.
****************************************************************************/
FINLINE void flmUpdateRTransStats(
SFLM_RTRANS_STATS * pDest,
SFLM_RTRANS_STATS * pSrc)
{
flmUpdateCountTimeStats( &pDest->CommittedTrans, &pSrc->CommittedTrans);
flmUpdateCountTimeStats( &pDest->AbortedTrans, &pSrc->AbortedTrans);
}
/****************************************************************************
Desc: This routine updates statistics from one SFLM_UTRANS_STATS structure
into another.
****************************************************************************/
FINLINE void flmUpdateUTransStats(
SFLM_UTRANS_STATS * pDest,
SFLM_UTRANS_STATS * pSrc)
{
flmUpdateCountTimeStats( &pDest->CommittedTrans, &pSrc->CommittedTrans);
flmUpdateCountTimeStats( &pDest->GroupCompletes, &pSrc->GroupCompletes);
pDest->ui64GroupFinished += pSrc->ui64GroupFinished;
flmUpdateCountTimeStats( &pDest->AbortedTrans, &pSrc->AbortedTrans);
}
/****************************************************************************
Desc: This routine updates statistics from one SFLM_BLOCKIO_STATS structure
into another.
****************************************************************************/
void flmUpdateBlockIOStats(
SFLM_BLOCKIO_STATS * pDest,
SFLM_BLOCKIO_STATS * pSrc)
{
flmUpdateDiskIOStats( &pDest->BlockReads, &pSrc->BlockReads);
flmUpdateDiskIOStats( &pDest->OldViewBlockReads,
&pSrc->OldViewBlockReads);
pDest->uiBlockChkErrs += pSrc->uiBlockChkErrs;
pDest->uiOldViewBlockChkErrs += pSrc->uiOldViewBlockChkErrs;
pDest->uiOldViewErrors += pSrc->uiOldViewErrors;
flmUpdateDiskIOStats( &pDest->BlockWrites, &pSrc->BlockWrites);
}
/****************************************************************************
Desc: This routine updates statistics from one SFLM_LFILE_STATS structure
into another.
****************************************************************************/
FSTATIC void flmUpdateLFileStats(
SFLM_LFILE_STATS * pDest,
SFLM_LFILE_STATS * pSrc)
{
pDest->bHaveStats = TRUE;
flmUpdateBlockIOStats( &pDest->RootBlockStats, &pSrc->RootBlockStats);
flmUpdateBlockIOStats( &pDest->MiddleBlockStats, &pSrc->MiddleBlockStats);
flmUpdateBlockIOStats( &pDest->LeafBlockStats, &pSrc->LeafBlockStats);
pDest->ui64BlockSplits += pSrc->ui64BlockSplits;
pDest->ui64BlockCombines += pSrc->ui64BlockCombines;
}
/****************************************************************************
Desc: This routine updates statistics from one SFLM_DB_STATS structure into
another.
****************************************************************************/
FSTATIC RCODE flmUpdateDbStats(
SFLM_DB_STATS * pDestDb,
SFLM_DB_STATS * pSrcDb)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiSrcLFile;
FLMUINT uiDestLFile;
SFLM_LFILE_STATS * pDestLFile;
SFLM_LFILE_STATS * pSrcLFile;
FLMUINT uiLowLFileStart;
FLMUINT uiSaveNumLFiles;
flmUpdateRTransStats( &pDestDb->ReadTransStats,
&pSrcDb->ReadTransStats);
flmUpdateUTransStats( &pDestDb->UpdateTransStats,
&pSrcDb->UpdateTransStats);
pDestDb->bHaveStats = TRUE;
flmUpdateBlockIOStats( &pDestDb->LFHBlockStats,
&pSrcDb->LFHBlockStats);
flmUpdateBlockIOStats( &pDestDb->AvailBlockStats,
&pSrcDb->AvailBlockStats);
flmUpdateDiskIOStats( &pDestDb->DbHdrWrites,
&pSrcDb->DbHdrWrites);
flmUpdateDiskIOStats( &pDestDb->LogBlockWrites,
&pSrcDb->LogBlockWrites);
flmUpdateDiskIOStats( &pDestDb->LogBlockRestores,
&pSrcDb->LogBlockRestores);
flmUpdateDiskIOStats( &pDestDb->LogBlockReads,
&pSrcDb->LogBlockReads);
pDestDb->uiLogBlockChkErrs += pSrcDb->uiLogBlockChkErrs;
pDestDb->uiReadErrors += pSrcDb->uiReadErrors;
pDestDb->uiWriteErrors += pSrcDb->uiWriteErrors;
flmUpdateCountTimeStats( &pDestDb->LockStats.NoLocks,
&pSrcDb->LockStats.NoLocks);
flmUpdateCountTimeStats( &pDestDb->LockStats.WaitingForLock,
&pSrcDb->LockStats.WaitingForLock);
flmUpdateCountTimeStats( &pDestDb->LockStats.HeldLock,
&pSrcDb->LockStats.HeldLock);
// Go through the LFILE statistics.
for (uiDestLFile = 0, uiSrcLFile = 0, uiLowLFileStart = 0,
pSrcLFile = pSrcDb->pLFileStats;
uiSrcLFile < pSrcDb->uiNumLFileStats;
uiSrcLFile++, pSrcLFile++)
{
if (!pSrcLFile->bHaveStats)
{
continue;
}
// Find or add the store in the destination store array.
uiSaveNumLFiles = pDestDb->uiNumLFileStats;
if (RC_BAD( rc = flmStatGetLFile( pDestDb, pSrcLFile->uiLFileNum,
pSrcLFile->eLfType,
uiLowLFileStart, &pDestLFile, NULL,
&uiLowLFileStart)))
{
goto Exit;
}
if (uiLowLFileStart < pDestDb->uiNumLFileStats - 1)
{
uiLowLFileStart++;
}
// If we created the LFILE, all we have to do is copy the
// LFILE statistics. It will be quicker.
if (uiSaveNumLFiles != pDestDb->uiNumLFileStats)
{
f_memcpy( pDestLFile, pSrcLFile, sizeof( SFLM_LFILE_STATS));
}
else
{
// LFILE was already present, need to go through and
// update the statistics.
flmUpdateLFileStats( pDestLFile, pSrcLFile);
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: This routine updates statistics from one FLM_STAT structure into
the global statistics.
****************************************************************************/
RCODE flmStatUpdate(
SFLM_STATS * pSrcStats)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiSrcDb;
FLMUINT uiDestDb;
SFLM_DB_STATS * pDestDb;
SFLM_DB_STATS * pSrcDb;
FLMUINT uiLowDbStart;
// Do not update the statistics if the source statistics were started
// at an earlier time that the destination statistics start time.
if (!gv_SFlmSysData.Stats.bCollectingStats ||
pSrcStats->uiStartTime < gv_SFlmSysData.Stats.uiStartTime)
{
return( NE_SFLM_OK);
}
f_mutexLock( gv_SFlmSysData.hStatsMutex);
// Go through each of the source's databases
for ( uiDestDb = 0, uiSrcDb = 0, uiLowDbStart = 0,
pSrcDb = pSrcStats->pDbStats;
uiSrcDb < pSrcStats->uiNumDbStats;
uiSrcDb++, pSrcDb++)
{
if (!pSrcDb->bHaveStats)
{
continue;
}
// Find or add the store in the destination store array.
if (RC_BAD( rc = flmStatGetDbByName( &gv_SFlmSysData.Stats,
pSrcDb->pszDbName,
uiLowDbStart, &pDestDb, NULL,
&uiLowDbStart)))
{
goto Exit;
}
if (uiLowDbStart < gv_SFlmSysData.Stats.uiNumDbStats - 1)
{
uiLowDbStart++;
}
if (RC_BAD( rc = flmUpdateDbStats( pDestDb, pSrcDb)))
{
goto Exit;
}
}
Exit:
f_mutexUnlock( gv_SFlmSysData.hStatsMutex);
if (RC_OK( rc))
{
flmStatReset( pSrcStats, FALSE);
}
return( rc);
}
/****************************************************************************
Desc: This routine frees however many queries that are over the limit of
the number we can save.
Note: This routine will ALWAYS unlock the query mutex on leaving. It may
be locked when entering.
****************************************************************************/
void flmFreeSavedQueries(
FLMBOOL bMutexAlreadyLocked)
{
QUERY_HDR * pQueriesToFree = NULL;
// Must determine the queries to free inside the mutex lock and then free
// them outside the mutex lock, because freeing a query may cause an
// embedded query to be put into the list again, which will cause the
// mutex to be locked again.
if (!bMutexAlreadyLocked)
{
f_mutexLock( gv_SFlmSysData.hQueryMutex);
}
while (gv_SFlmSysData.uiQueryCnt > gv_SFlmSysData.uiMaxQueries)
{
gv_SFlmSysData.pOldestQuery = gv_SFlmSysData.pOldestQuery->pPrev;
gv_SFlmSysData.uiQueryCnt--;
}
// Whatever is found after pOldestQuery should be freed. Unlink
// those from the list and point to them with pQueriesToFree.
if (!gv_SFlmSysData.pOldestQuery)
{
pQueriesToFree = gv_SFlmSysData.pNewestQuery;
gv_SFlmSysData.pNewestQuery = NULL;
}
else if (gv_SFlmSysData.pOldestQuery->pNext)
{
pQueriesToFree = gv_SFlmSysData.pOldestQuery->pNext;
pQueriesToFree->pPrev = NULL;
gv_SFlmSysData.pOldestQuery->pNext = NULL;
}
f_mutexUnlock( gv_SFlmSysData.hQueryMutex);
// Now clean up each of the queries in the pQueriesToFree list.
// This can be done outside the mutex lock because we are now
// dealing with a completely local list that no other thread can
// see. Also, the mutex must NOT be locked at this point because
// FlmCursorCleanup may free an embedded query, which will want
// to lock the mutex again.
while (pQueriesToFree)
{
QUERY_HDR * pQueryHdrToFree = pQueriesToFree;
pQueriesToFree = pQueriesToFree->pNext;
// VISIT
flmAssert( 0);
// pQueryHdrToFree->pCursor->cleanup();
f_free( &pQueryHdrToFree);
}
}
/****************************************************************************
Desc: This routine saves a query so it can be analyzed later.
****************************************************************************/
void flmSaveQuery(
F_Query * pQuery)
{
QUERY_HDR * pQueryHdr = NULL;
FLMBOOL bNeedToCleanup = TRUE;
FLMBOOL bMutexLocked = FALSE;
// Allocate memory for the new query
if (RC_BAD( f_calloc( sizeof( QUERY_HDR), &pQueryHdr)))
{
goto Exit;
}
pQueryHdr->pQuery = pQuery;
f_mutexLock( gv_SFlmSysData.hQueryMutex);
bMutexLocked = TRUE;
// uiMaxQueries was originally checked outside of the mutex lock.
// Make sure it is still non-zero.
if (gv_SFlmSysData.uiMaxQueries)
{
// Link query to head of list.
bNeedToCleanup = FALSE;
if ((pQueryHdr->pNext = gv_SFlmSysData.pNewestQuery) != NULL)
{
pQueryHdr->pNext->pPrev = pQueryHdr;
}
else
{
gv_SFlmSysData.pOldestQuery = pQueryHdr;
}
gv_SFlmSysData.pNewestQuery = pQueryHdr;
if (++gv_SFlmSysData.uiQueryCnt > gv_SFlmSysData.uiMaxQueries)
{
flmFreeSavedQueries( TRUE);
// flmFreeSavedQueries will always unlock the mutex.
bMutexLocked = FALSE;
}
}
Exit:
if (bMutexLocked)
{
f_mutexUnlock( gv_SFlmSysData.hQueryMutex);
}
// Must clean up the query if we didn't get it into the list for
// some reason.
if (bNeedToCleanup)
{
if (pQueryHdr)
{
f_free( &pQueryHdr);
}
// VISIT
flmAssert( 0);
// pCursor->cleanup();
}
}
/****************************************************************************
Desc: This routine copies statistics from one FLM_STAT structure into
another. This is used to retrieve statistics.
****************************************************************************/
RCODE flmStatCopy(
SFLM_STATS * pDestStats,
SFLM_STATS * pSrcStats)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiDb;
SFLM_DB_STATS * pDestDb;
SFLM_DB_STATS * pSrcDb;
FLMUINT uiCount;
FLMUINT uiLoop;
SFLM_DB_STATS * pDbStats;
SFLM_LFILE_STATS * pLFile;
f_memcpy( pDestStats, pSrcStats, sizeof( SFLM_STATS));
// Zero out the database array. We need to do this
// so that if we get an error, we can correctly release memory for
// the destination structure.
pDestStats->uiNumDbStats = 0;
pDestStats->uiDbStatArraySize = 0;
pDestStats->pDbStats = NULL;
uiCount = 0;
if (pSrcStats->uiNumDbStats)
{
for (uiLoop = 0, pDbStats = pSrcStats->pDbStats;
uiLoop < pSrcStats->uiNumDbStats;
uiLoop++, pDbStats++)
{
if (pDbStats->bHaveStats)
{
uiCount++;
}
}
}
if (uiCount)
{
if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( SFLM_DB_STATS) * uiCount,
&pDestStats->pDbStats)))
{
goto Exit;
}
for (uiLoop = 0, uiCount = 0, pDbStats = pSrcStats->pDbStats;
uiLoop < pSrcStats->uiNumDbStats;
uiLoop++, pDbStats++)
{
if (pDbStats->bHaveStats)
{
char * pszTmpDbName;
pDestDb = &pDestStats->pDbStats [uiCount];
f_memcpy( pDestDb, pDbStats, sizeof( SFLM_DB_STATS));
// Allocate space for the database name
if (RC_BAD( rc = f_alloc( f_strlen( pDbStats->pszDbName) + 1,
&pszTmpDbName)))
{
goto Exit;
}
f_strcpy( pszTmpDbName, pDbStats->pszDbName);
pDestDb->pszDbName = pszTmpDbName;
// Zero out each store's LFILE statistics. We need to do this
// so that if we get an error, we can correctly release memory for
// the destination structure.
pDestDb->uiNumLFileStats = 0;
pDestDb->uiLFileStatArraySize = 0;
pDestDb->pLFileStats = NULL;
uiCount++;
}
}
pDestStats->uiDbStatArraySize =
pDestStats->uiNumDbStats = uiCount;
}
for (uiDb = pSrcStats->uiNumDbStats,
pDestDb = pDestStats->pDbStats,
pSrcDb = pSrcStats->pDbStats;
uiDb;
uiDb--, pSrcDb++)
{
if (!pSrcDb->bHaveStats)
{
continue;
}
pDestDb->uiNumLFileStats = 0;
pDestDb->uiLFileStatArraySize = 0;
pDestDb->pLFileStats = NULL;
uiCount = 0;
for (uiLoop = 0, pLFile = pSrcDb->pLFileStats;
uiLoop < pSrcDb->uiNumLFileStats;
uiLoop++, pLFile++)
{
if (pLFile->bHaveStats)
{
uiCount++;
}
}
if (uiCount)
{
if (RC_BAD( rc = f_calloc(
(FLMUINT)sizeof( SFLM_LFILE_STATS) * uiCount,
&pDestDb->pLFileStats)))
{
goto Exit;
}
uiCount = 0;
for (uiLoop = 0, pLFile = pSrcDb->pLFileStats;
uiLoop < pSrcDb->uiNumLFileStats;
uiLoop++, pLFile++)
{
if (pLFile->bHaveStats)
{
f_memcpy( &pDestDb->pLFileStats [uiCount],
pLFile, sizeof( SFLM_LFILE_STATS));
uiCount++;
}
}
pDestDb->uiLFileStatArraySize =
pDestDb->uiNumLFileStats = uiCount;
}
pDestDb++;
}
Exit:
if (RC_BAD( rc))
{
flmStatFree( pDestStats);
}
return( rc);
}
/****************************************************************************
Desc: This routine locates the appropriate SFLM_BLOCKIO_STATS structure for
a given block of data. NULL is returned if an appropriate one cannot
be found.
****************************************************************************/
SFLM_BLOCKIO_STATS * flmGetBlockIOStatPtr(
SFLM_DB_STATS * pDbStats,
SFLM_LFILE_STATS * pLFileStats,
FLMBYTE * pucBlk)
{
F_BLK_HDR * pBlkHdr = (F_BLK_HDR *)pucBlk;
if (pBlkHdr->ui8BlkType == BT_FREE)
{
pDbStats->bHaveStats = TRUE;
return( &pDbStats->AvailBlockStats);
}
else if (pBlkHdr->ui8BlkType == BT_LFH_BLK)
{
pDbStats->bHaveStats = TRUE;
return( &pDbStats->LFHBlockStats);
}
else if (pLFileStats)
{
pLFileStats->bHaveStats =
pDbStats->bHaveStats = TRUE;
// VISIT: Consider a one level tree.
// Is it more important to count root stats over leaf stats?
// What about the Data Only Blocks?
// Consider invalid type.
if (pBlkHdr->ui8BlkType != BT_LEAF &&
pBlkHdr->ui8BlkType != BT_NON_LEAF &&
pBlkHdr->ui8BlkType != BT_NON_LEAF_COUNTS &&
pBlkHdr->ui8BlkType != BT_LEAF_DATA)
{
return( &pLFileStats->LeafBlockStats);
}
if (pBlkHdr->ui32NextBlkInChain == 0 &&
pBlkHdr->ui32PrevBlkInChain == 0)
{
return( &pLFileStats->RootBlockStats);
}
else if ((pBlkHdr->ui8BlkType != BT_LEAF) &&
(pBlkHdr->ui8BlkType != BT_LEAF_DATA))
{
return( &pLFileStats->MiddleBlockStats);
}
else
{
return( &pLFileStats->LeafBlockStats);
}
}
else
{
return( (SFLM_BLOCKIO_STATS *)NULL);
}
}
/********************************************************************
Desc: Determine if a given year is a leap year.
*********************************************************************/
FINLINE FLMUINT flmLeapYear(
FLMUINT uiYear
)
{
if (uiYear % 4 != 0)
{
return( 0);
}
if (uiYear % 100 != 0)
{
return( 1);
}
if (uiYear % 400 != 0)
{
return( 0);
}
return( 1);
}
/********************************************************************
Desc: Calculate days in a given month of a given year.
*********************************************************************/
FSTATIC FLMUINT flmDaysInMonth(
FLMUINT uiYear,
FLMUINT uiMonth
)
{
switch (uiMonth + 1)
{
case 4:
case 6:
case 9:
case 11:
return( 30);
case 2:
return( 28 + flmLeapYear( uiYear));
default:
return( 31);
}
}
/********************************************************************
Desc: Adjust the time.
*********************************************************************/
FSTATIC void flmAdjustTime(
F_TMSTAMP * pTime,
FLMINT iStartPoint
)
{
switch (iStartPoint)
{
case 1:
goto Adj_1;
case 2:
goto Adj_2;
case 3:
goto Adj_3;
case 4:
goto Adj_4;
case 5:
goto Adj_5;
case 6:
goto Adj_6;
}
Adj_1:
if (pTime->hundredth >= 100)
{
pTime->second++;
pTime->hundredth = 0;
}
Adj_2:
if (pTime->second == 60)
{
pTime->minute++;
pTime->second = 0;
}
Adj_3:
if (pTime->minute == 60)
{
pTime->hour++;
pTime->minute = 0;
}
Adj_4:
if (pTime->hour == 24)
{
pTime->day++;
pTime->hour = 0;
}
Adj_5:
if ((FLMUINT)pTime->day > flmDaysInMonth( pTime->year, pTime->month))
{
pTime->month++;
pTime->day = 1;
}
Adj_6:
if (pTime->month > 11)
{
pTime->year++;
pTime->month = 1;
}
}
/********************************************************************
Desc: Calculate the elapsed time, including milliseconds.
*********************************************************************/
void flmAddElapTime(
F_TMSTAMP * pStartTime,
FLMUINT64 * pui64ElapMilli
)
{
F_TMSTAMP StartTime;
F_TMSTAMP EndTime;
FLMUINT uiSec = 0;
FLMUINT uiHundredth = 0;
f_timeGetTimeStamp( &EndTime);
f_memcpy( &StartTime, pStartTime, sizeof( F_TMSTAMP));
if (StartTime.year < EndTime.year)
{
if (StartTime.hundredth)
{
uiHundredth += (FLMUINT)(100 - StartTime.hundredth);
StartTime.hundredth = 0;
StartTime.second++;
flmAdjustTime( &StartTime, 2);
}
if (StartTime.second)
{
uiSec += (FLMUINT)(60 - StartTime.second);
StartTime.second = 0;
StartTime.minute++;
flmAdjustTime( &StartTime, 3);
}
if (StartTime.minute)
{
uiSec += (FLMUINT)((60 - StartTime.minute) * 60);
StartTime.minute = 0;
StartTime.hour++;
flmAdjustTime( &StartTime, 4);
}
if (StartTime.hour)
{
uiSec += (FLMUINT)((24 - StartTime.hour) * 3600);
StartTime.hour = 0;
StartTime.day++;
flmAdjustTime( &StartTime, 5);
}
if (StartTime.day > 1)
{
uiSec += (FLMUINT)(flmDaysInMonth( StartTime.year, StartTime.month) -
StartTime.day + 1) * (FLMUINT)86400;
StartTime.day = 1;
StartTime.month++;
flmAdjustTime( &StartTime, 6);
}
if (StartTime.month > 1)
{
while (StartTime.month <= 11)
{
uiSec += (FLMUINT)((FLMUINT)flmDaysInMonth( StartTime.year,
StartTime.month) * (FLMUINT)86400);
StartTime.month++;
}
StartTime.year++;
}
while (StartTime.year < EndTime.year)
{
uiSec += (FLMUINT)((FLMUINT)(365 + flmLeapYear( StartTime.year)) *
(FLMUINT)86400);
StartTime.year++;
}
}
if (StartTime.month < EndTime.month)
{
if (StartTime.hundredth)
{
uiHundredth += (FLMUINT)(100 - StartTime.hundredth);
StartTime.hundredth = 0;
StartTime.second++;
flmAdjustTime( &StartTime, 2);
}
if (StartTime.second)
{
uiSec += (FLMUINT)(60 - StartTime.second);
StartTime.second = 0;
StartTime.minute++;
flmAdjustTime( &StartTime, 3);
}
if (StartTime.minute)
{
uiSec += (FLMUINT)((60 - StartTime.minute) * 60);
StartTime.minute = 0;
StartTime.hour++;
flmAdjustTime( &StartTime, 4);
}
if (StartTime.hour)
{
uiSec += (FLMUINT)((24 - StartTime.hour) * 3600);
StartTime.hour = 0;
StartTime.day++;
flmAdjustTime( &StartTime, 5);
}
if (StartTime.day > 1)
{
uiSec += (FLMUINT)(flmDaysInMonth( StartTime.year, StartTime.month) -
StartTime.day + 1) * (FLMUINT)86400;
StartTime.day = 1;
StartTime.month++;
flmAdjustTime( &StartTime, 6);
}
while (StartTime.month < EndTime.month)
{
uiSec += (FLMUINT)((FLMUINT)flmDaysInMonth( StartTime.year,
StartTime.month) * (FLMUINT)86400);
StartTime.month++;
}
}
if (StartTime.day < EndTime.day)
{
if (StartTime.hundredth)
{
uiHundredth += (FLMUINT)(100 - StartTime.hundredth);
StartTime.hundredth = 0;
StartTime.second++;
flmAdjustTime( &StartTime, 2);
}
if (StartTime.second)
{
uiSec += (FLMUINT)(60 - StartTime.second);
StartTime.second = 0;
StartTime.minute++;
flmAdjustTime( &StartTime, 3);
}
if (StartTime.minute)
{
uiSec += (FLMUINT)((60 - StartTime.minute) * 60);
StartTime.minute = 0;
StartTime.hour++;
flmAdjustTime( &StartTime, 4);
}
if (StartTime.hour)
{
uiSec += (FLMUINT)((24 - StartTime.hour) * 3600);
StartTime.hour = 0;
StartTime.day++;
flmAdjustTime( &StartTime, 5);
}
uiSec += (FLMUINT)(EndTime.day - StartTime.day) * (FLMUINT)86400;
StartTime.day = 1;
StartTime.month++;
flmAdjustTime( &StartTime, 6);
}
if (StartTime.hour < EndTime.hour)
{
if (StartTime.hundredth)
{
uiHundredth += (FLMUINT)(100 - StartTime.hundredth);
StartTime.hundredth = 0;
StartTime.second++;
flmAdjustTime( &StartTime, 2);
}
if (StartTime.second)
{
uiSec += (FLMUINT)(60 - StartTime.second);
StartTime.second = 0;
StartTime.minute++;
flmAdjustTime( &StartTime, 3);
}
if (StartTime.minute)
{
uiSec += (FLMUINT)((60 - StartTime.minute) * 60);
StartTime.minute = 0;
StartTime.hour++;
flmAdjustTime( &StartTime, 4);
}
uiSec += (FLMUINT)((EndTime.hour - StartTime.hour) * 3600);
StartTime.hour = 0;
StartTime.day++;
flmAdjustTime( &StartTime, 5);
}
if (StartTime.minute < EndTime.minute)
{
if (StartTime.hundredth)
{
uiHundredth += (FLMUINT)(100 - StartTime.hundredth);
StartTime.hundredth = 0;
StartTime.second++;
flmAdjustTime( &StartTime, 2);
}
if (StartTime.second)
{
uiSec += (FLMUINT)(60 - StartTime.second);
StartTime.second = 0;
StartTime.minute++;
flmAdjustTime( &StartTime, 3);
}
uiSec += (FLMUINT)((EndTime.minute - StartTime.minute) * 60);
StartTime.minute = 0;
StartTime.hour++;
flmAdjustTime( &StartTime, 4);
}
if (StartTime.second < EndTime.second)
{
if (StartTime.hundredth)
{
uiHundredth += (FLMUINT)(100 - StartTime.hundredth);
StartTime.hundredth = 0;
StartTime.second++;
flmAdjustTime( &StartTime, 2);
}
uiSec += (FLMUINT)(EndTime.second - StartTime.second);
StartTime.second = 0;
StartTime.minute++;
flmAdjustTime( &StartTime, 3);
}
if (StartTime.hundredth < EndTime.hundredth)
{
uiHundredth += (FLMUINT)(EndTime.hundredth - StartTime.hundredth);
}
if (uiSec)
{
(*pui64ElapMilli) += (FLMUINT64)((uiHundredth * 10 + uiSec * 1000));
}
else
{
(*pui64ElapMilli) += (FLMUINT64)(uiHundredth * 10);
}
}