The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
// PerfMon.xs
//       +==========================================================+
//       |                                                          |
//       |                        PerfMon.xs                        |
//       |                     ---------------                      |
//       |                                                          |
//       | Copyright (c) 2004 Glen Small. All rights reserved. 	    |
//       |   This program is free software; you can redistribute    |
//       | it and/or modify it under the same terms as Perl itself. |
//       |                                                          |
//       +==========================================================+
//
//
//	Use under GNU General Public License or Larry Wall's "Artistic License"
//
//Check the README.TXT file that comes with this package for details about
//	it's history.
//

#define WIN32_LEAN_AND_MEAN

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "windows.h"
#include "PDH.h"
#include "PDHMSG.h"


MODULE = Win32::PerfMon		PACKAGE = Win32::PerfMon

void
open_query()

	PREINIT:

		PDH_STATUS stat;
		HQUERY	hQwy;
		DWORD dwGlen;

	PPCODE:

		dwGlen = 1;

		stat = PdhOpenQuery(NULL, dwGlen, &hQwy);


		if(stat != ERROR_SUCCESS)
		{
			XPUSHs(sv_2mortal(newSViv(-1)));
		}
		else
		{
			XPUSHs(sv_2mortal(newSViv((long)hQwy)));
		}




void
CleanUp(objQuery)
	SV* objQuery

	PREINIT:

		PDH_STATUS stat;
		HQUERY	pObj;

	PPCODE:

		pObj = (HQUERY)SvIV(objQuery);

		stat = PdhCloseQuery(pObj);


void
add_counter(BoxName, ObjectName, CounterName, InstanceName, InstanceNumber, pQwy, pError)
	SV* BoxName
	SV* ObjectName
	SV* CounterName
	SV* InstanceName
	SV* InstanceNumber
	SV* pQwy
	SV* pError;

	PREINIT:

		PDH_COUNTER_PATH_ELEMENTS	GStruct;
		DWORD dwSize;
		DWORD dwGlen;
		HCOUNTER cnt;
		HQUERY hQwy;
		char str[256];
		PDH_STATUS	stat;
		STRLEN len1;
		STRLEN len2;
		STRLEN len3;
		STRLEN BoxNameLen;
		DWORD TheInstance;

	PPCODE:

		hQwy = (HQUERY)SvIV(pQwy);

		dwGlen = 1;
		dwSize = 256;

		len1 = sv_len(ObjectName);
		len2 = sv_len(CounterName);
		BoxNameLen = sv_len(BoxName);

		TheInstance = SvUV(InstanceNumber);

		if(TheInstance == -1)
		{
			TheInstance = 0;
		}

		if(SvNIOK(InstanceName))
		{
			GStruct.szInstanceName = NULL;
		}
		else
		{
			len3 = sv_len(InstanceName);
			GStruct.szInstanceName = SvPV(InstanceName, len3);
		}

		GStruct.szObjectName = SvPV(ObjectName, len1);
		GStruct.szCounterName = SvPV(CounterName, len2);
		GStruct.szMachineName = SvPV(BoxName, BoxNameLen);
		GStruct.szParentInstance = NULL;
		GStruct.dwInstanceIndex = TheInstance;

		stat = PdhMakeCounterPath(&GStruct, (char*)str, &dwSize, NULL);



		if(stat != ERROR_SUCCESS)
		{
			sv_setpv(pError, "Failed to make the counter path - either the object, counter, or instance isn't valid");
			XPUSHs(sv_2mortal(newSViv(-1)));
		}
		else
		{

			stat = PdhAddCounter(hQwy, (LPTSTR)str, dwGlen, &cnt);

			switch(stat)
			{
				case ERROR_SUCCESS:

					XPUSHs(sv_2mortal(newSViv((long)cnt)));

					break;

				case PDH_CSTATUS_BAD_COUNTERNAME:

					sv_setpv(pError, "The counter name path string could not be parsed or interpreted.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_CSTATUS_NO_COUNTER:

					sv_setpv(pError, "The specified counter was not found.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_CSTATUS_NO_COUNTERNAME:

					sv_setpv(pError, "An empty counter name path string was passed in.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_CSTATUS_NO_MACHINE:

					sv_setpv(pError, "A computer entry could not be created.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_CSTATUS_NO_OBJECT:

					sv_setpv(pError, "The specified object could not be found.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_FUNCTION_NOT_FOUND:

					sv_setpv(pError, "The calculation function for this counter could not be determined.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_INVALID_ARGUMENT:

					sv_setpv(pError, "One or more arguments are invalid.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_INVALID_HANDLE:

					sv_setpv(pError, "The query handle is not valid.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_MEMORY_ALLOCATION_FAILURE:

					sv_setpv(pError, "A memory buffer could not be allocated.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				default:

					sv_setpv(pError, "Failed to add the counter - don't know why");
					XPUSHs(sv_2mortal(newSViv(-1)));
			}
		}





void
collect_data(pQwy, pError)
	SV* pQwy
	SV* pError

	PREINIT:

		HQUERY hQwy;
		PDH_STATUS stat;

	PPCODE:


		hQwy = (HQUERY)SvIV(pQwy);

		stat = PdhCollectQueryData(hQwy);

		switch(stat)
		{
			case ERROR_SUCCESS:

				XPUSHs(sv_2mortal(newSViv(0)));

				break;

			case PDH_INVALID_HANDLE:

				sv_setpv(pError, "The query handle is not valid.");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_NO_DATA:

				sv_setpv(pError, "The query does not currently have any counters.");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			default:

				sv_setpv(pError, "Collect Data Failed - I don't know why");
				XPUSHs(sv_2mortal(newSViv(-1)));

		}

void
collect_counter_value(pQwy, pCounter, pError)
	SV* pQwy
	SV* pCounter
	SV* pError

	PREINIT:

		HQUERY hQwy;
		HCOUNTER hCnt;
		PDH_STATUS stat;
		PDH_FMT_COUNTERVALUE val;
		DWORD dwType;

	PPCODE:

		hQwy = (HQUERY)SvIV(pQwy);
		hCnt = (HCOUNTER)SvIV(pCounter);

		stat = PdhGetFormattedCounterValue(hCnt, PDH_FMT_LONG | PDH_FMT_NOSCALE , &dwType, &val);

		switch(stat)
		{
			case ERROR_SUCCESS:

				XPUSHs(sv_2mortal(newSViv(val.longValue)));

				break;

			case PDH_INVALID_ARGUMENT:

				sv_setpv(pError, "An argument is not correct or is incorrectly formatted.");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_INVALID_DATA:

				sv_setpv(pError, "The specified counter does not contain valid data or a successful status code.");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_INVALID_HANDLE:

				sv_setpv(pError, "The counter handle is not valid.");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			default:

				sv_setpv(pError, "Failed to get the counter value - I don't know why.");
				XPUSHs(sv_2mortal(newSViv(-1)));

		}


void
list_objects(pBox, pError)
	SV*	pBox
	SV* pError

	PREINIT:

		DWORD dwSize;
		PDH_STATUS stat;
		char* szBuffer;
		char* szBox;
		char* c;
		STRLEN len;
		int index;

	PPCODE:

		len = sv_len(pBox);
		szBox = SvPV(pBox, len);

		stat = PdhEnumObjects(NULL, szBox, NULL, &dwSize, PERF_DETAIL_EXPERT, 0);

		Newz(0, szBuffer, (int)dwSize, char);

		stat = PdhEnumObjects(NULL, szBox, szBuffer, &dwSize, PERF_DETAIL_EXPERT, 0);

		c = szBuffer;

		for(index=0; index<(int)dwSize; index++)
		{
			if(*c == 0x00)
			{
				*c = '|';
			}

			c++;
		}

		switch(stat)
		{
			case ERROR_SUCCESS:

				XPUSHs(sv_2mortal(newSVpv(szBuffer, 0)));

				break;

			case PDH_MORE_DATA:

				printf("There are more entries available to return than there is room in the buffer\n");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_INSUFFICIENT_BUFFER:

				sv_setpv(pError, "The buffer provided is not large enough to contain any data.\n");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_INVALID_ARGUMENT:

				sv_setpv(pError, "A required argument is invalid or a reserved argument is not NULL.\n");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			default:

				sv_setpv(pError, "I have no idea what went wrong\n");
				XPUSHs(sv_2mortal(newSViv(-1)));
		}

		Safefree(szBuffer);

void
connect_to_box(pBox, pError)
	SV* pBox
	SV* pError

	PREINIT:

		PDH_STATUS stat;
		char* szBox;
		STRLEN len;

	PPCODE:

		len = sv_len(pBox);
		szBox = SvPV(pBox, len);

		stat = PdhConnectMachine(szBox);

		switch(stat)
		{
			case ERROR_SUCCESS:

				XPUSHs(sv_2mortal(newSViv(0)));

				break;

			case PDH_CSTATUS_NO_MACHINE:

				sv_setpv(pError, "Unable to connect to the specified machine");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_MEMORY_ALLOCATION_FAILURE:

				sv_setpv(pError, "Unable to allocate a dynamic memory block due to too many applications running on the system or an insufficient memory paging file.");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			default:

				sv_setpv(pError, "ERROR: Don't really know what happened though !");
				XPUSHs(sv_2mortal(newSViv(-1)));
		}

void
explain_counter(pCounter, pError)
	SV* pCounter
	SV* pError

	PREINIT:

		PDH_COUNTER_INFO* cntInfo;
		HCOUNTER cnt;
		PDH_STATUS	stat;
		DWORD dwSize;

	PPCODE:

		cnt = (HCOUNTER)SvIV(pCounter);
		cntInfo = NULL;
		dwSize = 0;

		stat = PdhGetCounterInfo(cnt, 1, &dwSize, cntInfo);

		New(0, cntInfo, (int)dwSize, PDH_COUNTER_INFO);

		cntInfo->dwLength = dwSize;

		stat = PdhGetCounterInfo(cnt, 1, &dwSize, cntInfo);

		if(stat ==  ERROR_SUCCESS)
		{
			XPUSHs(sv_2mortal(newSVpv(cntInfo->szExplainText, 0)));
		}
		else
		{
			sv_setpv(pError, "Failed to get the explain text for this counter");
			XPUSHs(sv_2mortal(newSViv(-1)));
		}


		Safefree(cntInfo);


void
remove_counter(pCounter, pError)
	SV* pCounter
	SV* pError

	PREINIT:

		HCOUNTER cnt;
		PDH_STATUS stat;

	PPCODE:

		cnt = (HCOUNTER)SvIV(pCounter);

		stat = PdhRemoveCounter(cnt);

		if(stat == PDH_INVALID_HANDLE)
		{
			sv_setpv(pError, "The query handle is not valid.");
			XPUSHs(sv_2mortal(newSViv(-1)));
		}
		else
		{
			XPUSHs(sv_2mortal(newSViv(0)));
		}


void
list_counters(pBox, pObject, pError)
	SV*	pBox
	SV* pObject
	SV* pError

	PREINIT:

		DWORD dwSize;
		DWORD dwSize1;
		PDH_STATUS stat;
		char* szBuffer;
		char* szBuffer2;
		char* szBox;
		char* szObject;
		char* c;
		STRLEN len;
		STRLEN len2;
		int index;

	PPCODE:

		dwSize = 0;
		dwSize1 = 0;
		szBuffer = NULL;
		szBuffer2 = NULL;
		len = sv_len(pBox);
		szBox = SvPV(pBox, len);

		len2 = sv_len(pObject);
		szObject = SvPV(pObject, len2);

		stat = PdhEnumObjects(NULL, szBox, NULL, &dwSize, PERF_DETAIL_EXPERT, 1);

		dwSize = 0;

		stat = PdhEnumObjectItems(NULL, szBox, szObject, szBuffer, &dwSize, szBuffer2, &dwSize1, PERF_DETAIL_EXPERT, 0);

		dwSize += 5;

		Newz(0, szBuffer, (int)dwSize, char);

		stat = PdhEnumObjectItems(NULL, szBox, szObject, szBuffer, &dwSize, szBuffer2, &dwSize1, PERF_DETAIL_EXPERT, 0);

		c = szBuffer;

		for(index=0; index<(int)dwSize; index++)
		{
			if(*c == 0x00)
			{
				*c = '|';
			}

			c++;
		}

		switch(stat)
		{
			case ERROR_SUCCESS:

				XPUSHs(sv_2mortal(newSVpv(szBuffer, 0)));

				break;

			case PDH_MORE_DATA:

				printf("There are more entries available to return than there is room in the buffer\n");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_MEMORY_ALLOCATION_FAILURE:

				sv_setpv(pError, "A required temporary buffer could not be allocated.\n");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_INVALID_ARGUMENT:

				sv_setpv(pError, "A required argument is invalid or a reserved argument is not NULL.\n");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_CSTATUS_NO_MACHINE:

				sv_setpv(pError, "The specified computer is offline or unavailable.\n");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_CSTATUS_NO_OBJECT:

				sv_setpv(pError, "The specified object could not be found on the specified computer.\n");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			default:

				sv_setpv(pError, "I have no idea what went wrong\n");
				XPUSHs(sv_2mortal(newSViv(-1)));
		}

		Safefree(szBuffer);

void
list_instances(pBox, pObject, pError)
	SV*	pBox
	SV* pObject
	SV* pError

	PREINIT:

		DWORD dwSize;
		DWORD dwSize1;
		PDH_STATUS stat;
		char* szBuffer;
		char* szBuffer2;
		char* szBox;
		char* szObject;
		char* c;
		STRLEN len;
		STRLEN len2;
		int index;

	PPCODE:

		dwSize = 0;
		dwSize1 = 0;
		szBuffer = NULL;
		szBuffer2 = NULL;
		len = sv_len(pBox);
		szBox = SvPV(pBox, len);

		len2 = sv_len(pObject);
		szObject = SvPV(pObject, len2);

		stat = PdhEnumObjects(NULL, szBox, NULL, &dwSize, PERF_DETAIL_EXPERT, 1);

		dwSize = 0;

		stat = PdhEnumObjectItems(NULL, szBox, szObject, szBuffer, &dwSize, szBuffer2, &dwSize1, PERF_DETAIL_EXPERT, 0);

		dwSize1 += 5;

		Newz(0, szBuffer2, (int)dwSize1, char);

		stat = PdhEnumObjectItems(NULL, szBox, szObject, szBuffer, &dwSize, szBuffer2, &dwSize1, PERF_DETAIL_EXPERT, 0);

		c = szBuffer2;

		for(index=0; index<(int)dwSize1; index++)
		{
			if(*c == 0x00)
			{
				*c = '|';
			}

			c++;
		}

		switch(stat)
		{
			case ERROR_SUCCESS:

				XPUSHs(sv_2mortal(newSVpv(szBuffer2, 0)));

				break;

			case PDH_MORE_DATA:

				printf("There are more entries available to return than there is room in the buffer\n");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_MEMORY_ALLOCATION_FAILURE:

				sv_setpv(pError, "A required temporary buffer could not be allocated.\n");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_INVALID_ARGUMENT:

				sv_setpv(pError, "A required argument is invalid or a reserved argument is not NULL.\n");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_CSTATUS_NO_MACHINE:

				sv_setpv(pError, "The specified computer is offline or unavailable.\n");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_CSTATUS_NO_OBJECT:

				sv_setpv(pError, "The specified object could not be found on the specified computer.\n");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			default:

				sv_setpv(pError, "I have no idea what went wrong\n");
				XPUSHs(sv_2mortal(newSViv(-1)));
		}

		Safefree(szBuffer2);