windows-nt/Source/XPSP1/NT/enduser/troubleshoot/tshoot/counter.cpp
2020-09-26 16:20:57 +08:00

819 lines
19 KiB
C++

//
// MODULE: COUNTER.CPP
//
// PURPOSE: implementation the counter classes:
// CPeriodicTotals (utility class)
// CAbstractCounter (abstract base class).
// CCounter (simple counter)
// CHourlyCounter (counter with "bins" for each hour of the day)
// CDailyCounter (counter with "bins" for day of the week)
// CHourlyDailyCounter (counter with "bins" for each hour of the day and each day of the week)
//
// PROJECT: Generic Troubleshooter DLL for Microsoft AnswerPoint
//
// COMPANY: Saltmine Creative, Inc. (206)-284-7511 support@saltmine.com
//
// AUTHOR: Joe Mabel
//
// ORIGINAL DATE: 7-20-1998
//
// NOTES:
// 1. Right as daylight savings time clicks in, there will be a few anomalies.
// Since this defines "days" to be 24-hour periods, rather than calendar days,
// if you have just gone from standard time to daylight time, "previous days"
// before the switch will begin at 11pm the night before the relevant day;
// if you have just gone from daylight time to standard time, "previous days"
// before the switch will begin at 1am on the relevant day.
//
// Version Date By Comments
//--------------------------------------------------------------------
// V3.0 7-20-98 JM Original
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "event.h"
#include "SafeTime.h"
#include "Counter.h"
#include "CounterMgr.h"
#include "baseexception.h"
#include <new>
#include "CharConv.h"
#include "apiwraps.h"
const long k_secsPerHour = 3600;
const long k_secsPerDay = k_secsPerHour * 24;
const long k_secsPerWeek = k_secsPerDay * 7;
//////////////////////////////////////////////////////////////////////
// CPeriodicTotals
// Utility class, returned to provide an effective table of hourly/daily
// counts.
//////////////////////////////////////////////////////////////////////
CPeriodicTotals::CPeriodicTotals(long nPeriods) :
m_nPeriods(nPeriods),
m_ptime(NULL),
m_pCount(NULL)
{
Reset();
}
CPeriodicTotals::~CPeriodicTotals()
{
ReleaseMem();
}
void CPeriodicTotals::Reset()
{
ReleaseMem();
m_nPeriodsSet = 0;
m_iPeriod = 0;
m_ptime = NULL;
m_pCount = NULL;
try
{
m_ptime = new time_t[m_nPeriods];
m_pCount = new long[m_nPeriods];
}
catch (bad_alloc&)
{
// Set the number of periods to zero, release any allocated memory, and rethrow the exception.
m_nPeriods= 0;
ReleaseMem();
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
SrcLoc.GetSrcFileLineStr(),
_T(""), _T(""), EV_GTS_CANT_ALLOC );
throw;
}
}
void CPeriodicTotals::ReleaseMem()
{
delete [] m_ptime;
delete [] m_pCount;
}
// Set the time & Count values at the current position and increment the position
bool CPeriodicTotals::SetNext(time_t time, long Count)
{
if (m_iPeriod >= m_nPeriods)
return false;
m_ptime[m_iPeriod] = time;
m_pCount[m_iPeriod++] = Count;
m_nPeriodsSet++;
return true;
}
// Format a time and count suitably for HTML or other text use.
// returns a reference of convenience to the same string passed in.
CString & CPeriodicTotals::DisplayPeriod(long i, CString & str) const
{
CString strTime;
{
CSafeTime safe(m_ptime[i]);
str = safe.StrLocalTime();
}
strTime.Format(_T(" %8.8d"), m_pCount[i]);
str += strTime;
return str;
}
//////////////////////////////////////////////////////////////////////
// CHourlyTotals
//////////////////////////////////////////////////////////////////////
CHourlyTotals::CHourlyTotals() :
CPeriodicTotals (24+1)
{
}
CHourlyTotals::~CHourlyTotals()
{
}
// This is strictly for display to operator, so hard-coding English is OK
// returns a reference of convenience to the same string passed in.
CString CHourlyTotals::HTMLDisplay() const
{
CString str, strTemp;
if (m_nPeriodsSet > 1)
{
str += _T("<TR>\n");
str += _T("<TD ROWSPAN=\"24\" ALIGN=\"CENTER\" BGCOLOR=\"#CCCC99\">\n");
str += _T("<B>Last 24 hours: </B>");
str += _T("</TD>\n");
for (int i=0; i<24 && i<m_nPeriodsSet-1 ; i++)
{
if (i!=0)
str += _T("<TR>\n");
str+= _T("<TD ALIGN=\"CENTER\" BGCOLOR=\"#FFFFCC\">\n");
CPeriodicTotals::DisplayPeriod(i, strTemp);
str += strTemp;
str += _T("</TD>\n");
str += _T("</TR>\n");
}
}
if (m_nPeriodsSet >= 1)
{
str += _T("<TR>\n");
str += _T("<TD ALIGN=\"CENTER\" BGCOLOR=\"#CCCC99\"> \n");
str += _T("<B>Current hour:</B> ");
str += _T("</TD>\n");
str += _T("<TD ALIGN=\"CENTER\" BGCOLOR=\"#FFFFCC\">\n");
CPeriodicTotals::DisplayPeriod(m_nPeriodsSet-1, strTemp);
str += strTemp;
str += _T("</TD>\n");
str += _T("</TR>\n");
}
else
str = _T("<BR>No hourly data.");
return str;
}
//////////////////////////////////////////////////////////////////////
// CDailyTotals
//////////////////////////////////////////////////////////////////////
CDailyTotals::CDailyTotals() :
CPeriodicTotals (7+1)
{
}
CDailyTotals::~CDailyTotals()
{
}
// This is strictly for display to operator, so hard-coding English is OK
// returns a reference of convenience to the same string passed in.
CString CDailyTotals::HTMLDisplay() const
{
CString str, strTemp;
if (m_nPeriodsSet > 1)
{
str = _T("<TR>\n");
str+= _T("<TD ROWSPAN=\"7\" ALIGN=\"CENTER\" BGCOLOR=\"#CCCC99\">\n");
str += _T("<B>Last 7 days: </B>");
str += _T("</TD>\n");
for (int i=0; i<7 && i<m_nPeriodsSet-1 ; i++)
{
if (i!=0)
str += _T("<TR>\n");
str+= _T("<TD ALIGN=\"CENTER\" BGCOLOR=\"#FFFFCC\">\n");
CPeriodicTotals::DisplayPeriod(i, strTemp);
str += strTemp;
str += _T("</TD>\n");
str += _T("</TR>\n");
}
}
if (m_nPeriodsSet >= 1)
{
str += _T("<TR>\n");
str += _T("<TD ALIGN=\"CENTER\" BGCOLOR=\"#CCCC99\"> \n");
str += _T("<B>Today: </B>");
str += _T("</TD>\n");
str += _T("<TD ALIGN=\"CENTER\" BGCOLOR=\"#FFFFCC\">\n");
CPeriodicTotals::DisplayPeriod(m_nPeriodsSet-1, strTemp);
str += strTemp;
str += _T("</TD>\n");
str += _T("</TR>\n");
}
else
str = _T("<BR>No daily data.");
return str;
}
//////////////////////////////////////////////////////////////////////
// CCounterLocation
//////////////////////////////////////////////////////////////////////
/*static*/ LPCTSTR CCounterLocation::m_GlobalStr = _T("Global");
/*static*/ LPCTSTR CCounterLocation::m_TopicStr = _T("Topic ");
/*static*/ LPCTSTR CCounterLocation::m_ThreadStr = _T("Thread ");
CCounterLocation::CCounterLocation(EId id, LPCTSTR scope /*=m_GlobalStr*/)
: m_Scope(scope),
m_Id(id)
{
}
CCounterLocation::~CCounterLocation()
{
}
//////////////////////////////////////////////////////////////////////
// CAbstractCounter
//////////////////////////////////////////////////////////////////////
CAbstractCounter::CAbstractCounter(EId id /*=eIdGeneric*/, CString scope /*=m_GlobalStr*/)
: CCounterLocation(id, scope)
{
::Get_g_CounterMgr()->AddSubstitute(*this);
}
CAbstractCounter::~CAbstractCounter()
{
::Get_g_CounterMgr()->Remove(*this);
}
//////////////////////////////////////////////////////////////////////
// CCounter
// a simple counter
//////////////////////////////////////////////////////////////////////
CCounter::CCounter(EId id /*=eIdGeneric*/, CString scope /*=m_GlobalStr*/)
: CAbstractCounter(id, scope)
{
Clear();
}
CCounter::~CCounter()
{
}
void CCounter::Increment()
{
::InterlockedIncrement( &m_Count );
}
void CCounter::Clear()
{
::InterlockedExchange( &m_Count, 0);
}
void CCounter::Init(long count)
{
::InterlockedExchange( &m_Count, count);
}
long CCounter::Get() const
{
return m_Count;
}
//////////////////////////////////////////////////////////////////////
// CHourlyCounter
// This counter maintains bins to keep track of values on a per-hour basis.
// The code that sets the values can treat this as a CAbstractCounter.
// Additional public functions are available to report results.
//////////////////////////////////////////////////////////////////////
CHourlyCounter::CHourlyCounter() :
m_ThisHour (-1),
m_ThisTime (0)
{
m_hMutex = ::CreateMutex(NULL, FALSE, NULL);
if (!m_hMutex)
{
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
SrcLoc.GetSrcFileLineStr(),
_T("Hourly"),
_T(""),
EV_GTS_ERROR_MUTEX );
}
Clear();
}
CHourlyCounter::~CHourlyCounter()
{
::CloseHandle(m_hMutex);
}
void CHourlyCounter::Increment()
{
WAIT_INFINITE( m_hMutex );
SetHour();
m_arrCount[m_ThisHour].Increment();
::ReleaseMutex(m_hMutex);
}
void CHourlyCounter::Clear()
{
WAIT_INFINITE( m_hMutex );
for (long i = 0; i < 24; i++)
m_arrCount[i].Clear();
m_nThisHourYesterday = 0;
::ReleaseMutex(m_hMutex);
}
void CHourlyCounter::Init(long count)
{
CHourlyCounter::Clear();
WAIT_INFINITE( m_hMutex );
SetHour();
m_arrCount[m_ThisHour].Init(count);
::ReleaseMutex(m_hMutex);
}
// return a 24-hour total prior to the present hour.
// non-const because it calls SetHour()
long CHourlyCounter::GetDayCount()
{
long DayCount = 0;
WAIT_INFINITE( m_hMutex );
SetHour();
for (long i=0; i<24; i++)
{
if ( i != m_ThisHour )
DayCount += m_arrCount[i].Get();
DayCount += m_nThisHourYesterday;
}
::ReleaseMutex(m_hMutex);
return DayCount;
}
// non-const because it calls SetHour()
void CHourlyCounter::GetHourlies(CHourlyTotals & totals)
{
WAIT_INFINITE( m_hMutex );
totals.Reset();
SetHour();
time_t time = m_ThisTime - (k_secsPerDay);
totals.SetNext(time, m_nThisHourYesterday);
long i;
for (i=m_ThisHour+1; i<24; i++)
{
time += k_secsPerHour;
totals.SetNext(time, m_arrCount[i].Get());
}
for (i=0; i<=m_ThisHour; i++)
{
time += k_secsPerHour;
totals.SetNext(time, m_arrCount[i].Get());
}
::ReleaseMutex(m_hMutex);
}
// Based on the present time, shifts to the appropriate bin.
void CHourlyCounter::SetHour()
{
time_t timeNow;
time_t timeStartOfHour;
WAIT_INFINITE( m_hMutex );
time(&timeNow);
timeStartOfHour = (timeNow / k_secsPerHour) * k_secsPerHour;
if (timeStartOfHour > m_ThisTime)
{
// If we get here, hour changed. Typically the last action was the previous
// hour, but the algorithm here does not require that.
long Hour;
{
// minimize how long we use CSafeTime, because it means holding a mutex.
CSafeTime safe(timeStartOfHour);
Hour = safe.LocalTime().tm_hour;
}
if (timeStartOfHour - m_ThisTime > k_secsPerDay)
Clear();
else
{
m_nThisHourYesterday = m_arrCount[Hour].Get();
if (m_ThisHour > Hour)
{
long i;
for (i=m_ThisHour+1; i<24; i++)
{
m_arrCount[i].Clear();
}
for (i=0; i<=Hour; i++)
{
m_arrCount[i].Clear();
}
}
else
{
for (long i=m_ThisHour+1; i<=Hour; i++)
{
m_arrCount[i].Clear();
}
}
}
m_ThisHour = Hour;
m_ThisTime = timeStartOfHour;
}
::ReleaseMutex(m_hMutex);
return;
}
//////////////////////////////////////////////////////////////////////
// CDailyCounter
// This counter maintains bins to keep track of values on a per-day basis.
// The code that sets the values can treat this as a CAbstractCounter.
// Additional public functions are available to report results.
// This could share more code with CHourlyCounter, but it would be very hard to come up
// with appropriate variable and function names, so we are suffering dual maintenance.
//////////////////////////////////////////////////////////////////////
CDailyCounter::CDailyCounter() :
m_ThisDay (-1),
m_ThisTime (0)
{
m_hMutex = ::CreateMutex(NULL, FALSE, NULL);
if (!m_hMutex)
{
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
SrcLoc.GetSrcFileLineStr(),
_T("Daily"),
_T(""),
EV_GTS_ERROR_MUTEX );
}
Clear();
}
CDailyCounter::~CDailyCounter()
{
::CloseHandle(m_hMutex);
}
void CDailyCounter::Increment()
{
WAIT_INFINITE( m_hMutex );
SetDay();
m_arrCount[m_ThisDay].Increment();
::ReleaseMutex(m_hMutex);
}
void CDailyCounter::Clear()
{
WAIT_INFINITE( m_hMutex );
for (long i = 0; i < 7; i++)
m_arrCount[i].Clear();
m_nThisDayLastWeek = 0;
::ReleaseMutex(m_hMutex);
}
void CDailyCounter::Init(long count)
{
CDailyCounter::Clear();
WAIT_INFINITE( m_hMutex );
SetDay();
m_arrCount[m_ThisDay].Init(count);
::ReleaseMutex(m_hMutex);
}
// return a 7-day total prior to the present day.
// non-const because it calls SetDay()
long CDailyCounter::GetWeekCount()
{
long WeekCount = 0;
WAIT_INFINITE( m_hMutex );
SetDay();
for (long i=0; i<7; i++)
{
if ( i != m_ThisDay )
WeekCount += m_arrCount[i].Get();
WeekCount += m_nThisDayLastWeek;
}
::ReleaseMutex(m_hMutex);
return WeekCount;
}
// non-const because it calls SetDay()
void CDailyCounter::GetDailies(CDailyTotals & totals)
{
WAIT_INFINITE( m_hMutex );
totals.Reset();
SetDay();
time_t time = m_ThisTime - (k_secsPerWeek);
totals.SetNext(time, m_nThisDayLastWeek);
long i;
for (i=m_ThisDay+1; i<7; i++)
{
time += k_secsPerDay;
totals.SetNext(time, m_arrCount[i].Get());
}
for (i=0; i<=m_ThisDay; i++)
{
time += k_secsPerDay;
totals.SetNext(time, m_arrCount[i].Get());
}
::ReleaseMutex(m_hMutex);
}
// Based on the present time, shifts to the appropriate bin.
void CDailyCounter::SetDay()
{
time_t timeNow;
time_t timeStartOfDay;
WAIT_INFINITE( m_hMutex );
time(&timeNow);
// Want to get start of day local time.
// Can't just set timeStartOfDay = (timeNow / k_secsPerDay) * k_secsPerDay
// because that would be the start of the day based on GMT!
long DayOfWeek;
{
// minimize how long we use CSafeTime, because it means holding a mutex.
CSafeTime safe(timeNow);
struct tm tmStartOfDay = safe.LocalTime();
DayOfWeek = tmStartOfDay.tm_wday;
tmStartOfDay.tm_sec = 0;
tmStartOfDay.tm_min = 0;
tmStartOfDay.tm_hour = 0;
timeStartOfDay = mktime(&tmStartOfDay);
}
if (timeStartOfDay > m_ThisTime)
{
// If we get here, day changed. Typically the last action was the previous
// hour, but the algorithm here does not require that.
{
// minimize how long we use CSafeTime, because it means holding a mutex.
CSafeTime safe(timeStartOfDay);
DayOfWeek = safe.LocalTime().tm_wday;
}
if (timeStartOfDay - m_ThisTime > k_secsPerWeek)
Clear();
else
{
m_nThisDayLastWeek = m_arrCount[DayOfWeek].Get();
if (m_ThisDay > DayOfWeek)
{
long i;
for (i=m_ThisDay+1; i<7; i++)
{
m_arrCount[i].Clear();
}
for (i=0; i<=DayOfWeek; i++)
{
m_arrCount[i].Clear();
}
}
else
{
for (long i=m_ThisDay+1; i<=DayOfWeek; i++)
{
m_arrCount[i].Clear();
}
}
}
m_ThisDay = DayOfWeek;
m_ThisTime = timeStartOfDay;
}
::ReleaseMutex(m_hMutex);
return;
}
//////////////////////////////////////////////////////////////////////
// CHourlyDailyCounter
//////////////////////////////////////////////////////////////////////
CHourlyDailyCounter::CHourlyDailyCounter(EId id /*=eIdGeneric*/, CString scope /*=m_GlobalStr*/)
: CAbstractCounter(id, scope),
m_Total(0),
m_timeFirst(0),
m_timeLast(0),
m_timeCleared(0)
{
m_hMutex = ::CreateMutex(NULL, FALSE, NULL);
if (!m_hMutex)
{
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
SrcLoc.GetSrcFileLineStr(),
_T("HourlyDaily"),
_T(""),
EV_GTS_ERROR_MUTEX );
}
time(&m_timeCreated);
time(&m_timeCleared);
}
CHourlyDailyCounter::~CHourlyDailyCounter()
{
::CloseHandle(m_hMutex);
}
void CHourlyDailyCounter::Increment()
{
WAIT_INFINITE( m_hMutex );
m_hourly.Increment();
m_daily.Increment();
m_Total++;
time(&m_timeLast);
if (!m_timeFirst)
m_timeFirst = m_timeLast;
::ReleaseMutex(m_hMutex);
}
void CHourlyDailyCounter::Clear()
{
WAIT_INFINITE( m_hMutex );
m_hourly.Clear();
m_daily.Clear();
m_Total= 0;
m_timeFirst = 0;
m_timeLast = 0;
time(&m_timeCleared);
::ReleaseMutex(m_hMutex);
}
void CHourlyDailyCounter::Init(long count)
{
CHourlyDailyCounter::Clear();
WAIT_INFINITE( m_hMutex );
m_hourly.Init(count);
m_daily.Init(count);
m_Total = count;
time(&m_timeLast);
if (!m_timeFirst)
m_timeFirst = m_timeLast;
::ReleaseMutex(m_hMutex);
}
// no need to lock here, because m_hourly does its own locking.
long CHourlyDailyCounter::GetDayCount()
{
return m_hourly.GetDayCount();
}
// no need to lock here, because m_hourly does its own locking.
void CHourlyDailyCounter::GetHourlies(CHourlyTotals & totals)
{
m_hourly.GetHourlies(totals);
}
// no need to lock here, because m_daily does its own locking.
long CHourlyDailyCounter::GetWeekCount()
{
return m_daily.GetWeekCount();
}
// no need to lock here, because m_daily does its own locking.
void CHourlyDailyCounter::GetDailies(CDailyTotals & totals)
{
m_daily.GetDailies(totals);
}
long CHourlyDailyCounter::GetTotal() const
{
WAIT_INFINITE( m_hMutex );
long ret = m_Total;
::ReleaseMutex(m_hMutex);
return ret;
};
time_t CHourlyDailyCounter::GetTimeFirst() const
{
WAIT_INFINITE( m_hMutex );
time_t ret = m_timeFirst;
::ReleaseMutex(m_hMutex);
return ret;
};
time_t CHourlyDailyCounter::GetTimeLast() const
{
WAIT_INFINITE( m_hMutex );
time_t ret = m_timeLast;
::ReleaseMutex(m_hMutex);
return ret;
};
time_t CHourlyDailyCounter::GetTimeCleared() const
{
WAIT_INFINITE( m_hMutex );
time_t ret = m_timeCleared;
::ReleaseMutex(m_hMutex);
return ret;
};
time_t CHourlyDailyCounter::GetTimeCreated() const
{
WAIT_INFINITE( m_hMutex );
time_t ret = m_timeCreated;
::ReleaseMutex(m_hMutex);
return ret;
}
time_t CHourlyDailyCounter::GetTimeNow() const
{
// No need to lock mutex on this call.
time_t ret;
time(&ret);
return ret;
}
////////////////////////////////////////////////////////////////////////////////////
// CDisplayCounter...::Display() implementation
////////////////////////////////////////////////////////////////////////////////////
#define STATUS_INVALID_NUMBER_STR _T("none")
#define STATUS_INVALID_TIME_STR _T("none")
CString CDisplayCounterTotal::Display()
{
TCHAR buf[128] = {0};
_stprintf(buf, _T("%ld"), long(((CHourlyDailyCounter*)m_pAbstractCounter)->GetTotal()));
return buf;
}
CString CDisplayCounterCurrentDateTime::Display()
{
return CSafeTime(((CHourlyDailyCounter*)m_pAbstractCounter)->GetTimeNow()).StrLocalTime(STATUS_INVALID_TIME_STR);
}
CString CDisplayCounterCreateDateTime::Display()
{
return CSafeTime(((CHourlyDailyCounter*)m_pAbstractCounter)->GetTimeCreated()).StrLocalTime(STATUS_INVALID_TIME_STR);
}
CString CDisplayCounterFirstDateTime::Display()
{
return CSafeTime(((CHourlyDailyCounter*)m_pAbstractCounter)->GetTimeFirst()).StrLocalTime(STATUS_INVALID_TIME_STR);
}
CString CDisplayCounterLastDateTime::Display()
{
return CSafeTime(((CHourlyDailyCounter*)m_pAbstractCounter)->GetTimeLast()).StrLocalTime(STATUS_INVALID_TIME_STR);
}
CString CDisplayCounterDailyHourly::Display()
{
CString ret;
if (m_pDailyTotals) {
((CHourlyDailyCounter*)m_pAbstractCounter)->GetDailies(*m_pDailyTotals);
ret += m_pDailyTotals->HTMLDisplay();
}
if (m_pHourlyTotals) {
((CHourlyDailyCounter*)m_pAbstractCounter)->GetHourlies(*m_pHourlyTotals);
ret += m_pHourlyTotals->HTMLDisplay();
}
return ret;
}