/******************************************************************************
 *
 * Copyright (c) 2000	TOSHIYUKI ARAI. ALL RIGHTS RESERVED. 
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *  
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *	Performance.cpp
 *
 *****************************************************************************/

// SOL++2000

#include <tchar.h>
#include <sol\Stdio.h>
#include <sol\LogFile.h>
#include <sol\Performance.h>
#include <sol\ProcessInfo.h>


#define PEF_DATASIZE		(1024*10)

#define NAME_COUNTERS		"Counters"
#define NAME_PROCESS		"Process"
#define NAME_PROCESSID		"ID Process"
#define NAME_WORKING_SET	"Working Set"


#define NAME_PEFLIB		"Software\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009"


void Performance::getProcessList(LinkedList& list)
{
	PPERF_DATA_BLOCK	pdb = getDataBlock();
	if (pdb == NULL){
		return;
	}

	DWORD pidOffset = indexOffset(pdb, NAME_PROCESSID);
	DWORD wsOffset  = indexOffset(pdb, NAME_WORKING_SET);

	PPERF_OBJECT_TYPE         pot = getObjectType(pdb);
	PPERF_INSTANCE_DEFINITION pid = firstInstance(pdb);
	PPERF_COUNTER_BLOCK       pcb = firstCounterBlock(pdb);

	for(int n = 1; n < (int)pot->NumInstances; n++){
		DWORD id       = getLongValue(pcb, pidOffset);
		DWORD workSet  = getLongValue(pcb, wsOffset);

		char name[_MAX_PATH];
		getProcessName(pid, name, sizeof(name));

		list.add(new ProcessInfo(id, name));
		pid = nextInstance(pid);
		pcb = nextCounterBlock(pcb);
	}

	HeapFree(GetProcessHeap(), 0, pdb);

	return;
}


BOOL Performance::dumpTitleIndex()
{
	BOOL	rc = FALSE;
	HKEY	hkey;

	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NAME_PEFLIB, 0, KEY_READ, &hkey) 
			!= ERROR_SUCCESS){
		return rc;
	}

	HANDLE hHeap = GetProcessHeap();

	DWORD	size = 0;

	if (RegQueryValueEx(hkey, NAME_COUNTERS, NULL, NULL, NULL, &size)
			!= ERROR_SUCCESS){
		return rc;
	}

	char* buffer = (char*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, size);

	if (RegQueryValueEx(hkey, NAME_COUNTERS, NULL, NULL, (LPBYTE)buffer, &size)
			!= ERROR_SUCCESS){
		HeapFree(hHeap, 0, buffer);
		return rc;
	}

	char* lpsz = buffer;
	int   len = 0;
	LogFile logFile("titleindex.txt");
	while((len = strlen(lpsz))){
		char	name[MAX_PATH];
		strcpy(name, lpsz);
		lpsz += (len + 1);
		logFile.printf("%s=[%s]\r\n", name, lpsz);
	
		lpsz += (strlen(lpsz) + 1);
	}
	rc = TRUE;

	RegCloseKey(hkey);
	HeapFree(hHeap, 0, buffer);

	return rc;
}


DWORD Performance::getIndex(const char* title)
{
	DWORD	index = -1;
	HKEY	hkey;

	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NAME_PEFLIB, 0, KEY_READ, &hkey) 
			!= ERROR_SUCCESS){
		return index;
	}

	HANDLE hHeap = GetProcessHeap();

	DWORD	size = 0;

	if (RegQueryValueEx(hkey, NAME_COUNTERS, NULL, NULL, NULL, &size)
			!= ERROR_SUCCESS){
		return index;
	}

	char* buffer = (char*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, size);

	if (RegQueryValueEx(hkey, NAME_COUNTERS, NULL, NULL, (LPBYTE)buffer, &size)
			!= ERROR_SUCCESS){
		HeapFree(hHeap, 0, buffer);
		return index;
	}

	char* lpsz = buffer;
	int   len = 0;

	while((len = strlen(lpsz))){
		index = (DWORD)atoi(lpsz);
		lpsz += (len + 1);

		if(strcmp(title, lpsz) == 0){
			break;
		}
		lpsz += (strlen(lpsz) + 1);
	}
	
	RegCloseKey(hkey);
	HeapFree(hHeap, 0, buffer);

	return index;
}



PerfDataBlock* Performance::getDataBlock()
{
	HANDLE hHeap = GetProcessHeap();

	DWORD	size = PEF_DATASIZE;
	LRESULT rc = 0;

	DWORD id = getIndex(NAME_PROCESS);
	char index[MAX_PATH];
	sprintf(index, "%d", id);

	PPERF_DATA_BLOCK	pdb = NULL;

	do{
		pdb = (PerfDataBlock*)HeapAlloc(hHeap, 0, size);
		if(pdb == NULL){
			return NULL;
		}

		rc = RegQueryValueEx(HKEY_PERFORMANCE_DATA, index, 
					NULL, NULL, (LPBYTE)pdb, &size);

		if (rc == ERROR_MORE_DATA) {
			HeapFree(hHeap, 0, pdb);
			size += 1024;
			continue;
		}

	} while (rc == ERROR_MORE_DATA);

	return pdb;
}


DWORD Performance::indexOffset(PerfDataBlock* pdb, const char* name)
{
	PerfObject* pot = getObjectType(pdb);
	PerfCounter* pcd = firstCounter(pdb);

	DWORD index = getIndex(name);

	DWORD offset = 0;

	for(int n = 0; n < (int)pot->NumCounters; n++){
		if (pcd->CounterNameTitleIndex == index){
			offset = pcd->CounterOffset;
			break;
		}
		pcd = nextCounter(pcd);
	}

	return offset;
}



PerfInstance* Performance::findNthInstance(PerfDataBlock* pdb, 
													int index)
{
	PerfInstance* pid = firstInstance(pdb);

	PerfInstance* nth = NULL;
	int n = 0;
	while (pid) {
		if (n == index) {
			nth = pid;
			break;
		}
		pid = nextInstance(pid);
		n++;
	}
	return nth;
}

