1344 lines
32 KiB
C++
1344 lines
32 KiB
C++
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
//
|
|
// HTTPXPC.CXX
|
|
//
|
|
// Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
|
|
//
|
|
// Monitor-side for W3 integration.
|
|
//
|
|
|
|
#include "w3p.hxx" // W3SVC Precompiled header
|
|
|
|
#include <windows.h>
|
|
#include <winperf.h>
|
|
#include "pclib.hxx"
|
|
#include "httpxctr.hxx" // Perf counter title index #defines
|
|
#include "caldbg.h" // For DebugTrace()/Assert()
|
|
|
|
|
|
EXTERN_C const WCHAR gc_wszSignature[] = L"HTTPEXT";
|
|
EXTERN_C const CHAR gc_szDbgIni[] = "HTTPXPC.INI";
|
|
EXTERN_C const CHAR gc_szSignature[] = "HTTPXPC";
|
|
|
|
//
|
|
// This is declared here because the shared memory library
|
|
// now requires any dll or exe using it to define this function.
|
|
// However this function is only used for the shared lock cache
|
|
// so we do not need to actually implement it for perf counters.
|
|
//
|
|
namespace IMPLSTUB
|
|
{
|
|
VOID __fastcall SaveHandle(HANDLE hHandle);
|
|
}
|
|
|
|
VOID __fastcall
|
|
IMPLSTUB::SaveHandle(HANDLE h)
|
|
{
|
|
Assert ( FALSE );
|
|
return;
|
|
}
|
|
// end of hack working around shared lock cache.
|
|
|
|
|
|
//
|
|
// The following macros, templates and classes are derived from
|
|
// sources in the DAV project.
|
|
//
|
|
|
|
// ========================================================================
|
|
//
|
|
// TEMPLATE CLASS Singleton
|
|
//
|
|
// Use this template to implement classes that can only have one instance.
|
|
// NOTE: For ref-counted or on-demand global objects, see below
|
|
// (RefCountedGlobal and OnDemandGlobal).
|
|
//
|
|
// The Singleton template provides the following:
|
|
//
|
|
// o a common memory layout for singleton classes which
|
|
// allows template folding to reduce overall code size.
|
|
//
|
|
// o an instantiation mechanism that verifies (asserts)
|
|
// that only instance of your class ever exists.
|
|
//
|
|
// o asserts to catch any code which attempts to use
|
|
// your class when it's not initialized.
|
|
//
|
|
// To use this template, declare your class like this:
|
|
//
|
|
// class YourClass : private Singleton<YourClass>
|
|
// {
|
|
// //
|
|
// // Declare Singleton as a friend of YourClass
|
|
// // if YourClass's constructor is private (which it
|
|
// // should be since YourClass is a singleton and
|
|
// // should not be allowed to arbitrarily create
|
|
// // instances of it).
|
|
// //
|
|
// friend class Singleton<YourClass>;
|
|
//
|
|
// //
|
|
// // YourClass private members. Since the 'staticness'
|
|
// // of YourClass is provided entirely by this template,
|
|
// // you do not need to declare your members as 'static'
|
|
// // and you should use standard Hungarian class member
|
|
// // naming conventions (e.g. m_dwShoeSize).
|
|
// //
|
|
// [...]
|
|
//
|
|
// public:
|
|
// //
|
|
// // Public declarations for YourClass. Among these should
|
|
// // be static functions to init and deinit YourClass which
|
|
// // would call CreateInstance() and DestroyInstance()
|
|
// // respectively. Or, you could just expose these functions
|
|
// // directly to clients of YourClass with using declarations:
|
|
// //
|
|
// // using Singleton<YourClass>::CreateInstance;
|
|
// // using Singleton<YourClass>::DestroyInstance;
|
|
// //
|
|
// // Similarly, YourClass will probably have additional
|
|
// // static methods which access or operate on the
|
|
// // singleton instance. These will call Instance()
|
|
// // to get at the global instance. Or, though it's
|
|
// // not recommended from an encapsulation standpoint,
|
|
// // you could expose the global instance directly to
|
|
// // clients with:
|
|
// //
|
|
// // using Singleton<YourClass>::Instance;
|
|
// //
|
|
// [...]
|
|
// };
|
|
//
|
|
template<class _X>
|
|
class Singleton
|
|
{
|
|
//
|
|
// Space for the sole instance
|
|
//
|
|
static BYTE sm_rgbInstance[];
|
|
|
|
//
|
|
// Pointer to the instance
|
|
//
|
|
static _X * sm_pInstance;
|
|
|
|
public:
|
|
// STATICS
|
|
//
|
|
|
|
//
|
|
// Create the single, global instance of class _X.
|
|
//
|
|
static _X& CreateInstance()
|
|
{
|
|
//
|
|
// This actually calls Singleton::new()
|
|
// (defined below), but calls to new()
|
|
// must always be unqualified.
|
|
//
|
|
return *(new _X());
|
|
}
|
|
|
|
//
|
|
// Destroy the single, global instance of class _X.
|
|
//
|
|
static VOID DestroyInstance()
|
|
{
|
|
//
|
|
// This actually calls Singleton::delete()
|
|
// (defined below), but calls to delete()
|
|
// must always be unqualified.
|
|
//
|
|
delete sm_pInstance;
|
|
}
|
|
|
|
//
|
|
// Provide access to the single, global instance of class _X.
|
|
//
|
|
static _X& Instance()
|
|
{
|
|
Assert( sm_pInstance );
|
|
|
|
return *sm_pInstance;
|
|
}
|
|
|
|
//
|
|
// Singleton operator new and operator delete "allocate"
|
|
// space for the object in static memory. These must be
|
|
// defined public for syntactic reasons. Do NOT call them
|
|
// directly!! Use CreateInstance()/DestroyInstance().
|
|
//
|
|
static void * operator new(size_t)
|
|
{
|
|
Assert( !sm_pInstance );
|
|
|
|
//
|
|
// Just return a pointer to the space
|
|
// in which to instantiate the object.
|
|
//
|
|
sm_pInstance = reinterpret_cast<_X *>(sm_rgbInstance);
|
|
return sm_pInstance;
|
|
}
|
|
|
|
static void operator delete(void *, size_t)
|
|
{
|
|
Assert( sm_pInstance );
|
|
|
|
//
|
|
// Since nothing was done to allocate space
|
|
// for the instance, we don't do anything
|
|
// here to free it.
|
|
//
|
|
sm_pInstance = NULL;
|
|
}
|
|
|
|
};
|
|
|
|
//
|
|
// Space for the sole instance of class _X
|
|
//
|
|
template<class _X>
|
|
BYTE Singleton<_X>::sm_rgbInstance[sizeof(_X)];
|
|
|
|
//
|
|
// Pointer to the instance
|
|
//
|
|
template<class _X>
|
|
_X * Singleton<_X>::sm_pInstance = NULL;
|
|
|
|
|
|
// ========================================================================
|
|
//
|
|
// CLASS _Empty
|
|
//
|
|
// A completely empty, but instantiatable class. Use the _Empty class
|
|
// to get around the syntactic inability to instantiate anything of
|
|
// type void (or VOID).
|
|
//
|
|
// In retail builds, _Empty has the same memory footprint and code
|
|
// impact as void -- none.
|
|
//
|
|
// See the RefCountedGlobal template below for a usage example.
|
|
//
|
|
class _Empty
|
|
{
|
|
// NOT IMPLEMENTED
|
|
//
|
|
_Empty( const _Empty& );
|
|
_Empty& operator=( const _Empty& );
|
|
|
|
public:
|
|
_Empty() {}
|
|
~_Empty() {}
|
|
};
|
|
|
|
|
|
// ========================================================================
|
|
//
|
|
// TEMPLATE CLASS RefCountedGlobal
|
|
//
|
|
// Use this template as boilerplate for any class that encapsulates
|
|
// a global, refcounted initialization/deinitialization process.
|
|
//
|
|
// This template maintains proper refcounting and synchronization when
|
|
// there are multiple threads trying to initialize and deinitialize
|
|
// references at the same time. And it does so without a critical
|
|
// section.
|
|
//
|
|
// To use this template, declare your class like this:
|
|
//
|
|
// class YourClass : private RefCountedGlobal<YourClass>
|
|
// {
|
|
// //
|
|
// // Declare Singleton and RefCountedGlobal as friends
|
|
// // of YourClass so that they can call your private
|
|
// // initialization functions.
|
|
// //
|
|
// friend class Singleton<YourClass>;
|
|
// friend class RefCountedGlobal<YourClass>;
|
|
//
|
|
// //
|
|
// // Private declarations for YourClass
|
|
// //
|
|
// [...]
|
|
//
|
|
// //
|
|
// // Failable initialization function. This function
|
|
// // should perform any failable initialization of the
|
|
// // instance of YourClass. It should return TRUE
|
|
// // if initialization succeeds, and FALSE otherwise.
|
|
// // If YourClass does not have any initialization that
|
|
// // can fail then you should implement this function inline
|
|
// // to just return TRUE.
|
|
// //
|
|
// BOOL FInit();
|
|
//
|
|
// public:
|
|
// //
|
|
// // Public declarations for YourClass. Among these should
|
|
// // be static functions to init and deinit YourClass. These
|
|
// // functions would call DwInitRef() and DeinitRef() respectively.
|
|
// // Or, you could just expose DwInitRef() and DeinitRef()
|
|
// // directly to clients of YourClass with using declarations:
|
|
// //
|
|
// // using RefCountedGlobal<YourClass>::DwInitRef;
|
|
// // using RefCountedGlobal<YourClass>::DeinitRef;
|
|
// //
|
|
// [...]
|
|
// };
|
|
//
|
|
// If YourClass::FInit() succeeds (returns TRUE), then DwInitRef()
|
|
// returns the new refcount. If YourClass::FInit() fails (returns
|
|
// FALSE), then DwInitRef() returns 0.
|
|
//
|
|
// See \cal\src\inc\memx.h for sample usage.
|
|
//
|
|
// If YourClass::FInit() requires initialization parameters, you can
|
|
// still use the RefCountedGlobal template. You just need to provide
|
|
// your parameter type in the template instantiation and declare your
|
|
// FInit() to take a const reference to a parameter of that type:
|
|
//
|
|
// class YourClass : private RefCountedGlobal<YourClass, YourParameterType>
|
|
// {
|
|
// //
|
|
// // Declare Singleton and RefCountedGlobal as friends
|
|
// // of YourClass so that htey can call your private
|
|
// // initialization functions.
|
|
// //
|
|
// // Note the added parameter type to the RefCountedGlobal
|
|
// // declaration.
|
|
// //
|
|
// friend class Singleton<YourClass>;
|
|
// friend class RefCountedGlobal<YourClass, YourParameterType>;
|
|
//
|
|
// //
|
|
// // Private declarations for YourClass
|
|
// //
|
|
// [...]
|
|
//
|
|
// //
|
|
// // Failable initialization function. This function
|
|
// // now takes a const ref to the initialization parameters.
|
|
// //
|
|
// BOOL FInit( const YourParameterType& initParam );
|
|
//
|
|
// public:
|
|
// //
|
|
// // Public declarations for YourClass
|
|
// //
|
|
// [...]
|
|
// };
|
|
//
|
|
// See \cal\src\httpext\entry.cpp for an example of this usage.
|
|
//
|
|
template<class _X, class _ParmType = _Empty>
|
|
class RefCountedGlobal : private Singleton<_X>
|
|
{
|
|
//
|
|
// The object's reference count.
|
|
//
|
|
static LONG sm_lcRef;
|
|
|
|
//
|
|
// Set of states which describe the object's state
|
|
// of initialization. The object's state is
|
|
// STATE_UNKNOWN while it is being initialized or
|
|
// deinitialized.
|
|
//
|
|
enum
|
|
{
|
|
STATE_UNINITIALIZED,
|
|
STATE_INITIALIZED,
|
|
STATE_UNKNOWN
|
|
};
|
|
|
|
static LONG sm_lState;
|
|
|
|
//
|
|
// Member template that generates an appropriately-typed,
|
|
// (inline) function that calls _X::FInit with initialization
|
|
// parameters.
|
|
//
|
|
template<class _P> static BOOL
|
|
FInit( const _P& parms ) { return Instance().FInit( parms ); }
|
|
|
|
//
|
|
// Specialization of the above member template for
|
|
// the _Empty parameter type, which calls _X::FInit
|
|
// without initialization parameters.
|
|
//
|
|
static BOOL FInit( const _Empty& ) { return Instance().FInit(); }
|
|
|
|
protected:
|
|
//
|
|
// Expose access to the single instance of class _X
|
|
//
|
|
using Singleton<_X>::Instance;
|
|
|
|
//
|
|
// Expose operator new and operator delete from
|
|
// the Singleton template so that they will be
|
|
// used rather than the default new and delete
|
|
// to "allocate" space for the instance of class _X.
|
|
//
|
|
using Singleton<_X>::operator new;
|
|
using Singleton<_X>::operator delete;
|
|
|
|
static BOOL FInitialized()
|
|
{
|
|
return sm_lState == STATE_INITIALIZED;
|
|
}
|
|
|
|
static LONG CRef()
|
|
{
|
|
return sm_lcRef;
|
|
}
|
|
|
|
public:
|
|
static DWORD DwInitRef( const _ParmType& parms )
|
|
{
|
|
LONG lcRef;
|
|
|
|
//
|
|
// Assert the invariant condition that we never have a
|
|
// reference without the state being initialized.
|
|
//
|
|
Assert( sm_lState != STATE_INITIALIZED || sm_lcRef >= 1 );
|
|
|
|
//
|
|
// Add the reference for the instance we're about
|
|
// to initialize. Doing this now simplifies the
|
|
// code below at the expense of having to decrement
|
|
// if first time initialization (FInit()) fails.
|
|
// The only thing critical to the design is that
|
|
// at any point, when sm_lState is STATE_INITIALIZED,
|
|
// sm_lcRef is at least 1.
|
|
//
|
|
lcRef = InterlockedIncrement( &sm_lcRef );
|
|
Assert( lcRef > 0 );
|
|
|
|
//
|
|
// Don't proceed until the object is initialized.
|
|
//
|
|
while ( sm_lState != STATE_INITIALIZED )
|
|
{
|
|
//
|
|
// Simultaneously check whether initialization has
|
|
// started and, if it has not, start it.
|
|
//
|
|
LONG lStatePrev = InterlockedCompareExchange(
|
|
&sm_lState,
|
|
STATE_UNKNOWN,
|
|
STATE_UNINITIALIZED );
|
|
|
|
//
|
|
// If we're starting first time initialization,
|
|
// then create and initialize the sole instance.
|
|
//
|
|
if ( lStatePrev == STATE_UNINITIALIZED )
|
|
{
|
|
CreateInstance();
|
|
|
|
// This calls our private member template FInit()
|
|
// (declared above), which in turn calls _X::Finit()
|
|
// with the appropriate parameters.
|
|
//
|
|
if ( FInit( parms ) )
|
|
{
|
|
sm_lState = STATE_INITIALIZED;
|
|
break;
|
|
}
|
|
|
|
// We failed to init.
|
|
// Tear down now.
|
|
//
|
|
|
|
Assert( lcRef == 1 );
|
|
Assert( sm_lState == STATE_UNKNOWN );
|
|
|
|
// Let go of our ref on the object.
|
|
// Destroy the object.
|
|
// And LAST, set the state to UNINITIALIZED.
|
|
// NOTE: This will let the next caller through the
|
|
// InterlockedCompare above.
|
|
//
|
|
InterlockedDecrement( &sm_lcRef );
|
|
DestroyInstance();
|
|
sm_lState = STATE_UNINITIALIZED;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// If first time initialization is in progress on
|
|
// another thread, then get out of the way so
|
|
// it can finish.
|
|
//
|
|
//$OPT We should probably spin rather than Sleep()
|
|
//$OPT on multi-proc machines on the assumption that
|
|
//$OPT we only get here on a processor other than
|
|
//$OPT the one which is doing the initialization
|
|
//$OPT and we don't want to invite a task switch
|
|
//$OPT by calling Sleep() while we are waiting
|
|
//$OPT for initialization to complete.
|
|
//
|
|
if ( lStatePrev == STATE_UNKNOWN )
|
|
Sleep(0);
|
|
}
|
|
|
|
//
|
|
// At this point, there must be at least
|
|
// one initialized reference.
|
|
//
|
|
Assert( sm_lState == STATE_INITIALIZED );
|
|
Assert( sm_lcRef > 0 );
|
|
|
|
return static_cast<DWORD>(lcRef);
|
|
}
|
|
|
|
static VOID DeinitRef()
|
|
{
|
|
//
|
|
// At this point, there must be at least
|
|
// one initialized reference.
|
|
//
|
|
Assert( sm_lState == STATE_INITIALIZED );
|
|
Assert( sm_lcRef > 0 );
|
|
|
|
//
|
|
// Remove that reference. If it is the last
|
|
// then deinit the object.
|
|
//
|
|
if ( 0 == InterlockedDecrement( &sm_lcRef ) )
|
|
{
|
|
//
|
|
// After releasing the last reference, declare that
|
|
// the object is in an unknown state. This prevents
|
|
// other threads trying to re-initialize the object
|
|
// from proceeding until we're done.
|
|
//
|
|
sm_lState = STATE_UNKNOWN;
|
|
|
|
//
|
|
// There is a tiny window between decrementing the
|
|
// refcount and changing the state where another
|
|
// initialization could have come through. Test this
|
|
// by rechecking the refcount.
|
|
//
|
|
if ( 0 == sm_lcRef )
|
|
{
|
|
//
|
|
// If the refcount is still zero, then no
|
|
// initializations happened before we changed
|
|
// states. At this point, if an initialization
|
|
// starts, it will block until we change state,
|
|
// so it is safe to actually destroy the instance.
|
|
//
|
|
DestroyInstance();
|
|
|
|
//
|
|
// Once the object has been deinitialized, update
|
|
// the state information. This unblocks any
|
|
// initializations waiting to happen.
|
|
//
|
|
sm_lState = STATE_UNINITIALIZED;
|
|
}
|
|
else // refcount is now non-zero
|
|
{
|
|
//
|
|
// If the refcount is no longer zero, then an
|
|
// initialization happened between decrementing
|
|
// the refcount above and entering the unknown
|
|
// state. When that happens, DO NOT deinit --
|
|
// there is now another valid reference somewhere.
|
|
// Instead, just restore the object's state to let
|
|
// other references proceed.
|
|
//
|
|
sm_lState = STATE_INITIALIZED;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Assert the invariant condition that we never have a
|
|
// reference without the state being initialized.
|
|
//
|
|
Assert( sm_lState != STATE_INITIALIZED || sm_lcRef >= 1 );
|
|
}
|
|
|
|
// This provides a no-parameter version of DwInitRef
|
|
// for clients that do not need any parameters in FInit().
|
|
//
|
|
static DWORD DwInitRef()
|
|
{
|
|
_Empty e;
|
|
|
|
return DwInitRef( e );
|
|
}
|
|
};
|
|
|
|
template<class _X, class _ParmType>
|
|
LONG RefCountedGlobal<_X, _ParmType>::sm_lcRef = 0;
|
|
|
|
template<class _X, class _ParmType>
|
|
LONG RefCountedGlobal<_X, _ParmType>::sm_lState = STATE_UNINITIALIZED;
|
|
|
|
|
|
// ========================================================================
|
|
// SHARED MEMORY HANDLES
|
|
//
|
|
// They are always defined as 32 bit values
|
|
//
|
|
#ifdef _WIN64
|
|
typedef VOID * __ptr32 SHMEMHANDLE;
|
|
#else
|
|
typedef HANDLE SHMEMHANDLE;
|
|
#endif
|
|
|
|
// ========================================================================
|
|
//
|
|
// NAMESPACE SMH
|
|
//
|
|
// Differentiates the shared memory heap allocation functions
|
|
// from all of the other various heap allocation functions
|
|
// in existence.
|
|
//
|
|
// Yes, this could have been done with a prefix just as easily....
|
|
//
|
|
namespace SMH
|
|
{
|
|
BOOL __fastcall FInitialize( IN LPCWSTR pwszInstanceName );
|
|
VOID __fastcall Deinitialize();
|
|
PVOID __fastcall PvAlloc( IN DWORD cbData, OUT SHMEMHANDLE * phsmba );
|
|
VOID __fastcall Free( IN SHMEMHANDLE hsmba );
|
|
PVOID __fastcall PvFromSMBA( IN SHMEMHANDLE hsmba );
|
|
};
|
|
|
|
|
|
// ========================================================================
|
|
//
|
|
// CLASS CSmhInit
|
|
//
|
|
// Initializer for shared memory heap. Any class containing member objects
|
|
// that reside on the shared memory heap should also include an instance
|
|
// of this class as a member BEFORE the others to ensure that they are
|
|
// properly destroyed before the heap is deinitialized.
|
|
//
|
|
class CSmhInit
|
|
{
|
|
// NOT IMPLEMENTED
|
|
//
|
|
CSmhInit& operator=( const CSmhInit& );
|
|
CSmhInit( const CSmhInit& );
|
|
|
|
public:
|
|
CSmhInit()
|
|
{
|
|
}
|
|
|
|
BOOL FInitialize( LPCWSTR lpwszSignature )
|
|
{
|
|
#ifdef COMPILE_WITHOUT_DAV
|
|
return FALSE;
|
|
#else
|
|
return SMH::FInitialize( lpwszSignature );
|
|
#endif
|
|
}
|
|
|
|
~CSmhInit()
|
|
{
|
|
#ifdef COMPILE_WITHOUT_DAV
|
|
// do nothing
|
|
#else
|
|
SMH::Deinitialize();
|
|
#endif
|
|
}
|
|
};
|
|
|
|
|
|
// ========================================================================
|
|
//
|
|
// TEMPLATE CLASS auto_ptr
|
|
//
|
|
// Stripped down auto_ptr class based on the C++ STL standard one
|
|
//
|
|
template<class X>
|
|
class auto_ptr
|
|
{
|
|
mutable X * owner;
|
|
X * px;
|
|
|
|
public:
|
|
explicit auto_ptr(X* p=0) : owner(p), px(p) {}
|
|
auto_ptr(const auto_ptr<X>& r) : owner(r.owner), px(r.relinquish()) {}
|
|
|
|
auto_ptr& operator=(const auto_ptr<X>& r)
|
|
{
|
|
if ((void*)&r != (void*)this)
|
|
{
|
|
delete owner;
|
|
owner = r.owner;
|
|
px = r.relinquish();
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
// NOTE: This equals operator is meant to be used to load a
|
|
// new pointer (not yet held in any auto-ptr anywhere) into this object.
|
|
auto_ptr& operator=(X* p)
|
|
{
|
|
Assert(!owner); // Scream on overwrite of good data.
|
|
owner = p;
|
|
px = p;
|
|
return *this;
|
|
}
|
|
|
|
~auto_ptr() { delete owner; }
|
|
bool operator!()const { return (px == NULL); }
|
|
operator X*() const { return px; }
|
|
X& operator*() const { return *px; }
|
|
X* operator->() const { return px; }
|
|
X* get() const { return px; }
|
|
X* relinquish() const { owner = 0; return px; }
|
|
|
|
void clear()
|
|
{
|
|
if (owner)
|
|
delete owner;
|
|
owner = 0;
|
|
px = 0;
|
|
}
|
|
};
|
|
|
|
|
|
//
|
|
// From here down implements the performance monitor for HTTPEXT and
|
|
// the functionality necessary to merge perf counters from HTTPEXT
|
|
// into the Web Service object defined by W3CTRS.
|
|
//
|
|
|
|
//
|
|
// The first flag says whether the CW3Monitor instance has been created.
|
|
// The second flag says whether it has been initialized.
|
|
// Since the CW3Monitor is created on-demand by the first call to
|
|
// W3MergeDavPerformanceData(), these flags keep us from continually
|
|
// retrying initialization after it fails the first time.
|
|
//
|
|
BOOL g_fCreatedMonitor = FALSE;
|
|
BOOL g_fInitializedMonitor = FALSE;
|
|
|
|
|
|
// ========================================================================
|
|
//
|
|
// CLASS CW3Monitor
|
|
//
|
|
class CW3Monitor : private Singleton<CW3Monitor>
|
|
{
|
|
//
|
|
// Friend declaration required by Singleton template
|
|
//
|
|
friend class Singleton<CW3Monitor>;
|
|
|
|
//
|
|
// Hint about the size of the buffer to use for perf data coming back
|
|
// from HTTPEXT. The buffer needs to be about 48K per vroot. The
|
|
// initial guess is 48K (one vroot), and scales up geometrically as
|
|
// necessary.
|
|
//
|
|
DWORD m_cbDavDataAlloc;
|
|
|
|
//
|
|
// Shared memory heap initialization. Declare before any member
|
|
// variables which use the shared memory heap to ensure proper
|
|
// order of destruction.
|
|
//
|
|
CSmhInit m_smh;
|
|
|
|
//
|
|
// Perf counter data
|
|
//
|
|
auto_ptr<ICounterData> m_pCounterData;
|
|
|
|
// CREATORS
|
|
//
|
|
CW3Monitor() :
|
|
m_cbDavDataAlloc(48 * 1024)
|
|
{
|
|
}
|
|
BOOL FInitialize();
|
|
|
|
// ACCESSORS
|
|
//
|
|
DWORD DwCollectPerformanceData( LPBYTE lpbPerfData,
|
|
LPDWORD lpdwcbPerfData )
|
|
{
|
|
DWORD dwcObjectTypes;
|
|
|
|
return m_pCounterData->DwCollectData( NULL, // Always query all data
|
|
0, // Use 0-relative indices
|
|
reinterpret_cast<LPVOID *>(&lpbPerfData),
|
|
lpdwcbPerfData,
|
|
&dwcObjectTypes );
|
|
}
|
|
|
|
// NOT IMPLEMENTED
|
|
//
|
|
CW3Monitor& operator=( const CW3Monitor& );
|
|
CW3Monitor( const CW3Monitor& );
|
|
|
|
public:
|
|
static BOOL W3MergeDavPerformanceData( DWORD dwServerId, W3_STATISTICS_1 * pW3Stats );
|
|
static VOID W3CloseDavPerformanceData()
|
|
{
|
|
DestroyInstance();
|
|
g_fCreatedMonitor = FALSE;
|
|
}
|
|
};
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CW3Monitor::FInitialize()
|
|
//
|
|
BOOL
|
|
CW3Monitor::FInitialize()
|
|
{
|
|
|
|
//
|
|
// Initialize the shared memory heap
|
|
//
|
|
if ( !m_smh.FInitialize( gc_wszSignature ) )
|
|
{
|
|
DWORD dwResult = GetLastError();
|
|
|
|
DebugTrace( "CW3Monitor::FInitialize() - Error initializing shared memory heap %d\n", dwResult );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Bind to the counter data
|
|
//
|
|
#ifdef COMPILE_WITHOUT_DAV
|
|
|
|
#else
|
|
|
|
m_pCounterData = NewCounterMonitor( gc_wszSignature );
|
|
|
|
if ( !m_pCounterData.get() )
|
|
{
|
|
|
|
DWORD dwResult = GetLastError();
|
|
|
|
DebugTrace( "CW3Monitor::FInitialize() - Error binding to counter data %d\n", dwResult );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// CW3Monitor::W3MergeDavPerformanceData()
|
|
//
|
|
BOOL
|
|
CW3Monitor::W3MergeDavPerformanceData( DWORD dwServerId, W3_STATISTICS_1 * pW3Stats )
|
|
{
|
|
|
|
BYTE * lpbDavData = NULL;
|
|
PERF_OBJECT_TYPE * ppot = NULL;
|
|
PERF_COUNTER_DEFINITION * rgpcd = NULL;
|
|
DWORD dwibServerId = 0;
|
|
PERF_INSTANCE_DEFINITION * ppid = NULL;
|
|
PERF_COUNTER_BLOCK * ppcb;
|
|
LONG lcInstances;
|
|
DWORD iCtr;
|
|
BOOL fRet = FALSE;
|
|
|
|
//
|
|
// Create the monitor on demand
|
|
//
|
|
|
|
if ( !g_fCreatedMonitor )
|
|
{
|
|
g_fInitializedMonitor = CreateInstance().FInitialize();
|
|
g_fCreatedMonitor = TRUE;
|
|
}
|
|
|
|
//
|
|
// If we failed to init the monitor, then we can't get any
|
|
// performance data. Since we can't fail this call, just leave
|
|
// the existing statistics alone.
|
|
//
|
|
if ( !g_fInitializedMonitor )
|
|
goto ret;
|
|
|
|
|
|
//
|
|
// From here out we always return TRUE
|
|
//
|
|
fRet = TRUE;
|
|
|
|
//
|
|
// Try collecting the data. If our buffer is too small, double
|
|
// its size repeatedly until it's big enough. The initial guess
|
|
// should be large enough to succeed most of the time.
|
|
//
|
|
for ( ;; )
|
|
{
|
|
DWORD cbDavDataAlloc;
|
|
DWORD cbDavData;
|
|
DWORD dwCollectResult;
|
|
|
|
// If we have a buffer from a previous trip around the loop
|
|
// then free it.
|
|
//
|
|
if (lpbDavData)
|
|
delete [] lpbDavData;
|
|
|
|
cbDavDataAlloc = Instance().m_cbDavDataAlloc;
|
|
cbDavData = cbDavDataAlloc;
|
|
lpbDavData = new BYTE[cbDavData];
|
|
if (NULL == lpbDavData)
|
|
{
|
|
DebugTrace( "W3MergeDavPerformanceData() - OOM allocating buffer for DAV perf data\n" );
|
|
goto ret;
|
|
}
|
|
|
|
dwCollectResult = Instance().
|
|
DwCollectPerformanceData( lpbDavData,
|
|
&cbDavData );
|
|
|
|
if ( ERROR_SUCCESS == dwCollectResult )
|
|
{
|
|
if ( 0 != cbDavData )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DebugTrace( "W3MergeDavPerformanceData() - No instances from which to collect data\n" );
|
|
goto ret;
|
|
}
|
|
}
|
|
|
|
if ( ERROR_MORE_DATA != dwCollectResult )
|
|
{
|
|
DebugTrace( "W3MergeDavPerformanceData() - Error collecting DAV perf data (%d)\n", dwCollectResult );
|
|
goto ret;
|
|
}
|
|
|
|
Instance().m_cbDavDataAlloc = 2 * cbDavDataAlloc;
|
|
}
|
|
|
|
//
|
|
// Locate the HTTPEXT perf object (should be the first
|
|
// thing returned) and make sure it looks reasonable.
|
|
//
|
|
ppot = reinterpret_cast<PERF_OBJECT_TYPE *>( lpbDavData );
|
|
|
|
if ( ppot->NumCounters < 1 )
|
|
{
|
|
DebugTrace( "W3MergeDavPerformanceData() - NumCounters < 1?" );
|
|
goto ret;
|
|
}
|
|
|
|
if ( ppot->NumInstances < 1 )
|
|
{
|
|
DebugTrace( "W3MergeDavPerformanceData() - NumInstances < 1?" );
|
|
goto ret;
|
|
}
|
|
|
|
//
|
|
// Locate the HTTPEXT counter definitions.
|
|
//
|
|
rgpcd = reinterpret_cast<PERF_COUNTER_DEFINITION *>(
|
|
reinterpret_cast<LPBYTE>(ppot) + ppot->HeaderLength );
|
|
|
|
//
|
|
// Locate the server ID counter. This counter tells us what
|
|
// instance of the W3Ctrs data to merge into, so bail if we can't
|
|
// find it or if it looks wrong.
|
|
//
|
|
for ( iCtr = 0; iCtr < ppot->NumCounters; iCtr++ )
|
|
{
|
|
if ( rgpcd[iCtr].CounterNameTitleIndex == HTTPEXT_COUNTER_SERVER_ID )
|
|
{
|
|
if ( rgpcd[iCtr].CounterSize == sizeof(DWORD) &&
|
|
rgpcd[iCtr].CounterType == (PERF_DISPLAY_NOSHOW |
|
|
PERF_SIZE_DWORD |
|
|
PERF_TYPE_NUMBER) )
|
|
{
|
|
dwibServerId = rgpcd[iCtr].CounterOffset;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DebugTrace( "W3MergePerformanceData() - Server ID counter doesn't look right. Bailing...\n" );
|
|
goto ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !dwibServerId )
|
|
{
|
|
DebugTrace( "W3MergePerformanceData() - Couldn't find server ID counter. Bailing...\n" );
|
|
goto ret;
|
|
}
|
|
|
|
//
|
|
// Now go through each instance of the HTTPEXT counters and merge the data
|
|
// from that instance into the correct W3Ctrs counter instance according
|
|
// to server ID.
|
|
//
|
|
ppid = reinterpret_cast<PERF_INSTANCE_DEFINITION *>(
|
|
reinterpret_cast<LPBYTE>(ppot) + ppot->DefinitionLength );
|
|
|
|
for ( lcInstances = ppot->NumInstances;
|
|
lcInstances-- > 0;
|
|
ppid = reinterpret_cast<PERF_INSTANCE_DEFINITION *>(
|
|
reinterpret_cast<LPBYTE>(ppcb) + ppcb->ByteLength ) )
|
|
{
|
|
ppcb = reinterpret_cast<PERF_COUNTER_BLOCK *>(
|
|
reinterpret_cast<LPBYTE>(ppid) + ppid->ByteLength );
|
|
|
|
//
|
|
// Get the server ID of this instance.
|
|
//
|
|
DWORD dwServerIdInstance =
|
|
*reinterpret_cast<LPDWORD>(
|
|
reinterpret_cast<LPBYTE>(ppcb) + dwibServerId);
|
|
|
|
//
|
|
// If the server ID of this instance isn't the one
|
|
// we want then keep looking.
|
|
//
|
|
if ( dwServerIdInstance != dwServerId )
|
|
continue;
|
|
|
|
//
|
|
// The server IDs match, so sum in the info for this instance.
|
|
// NOTE: Since each vroot has its own instance, there may be
|
|
// multiple instances with the same server ID. Sum them all.
|
|
//
|
|
// Go through all the counters in the HTTPEXT instance
|
|
// and update the W3 statistics data.
|
|
//
|
|
for ( DWORD iCounter = 0;
|
|
iCounter < ppot->NumCounters;
|
|
iCounter++ )
|
|
{
|
|
PERF_COUNTER_DEFINITION * ppcd = &rgpcd[iCounter];
|
|
|
|
switch ( ppcd->CounterNameTitleIndex )
|
|
{
|
|
case HTTPEXT_COUNTER_TOTAL_GET_REQUESTS:
|
|
case HTTPEXT_COUNTER_TOTAL_HEAD_REQUESTS:
|
|
case HTTPEXT_COUNTER_TOTAL_POST_REQUESTS:
|
|
case HTTPEXT_COUNTER_TOTAL_DELETE_REQUESTS:
|
|
case HTTPEXT_COUNTER_TOTAL_OTHER_REQUESTS:
|
|
{
|
|
//
|
|
// These values are already recorded
|
|
// in the statistics, so don't do anything
|
|
// more with them here.
|
|
//
|
|
break;
|
|
}
|
|
|
|
case HTTPEXT_COUNTER_TOTAL_PUT_REQUESTS:
|
|
{
|
|
if ( ppcd->CounterType == PERF_COUNTER_RAWCOUNT &&
|
|
ppcd->CounterSize == sizeof(DWORD) )
|
|
{
|
|
DWORD dwValue = *reinterpret_cast<LPDWORD>(
|
|
reinterpret_cast<LPBYTE>(ppcb) + ppcd->CounterOffset );
|
|
|
|
pW3Stats->TotalFilesReceived += dwValue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case HTTPEXT_COUNTER_TOTAL_OPTIONS_REQUESTS:
|
|
{
|
|
if ( ppcd->CounterType == PERF_COUNTER_RAWCOUNT &&
|
|
ppcd->CounterSize == sizeof(DWORD) )
|
|
{
|
|
DWORD dwValue = *reinterpret_cast<LPDWORD>(
|
|
reinterpret_cast<LPBYTE>(ppcb) + ppcd->CounterOffset );
|
|
|
|
pW3Stats->TotalOptions += dwValue;
|
|
pW3Stats->TotalOthers -= dwValue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case HTTPEXT_COUNTER_TOTAL_COPY_REQUESTS:
|
|
{
|
|
if ( ppcd->CounterType == PERF_COUNTER_RAWCOUNT &&
|
|
ppcd->CounterSize == sizeof(DWORD) )
|
|
{
|
|
DWORD dwValue = *reinterpret_cast<LPDWORD>(
|
|
reinterpret_cast<LPBYTE>(ppcb) + ppcd->CounterOffset );
|
|
|
|
pW3Stats->TotalCopy += dwValue;
|
|
pW3Stats->TotalOthers -= dwValue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case HTTPEXT_COUNTER_TOTAL_MOVE_REQUESTS:
|
|
{
|
|
if ( ppcd->CounterType == PERF_COUNTER_RAWCOUNT &&
|
|
ppcd->CounterSize == sizeof(DWORD) )
|
|
{
|
|
DWORD dwValue = *reinterpret_cast<LPDWORD>(
|
|
reinterpret_cast<LPBYTE>(ppcb) + ppcd->CounterOffset );
|
|
|
|
pW3Stats->TotalMove += dwValue;
|
|
pW3Stats->TotalOthers -= dwValue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case HTTPEXT_COUNTER_TOTAL_MKCOL_REQUESTS:
|
|
{
|
|
if ( ppcd->CounterType == PERF_COUNTER_RAWCOUNT &&
|
|
ppcd->CounterSize == sizeof(DWORD) )
|
|
{
|
|
DWORD dwValue = *reinterpret_cast<LPDWORD>(
|
|
reinterpret_cast<LPBYTE>(ppcb) + ppcd->CounterOffset );
|
|
|
|
pW3Stats->TotalMkcol += dwValue;
|
|
pW3Stats->TotalOthers -= dwValue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case HTTPEXT_COUNTER_TOTAL_PROPFIND_REQUESTS:
|
|
{
|
|
if ( ppcd->CounterType == PERF_COUNTER_RAWCOUNT &&
|
|
ppcd->CounterSize == sizeof(DWORD) )
|
|
{
|
|
DWORD dwValue = *reinterpret_cast<LPDWORD>(
|
|
reinterpret_cast<LPBYTE>(ppcb) + ppcd->CounterOffset );
|
|
|
|
pW3Stats->TotalPropfind += dwValue;
|
|
pW3Stats->TotalOthers -= dwValue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case HTTPEXT_COUNTER_TOTAL_PROPPATCH_REQUESTS:
|
|
{
|
|
if ( ppcd->CounterType == PERF_COUNTER_RAWCOUNT &&
|
|
ppcd->CounterSize == sizeof(DWORD) )
|
|
{
|
|
DWORD dwValue = *reinterpret_cast<LPDWORD>(
|
|
reinterpret_cast<LPBYTE>(ppcb) + ppcd->CounterOffset );
|
|
|
|
pW3Stats->TotalProppatch += dwValue;
|
|
pW3Stats->TotalOthers -= dwValue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case HTTPEXT_COUNTER_TOTAL_SEARCH_REQUESTS:
|
|
{
|
|
if ( ppcd->CounterType == PERF_COUNTER_RAWCOUNT &&
|
|
ppcd->CounterSize == sizeof(DWORD) )
|
|
{
|
|
DWORD dwValue = *reinterpret_cast<LPDWORD>(
|
|
reinterpret_cast<LPBYTE>(ppcb) + ppcd->CounterOffset );
|
|
|
|
pW3Stats->TotalSearch += dwValue;
|
|
pW3Stats->TotalOthers -= dwValue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case HTTPEXT_COUNTER_TOTAL_LOCK_REQUESTS:
|
|
{
|
|
if ( ppcd->CounterType == PERF_COUNTER_RAWCOUNT &&
|
|
ppcd->CounterSize == sizeof(DWORD) )
|
|
{
|
|
DWORD dwValue = *reinterpret_cast<LPDWORD>(
|
|
reinterpret_cast<LPBYTE>(ppcb) + ppcd->CounterOffset );
|
|
|
|
pW3Stats->TotalLock += dwValue;
|
|
pW3Stats->TotalOthers -= dwValue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case HTTPEXT_COUNTER_TOTAL_UNLOCK_REQUESTS:
|
|
{
|
|
if ( ppcd->CounterType == PERF_COUNTER_RAWCOUNT &&
|
|
ppcd->CounterSize == sizeof(DWORD) )
|
|
{
|
|
DWORD dwValue = *reinterpret_cast<LPDWORD>(
|
|
reinterpret_cast<LPBYTE>(ppcb) + ppcd->CounterOffset );
|
|
|
|
pW3Stats->TotalUnlock += dwValue;
|
|
pW3Stats->TotalOthers -= dwValue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case HTTPEXT_COUNTER_TOTAL_404_RESPONSES:
|
|
{
|
|
if ( ppcd->CounterType == PERF_COUNTER_RAWCOUNT &&
|
|
ppcd->CounterSize == sizeof(DWORD) )
|
|
{
|
|
DWORD dwValue = *reinterpret_cast<LPDWORD>(
|
|
reinterpret_cast<LPBYTE>(ppcb) + ppcd->CounterOffset );
|
|
|
|
pW3Stats->TotalNotFoundErrors += dwValue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case HTTPEXT_COUNTER_TOTAL_423_RESPONSES:
|
|
{
|
|
if ( ppcd->CounterType == PERF_COUNTER_RAWCOUNT &&
|
|
ppcd->CounterSize == sizeof(DWORD) )
|
|
{
|
|
DWORD dwValue = *reinterpret_cast<LPDWORD>(
|
|
reinterpret_cast<LPBYTE>(ppcb) + ppcd->CounterOffset );
|
|
|
|
pW3Stats->TotalLockedErrors += dwValue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
//
|
|
// We either don't care about or don't understand
|
|
// other counters, so skip them.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ret:
|
|
if (lpbDavData)
|
|
delete [] lpbDavData;
|
|
|
|
return fRet;
|
|
|
|
}
|
|
|
|
|
|
// ========================================================================
|
|
//
|
|
// CLASS CW3MonitorInit
|
|
//
|
|
// Refcounted global initialization class that controls the lifetime of
|
|
// the singleton CW3Monitor instance. Calls to collect performance data
|
|
// are done in the scope of a reference to this class so that the server
|
|
// can be taken down on one thread while another thread is in the middle
|
|
// of -- or just beginning to -- collect data. Whichever thread releases
|
|
// the last reference to this class will be the one to close the counters.
|
|
//
|
|
class CW3MonitorInit : private RefCountedGlobal<CW3MonitorInit>
|
|
{
|
|
//
|
|
// Friend declarations required by RefCountedGlobal template
|
|
//
|
|
friend class Singleton<CW3MonitorInit>;
|
|
friend class RefCountedGlobal<CW3MonitorInit>;
|
|
|
|
// CREATORS
|
|
//
|
|
// Declared private to ensure that arbitrary instances
|
|
// of this class cannot be created. The Singleton
|
|
// template (declared as a friend above) controls
|
|
// the sole instance of this class.
|
|
//
|
|
CW3MonitorInit()
|
|
{
|
|
}
|
|
|
|
BOOL FInit()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
~CW3MonitorInit()
|
|
{
|
|
CW3Monitor::W3CloseDavPerformanceData();
|
|
}
|
|
|
|
// NOT IMPLEMENTED
|
|
//
|
|
CW3MonitorInit( const CW3MonitorInit& );
|
|
CW3MonitorInit& operator=( const CW3MonitorInit& );
|
|
|
|
public:
|
|
using RefCountedGlobal<CW3MonitorInit>::DwInitRef;
|
|
using RefCountedGlobal<CW3MonitorInit>::DeinitRef;
|
|
};
|
|
|
|
|
|
// ========================================================================
|
|
//
|
|
// PUBLIC INTERFACE
|
|
//
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// W3MergeDavPerformanceData()
|
|
//
|
|
EXTERN_C BOOL __fastcall
|
|
W3MergeDavPerformanceData( DWORD dwServerId, W3_STATISTICS_1 * pW3Stats )
|
|
{
|
|
BOOL fResult;
|
|
|
|
CW3MonitorInit::DwInitRef();
|
|
|
|
fResult = CW3Monitor::W3MergeDavPerformanceData( dwServerId, pW3Stats );
|
|
|
|
CW3MonitorInit::DeinitRef();
|
|
|
|
return fResult;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// W3OpenDavPerformanceData()
|
|
//
|
|
EXTERN_C VOID __fastcall
|
|
W3OpenDavPerformanceData()
|
|
{
|
|
CW3MonitorInit::DwInitRef();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
//
|
|
// W3CloseDavPerformanceData()
|
|
//
|
|
EXTERN_C VOID __fastcall
|
|
W3CloseDavPerformanceData()
|
|
{
|
|
CW3MonitorInit::DeinitRef();
|
|
}
|