Understanding the Perfmon System


With Perfmon it s easy to start focusing on implementation details, which can lead you to quickly lose your understanding of what you re trying to accomplish. So now we ll just take a step back and look at this from a higher level.

You want to create a counter (or, more likely, a number of counters) to measure some arbitrary statistics for your application. Typically, you ll want to store your counter in your ISAPI DLL (because your ISAPI DLL handles every request and is common to all the application DLLs, it s the perfect place for storing global variables ), and you ll want to update your counter from your application DLL (your application DLL contains your application logic, therefore it contains the application-specific information you want to measure).

If you just wanted to read information from the global counter, then it could be this simple, but unfortunately you need to update it. Because you could be updating from one of any number of threads, you need to ensure that updating the counters is a thread-safe operation.

The Perfmon system is designed in a hierarchy that makes managing sets of counters as easy as possible from a user s point of view, but not necessarily from a developer s point of view. You can group one or more performance counters (which you can create with the ATL Performance Counter Wizard) into a performance category (which you can create with the ATL Performance Object Wizard). You can then manage one or more performance categories by using a Perfmon object manager.

Tip  

When Visual Studio .NET and Visual Studio .NET 2003 were designed, the Perfmon categories were referred to as objects, hence the fact that although we refer to Perfmon categories, the associated class names , wizards, and so forth refer to Perfmon objects. Categories is usually a better term to use and way to think about it, and the change from objects to categories may be made in a future version.

When you add performance monitoring to your own application, you ll start from the top (the Perfmon object manager) and work your way down to the counters you want to use.

Creating a Perfmon Counter

In this section you ll create your first counter and the infrastructure necessary to have a counter. You'll set up a simple Web page that updates Perfmon with the number of hits that the page has received. You could easily modify this example counter to track the number of orders on a e-commerce site, the number of downloads of a particular application, or the number of times users click banners on your site.

To start, simply create a new ATL Server project and select Predefined Perfmon Counters from the Server Options tab. Create a new header file to hold your Perfmon information. As you saw in the previous section, you need to start with a performance monitor object manager.

You can do this by using the wizard (right-click the project you want to add the file to in Solution Explorer or Class view, select Add Add Class, and then select ATL Performance Object Manager) or by writing the code by hand, either with or without attributes. For this example you ll first look at the code with attributes, and then you ll look at it without attributes.

Put this Perfmon code in a new header file in your ISAPI project. You then expose these counters to the application DLLs via a service from your ISAPI DLL. The performance object manager class looks like this:

 [perfmon(name="MyMonitor", register=true)]  class MyMonitor  {    public:  }; 

Now you need to create a performance object. Once again, there s a wizard to simplify this task (right-click the appropriate Perfmon object manager class in Class view and select Add Add Perfmon Object). Note that this code goes before the performance object manager class:

 [perf_object(namestring="MyObject",      helpstring="MyObject Helpstring",      detail=PERF_DETAIL_NOVICE)]  class MyObject  {    public:  }; 

Finally, add the counter itself. The wizard for doing so is again available from Class view (right-click the performance object class and select Add Add Perfmon Counter).

 [perf_object(namestring="MyObject",      helpstring="MyObject Helpstring",      detail=PERF_DETAIL_NOVICE)]  class MyObject  {    public:      [perf_counter(namestring="MyCounter",          helpstring="MyCounter Helpstring",          countertype=PERF_COUNTER_RAWCOUNT,          defscale=0, detail=PERF_DETAIL_NOVICE)]      ULONG Visits;  }; 

You can achieve the same results without using Visual C++ attributes. Again, start with the code for the ATL performance object manager:

 class MyMonitor : public CPerfMon  {    public:      BEGIN_PERF_MAP(_T("MyMonitor"))        CHAIN_PERF_OBJECT(MyObject)      END_PERF_MAP()  }; 

If you look at the attributed example, you ll notice that the manager has a "register=true" parameter. This sets registration for your performance object manager, and because you aren t using attributes, you need to do this manually. You add the following code:

 #define _ATL_PERF_REGISTER  PERFREG_ENTRY(MyMonitor); 

Now you can add the Perfmon object:

 class MyObject : public CPerfObject  {    public:      DECLARE_PERF_OBJECT(MyObject, 1, "MyObject", "MyObject Helpstring", -1);  }; 

And then you finish off by adding the counter. You ll notice that attributes greatly simplify this step by creating code that s easy to read and maintain:

 class MyObject : public CPerfObject  {    public:      DECLARE_PERF_OBJECT(MyObject, 1, "MyObject", "MyObject Helpstring", -1);      BEGIN_COUNTER_MAP(MyObject)        DEFINE_COUNTER(Visits, "MyCounter",                   MyCounter Helpstring",                    PERF_COUNTER_RAWCOUNT,                    0)      END_COUNTER_MAP();      ULONG Visits;  }; 

To get your Perfmon counter to actually work, you have to instantiate your object manager and the Perfmon object itself. Open the main cpp file in your ISAPI project. You should already have the header file in which you defined the Perfmon object manager, object, and counter #included . Now create two global objects: one for the object manager and one as a pointer to the object itself:

 MyMonitor g_Monitor;  MyObject *g_pObject; 

Next , go to GetExtensionVersion to initialize Perfmon as follows :

 g_Monitor.Initialize();  g_Monitor.CreateInstanceByName(L"My Monitor", &g_pObject); 

Now build and deploy your code. You should run regsvr32 on your deployed ISAPI DLL at the command line (make sure to register the deployed ISAPI in your vroot, not the version under your project). Launch your browser and click the wizard-generated SRF file to actually load your application. You can now launch Perfmon from the command line or via the Start menu, and then click the plus sign (+) to add a counter. From the Performance Object drop-down list, select your performance object (e.g., My Object). From the Select Counters From list, you can choose your counter (e.g., My Counter). You could also change your performance object manager from the Instance selection.

You ll notice, however, that you currently have no way to access the performance counter from your application DLL. In the next section, you ll expose this counter to your application DLLs via a service. The only potential downfall with this scheme is if you expect to change your monitor code regularly, as changes to your monitor code will mean changing your ISAPI DLL, which in turn requires you to reset IIS. You can avoid this issue by exposing your perfmon counter as a windows service as seen in the PerfPersist sample shipped with Visual Studio.

Exposing a Perfmon Counter As an ISAPI Service

You ll continue learning about Perfmon throughout this chapter as you expose it via an ISAPI service. We recommend that you read Chapter 4 before continuing through this chapter.

Let s begin by taking the same simple counter application you created earlier and modifying it so that your Perfmon object is now exposed via an ISAPI service instead of from your ISAPI DLL.

To begin, you ll get rid of the global monitor and object that you created previously, as well as the code you added to GetExtensionVersion . You can also remove the #include for the header file, which contains your Perfmon class information. In this section we assume that you generated your ATL Server application from the wizard without choosing to expose any services from your ISAPI DLL (this is the default setting on the Server Options tab). If you did decide to expose some services from your ISAPI DLL, then don t worry, some of the code we present in this section will already have been generated for you.

Let s start in the .cpp file. By default, the wizard has generated a typedef for CIsapiExtension , which should look something like this:

 typedef CIsapiExtension<> ExtensionType; 

To expose your own services, you need to modify this code so that you provide your own IsapiExtension and a custom thread pool (so that you can initialize and expose all your services). To do this, modify the preceding code to look like the following:

 typedef MyCIsapiExtension<> ExtensionType; 

You should now create a header file where you define what MyCIsapiExtension actually is. Make sure to #include this header file in your .cpp file. You begin in the new header file by including <atlisapi.h>, which provides the base functionality off of which you ll create your application, as well as the header file in which you defined your Perfmon classes. You can now create your MyCIsapiExtension class derived from CIsapiExtension . Listing 9-1 presents the code for this class.

Listing 9.1: Custom ISAPI Extension Class
start example
 template <class ThreadPoolClass=CThreadPool<CIsapiWorker>,    class CStatClass=CNoRequestStats,    class HttpUserErrorTextProvider=CDefaultErrorProvider,    class WorkerThreadTraits=DefaultThreadTraits >  class MyCIsapiExtension :    public CIsapiExtension<ThreadPoolClass,      CStatClass,      HttpUserErrorTextProvider,      WorkerThreadTraits>  {  protected:    typedef CIsapiExtension<ThreadPoolClass, CStatClass, HttpUserErrorTextProvider,        WorkerThreadTraits> baseISAPI;    typedef CWorkerThread<WorkerThreadTraits> WorkerThreadClass;  public:    BOOL GetExtensionVersion(HSE_VERSION_INFO* pVer)    {      if (!baseISAPI::GetExtensionVersion(pVer))      {        return FALSE;      }      if (GetCriticalIsapiError() != 0)      {        return TRUE;      }      return TRUE;    }    BOOL TerminateExtension(DWORD dwFlags)    {      BOOL bRet = baseISAPI::TerminateExtension(dwFlags);      return bRet;    }    HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService,        REFIID riid, void** ppvObject)    {      return baseISAPI::QueryService(guidService, riid, ppvObject);    }    virtual void OnThreadTerminate(DWORD /*dwThreadId*/)    {    }  }; 
end example
 

Now you have a framework into which you can add your Perfmon objects that you ll expose via a service. Once you ve finished creating the service, you ll consume it from your application DLL and use it as a page hit counter.

To create an interface that describes your hit counter interface, create a class that implements your hit counter. The code for this looks something like Listing 9-2.

Listing 9.2: Interface for a Simple Counter
start example
 __interface ATL_NO_VTABLE __declspec(uuid("A3B11C9C-C3B2-41ba-AA01-F086F4625B49"))  IHitCounter : public IUnknown  {    HRESULT (IncrementCounter)();  };  class CHitCounter : public IHitCounter,       public CComObjectRootEx<CComGlobalsThreadModel>  {    BEGIN_COM_MAP(CHitCounter)      COM_INTERFACE_ENTRY(IHitCounter)    END_COM_MAP()    MyObject *pObject;    void InitCounter(MyObject *pIn)    {      pObject = pIn;      pObject->Visits = 0;    }    HRESULT IncrementCounter()    {      InterlockedIncrement(reinterpret_cast<LONG *> (&pObject->Visits));      return S_OK;    }  }; 
end example
 

You can now add in an instance of your Perfmon object manager and a pointer to an actual performance object to your class. You can also create an instance of CHitCounter and use it to initialize your counter, as shown in Listing 9-3 (remember to add this to your MyCIsapiExtension class). Adding this code means that you can remove your global object and monitor and also the initialization code from GetExtensionVersion .

Listing 9.3: Initializing the Counter
start example
 MyMonitor m_Monitor;  MyObject *m_pObject;  CComObjectGlobal<CHitCounter> m_Counter;  public:  BOOL GetExtensionVersion(HSE_VERSION_INFO* pVer)  {    HRESULT hr = m_Monitor.Initialize();    if (hr != S_OK)    {      return FALSE;    }      CPerfLock lock(&m_Monitor);      hr = lock.GetStatus();      if (FAILED(hr))    {      return FALSE;    }    hr = m_Monitor.CreateInstanceByName(1,       L"Hit Counter",       reinterpret_cast<CPerfObject**>(&m_pObject));    if (FAILED(hr))    {      return FALSE;    }    m_Counter.InitCounter(m_pObject);    if (FAILED(hr))    {      return FALSE;    }    if (!baseISAPI::GetExtensionVersion(pVer))    {      return FALSE;    }    if (GetCriticalIsapiError() != 0)    {      return TRUE;    }      return TRUE;  } 
end example
 

You also need to implement the QueryService method. Luckily, this is easy:

 HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService,           REFIID riid,  void**  ppvObject)  {    if (InlineIsEqualGUID(guidService, __uuidof(IHitCounter)))        return m_Counter.QueryInterface(riid, ppvObject);    return baseISAPI::QueryService(guidService, riid, ppvObject);  } 

Next, check that your service actually works by using it from your existing ValidateAndExchange method in your application DLL. First, find this method in the header file for your application DLL.

You ll need to #include your ISAPI header file where you have your CHitCounter code. Now you can use your hit counter to measure the hits to your page in Perfmon. The code looks like this (make sure if you remove the OnHello method that you remove the corresponding {{Hello}} tag from your SRF file):

 [ request_handler("Default") ]  class CbkPerfmonCleanHandler  {  private:    CComPtr<IHitCounter> m_HitCounter;  public:    HTTP_CODE ValidateAndExchange()    {      // Set the content-type      m_HttpResponse.SetContentType("text/html");      HRESULT hr = m_spServiceProvider->QueryService(__uuidof(IHitCounter),           &m_HitCounter);      if (FAILED(hr))      {        m_HitCounter = 0;        return HTTP_FAIL;      }      m_HitCounter->IncrementCounter();      return HTTP_SUCCESS;  }  }; 

Now you should know how to expose a service from your ISAPI DLL and consume it from your application DLLs.

Some Other Types of Perfmon Counters

So far we ve focused predominantly on the raw Perfmon counters. These counters basically allow you to store arbitrary data in Perfmon. Perfmon supports more than just raw data storage, however, and it provides a number of predefined counter types for many different situations. In this section, we cover the various styles of counter types and discuss some examples of where they re useful:

  • PERF_100NSEC TIMER : This family of counters measures some data over time. The time measured is in 100-nanosecond increments . These counters help you measure the time that something is in use (or not in use). You might use these counters to tune your application by determining which functionality is most often in use and optimizing your application for these requests . Similarly, you might look for functionality that isn t often used and look for ways to remove it.

  • PERF AVERAGE : These counters usually give some base data about your application. In particular, there are functions to calculate the average time per operation and the average bytes per operation. You can use these counters to measure things such as average response time or average request size .

  • PERF COUNTER LARGE : These counters all have non- LARGE equivalents. The LARGE versions allow for 8 bytes of data, whereas the regular types only store 4 bytes. You can use these counters whenever your data is bigger than 4 bytes.

  • PERF COUNTER MULTI : These counters have a non- MULTI counterpart . The MULTI versions allow you to track multiple items and provide you with an average. For example, whereas the TIMER tracks the time an item is in use, the MULTI_TIMER tracks the time a number of items are in use and then takes the average. These counters basically allow you to group data. For example, as opposed to wanting to know how much each individual cache is used, you may be more interested in comparing how often caching in general is used versus session state. MULTI counters allow you to average over all the cache timers.

  • PERF RAW : These counters allow you to store some raw data. This is the type of the counter you just created, as well as many of the built-in ATL Server counters.

  • PERF PRECISION : You should use these counters when the standard system timers aren t precise enough for accurate readings . The service providing the data for these counters needs to provide a timestamp. These counters are useful when monitoring short time- spans with high precision. For a Web application, these counters would be applicable for any server-side calculations ”maybe measuring times for certain numerical computations .

  • PERF SAMPLE : These counters allow a value of 1 or 0, and they track the number of 1s sampled. You can use a counter such as this to track an on or off “style sampling. For example, you could use these counters to monitor how often a cache is hit versus how often it s missed.

You can access these counters by using the countertype parameter in the perf_counter attribute (within your perf_object ).




ATL Server. High Performance C++ on. NET
Observing the User Experience: A Practitioners Guide to User Research
ISBN: B006Z372QQ
EAN: 2147483647
Year: 2002
Pages: 181

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net