Commit 903de9b6 authored by Dmitry Timoshkov's avatar Dmitry Timoshkov Committed by Alexandre Julliard

advapi32: Add initial support for querying performance counters data.

parent 4adfa3dc
......@@ -2,6 +2,7 @@
* Registry management
*
* Copyright (C) 1999 Alexandre Julliard
* Copyright (C) 2017 Dmitry Timoshkov
*
* Based on misc/registry.c code
* Copyright (C) 1996 Marcus Meissner
......@@ -36,6 +37,7 @@
#include "winreg.h"
#include "winerror.h"
#include "winternl.h"
#include "winperf.h"
#include "winuser.h"
#include "sddl.h"
#include "advapi32_misc.h"
......@@ -1480,6 +1482,234 @@ LONG WINAPI RegSetKeyValueA( HKEY hkey, LPCSTR subkey, LPCSTR name, DWORD type,
return ret;
}
struct perf_provider
{
HMODULE perflib;
PM_OPEN_PROC *pOpen;
PM_CLOSE_PROC *pClose;
PM_COLLECT_PROC *pCollect;
};
static void *get_provider_entry(HKEY perf, HMODULE perflib, const char *name)
{
char buf[MAX_PATH];
DWORD err, type, len;
len = sizeof(buf) - 1;
err = RegQueryValueExA(perf, name, NULL, &type, (BYTE *)buf, &len);
if (err != ERROR_SUCCESS || type != REG_SZ)
return NULL;
buf[len] = 0;
TRACE("Loading function pointer for %s: %s\n", name, debugstr_a(buf));
return GetProcAddress(perflib, buf);
}
static BOOL load_provider(HKEY root, const WCHAR *name, struct perf_provider *provider)
{
static const WCHAR performanceW[] = { 'P','e','r','f','o','r','m','a','n','c','e',0 };
static const WCHAR libraryW[] = { 'L','i','b','r','a','r','y',0 };
WCHAR buf[MAX_PATH], buf2[MAX_PATH];
DWORD err, type, len;
HKEY service, perf;
err = RegOpenKeyExW(root, name, 0, KEY_READ, &service);
if (err != ERROR_SUCCESS)
return FALSE;
err = RegOpenKeyExW(service, performanceW, 0, KEY_READ, &perf);
RegCloseKey(service);
if (err != ERROR_SUCCESS)
return FALSE;
len = sizeof(buf) - sizeof(WCHAR);
err = RegQueryValueExW(perf, libraryW, NULL, &type, (BYTE *)buf, &len);
if (err != ERROR_SUCCESS || !(type == REG_SZ || type == REG_EXPAND_SZ))
goto error;
buf[len / sizeof(WCHAR)] = 0;
if (type == REG_EXPAND_SZ)
{
len = ExpandEnvironmentStringsW(buf, buf2, MAX_PATH);
if (!len || len > MAX_PATH) goto error;
strcpyW(buf, buf2);
}
if (!(provider->perflib = LoadLibraryW(buf)))
{
WARN("Failed to load %s\n", debugstr_w(buf));
goto error;
}
GetModuleFileNameW(provider->perflib, buf, MAX_PATH);
TRACE("Loaded provider %s\n", wine_dbgstr_w(buf));
provider->pOpen = get_provider_entry(perf, provider->perflib, "Open");
provider->pClose = get_provider_entry(perf, provider->perflib, "Close");
provider->pCollect = get_provider_entry(perf, provider->perflib, "Collect");
if (provider->pOpen && provider->pClose && provider->pCollect)
{
RegCloseKey(perf);
return TRUE;
}
TRACE("Provider is missing required exports\n");
FreeLibrary(provider->perflib);
error:
RegCloseKey(perf);
return FALSE;
}
static DWORD collect_data(struct perf_provider *provider, const WCHAR *query, void **data, DWORD *size, DWORD *obj_count)
{
DWORD err;
err = provider->pOpen(NULL);
if (err != ERROR_SUCCESS)
{
TRACE("Open error %u (%#x)\n", err, err);
return err;
}
*obj_count = 0;
err = provider->pCollect((WCHAR *)query, data, size, obj_count);
if (err != ERROR_SUCCESS)
{
TRACE("Collect error %u (%#x)\n", err, err);
*obj_count = 0;
}
provider->pClose();
return err;
}
#define MAX_SERVICE_NAME 260
static DWORD query_perf_data(const WCHAR *query, DWORD *type, void *data, DWORD *ret_size)
{
static const WCHAR SZ_SERVICES_KEY[] = { 'S','y','s','t','e','m','\\',
'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
'S','e','r','v','i','c','e','s',0 };
DWORD err, i, data_size;
HKEY root;
PERF_DATA_BLOCK *pdb;
if (!ret_size)
return ERROR_INVALID_PARAMETER;
data_size = *ret_size;
*ret_size = 0;
if (type)
*type = REG_BINARY;
if (!data || data_size < sizeof(*pdb))
return ERROR_MORE_DATA;
pdb = data;
pdb->Signature[0] = 'P';
pdb->Signature[1] = 'E';
pdb->Signature[2] = 'R';
pdb->Signature[3] = 'F';
#ifdef WORDS_BIGENDIAN
pdb->LittleEndian = FALSE;
#else
pdb->LittleEndian = TRUE;
#endif
pdb->Version = PERF_DATA_VERSION;
pdb->Revision = PERF_DATA_REVISION;
pdb->TotalByteLength = 0;
pdb->HeaderLength = sizeof(*pdb);
pdb->NumObjectTypes = 0;
pdb->DefaultObject = 0;
QueryPerformanceCounter(&pdb->PerfTime);
QueryPerformanceFrequency(&pdb->PerfFreq);
data = pdb + 1;
pdb->SystemNameOffset = sizeof(*pdb);
pdb->SystemNameLength = (data_size - sizeof(*pdb)) / sizeof(WCHAR);
if (!GetComputerNameW(data, &pdb->SystemNameLength))
return ERROR_MORE_DATA;
pdb->SystemNameLength++;
pdb->SystemNameLength *= sizeof(WCHAR);
pdb->HeaderLength += pdb->SystemNameLength;
/* align to 8 bytes */
if (pdb->SystemNameLength & 7)
pdb->HeaderLength += 8 - (pdb->SystemNameLength & 7);
if (data_size < pdb->HeaderLength)
return ERROR_MORE_DATA;
pdb->TotalByteLength = pdb->HeaderLength;
data_size -= pdb->HeaderLength;
data = (char *)data + pdb->HeaderLength;
err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, SZ_SERVICES_KEY, 0, KEY_READ, &root);
if (err != ERROR_SUCCESS)
return err;
i = 0;
for (;;)
{
DWORD collected_size = data_size, obj_count = 0;
struct perf_provider provider;
WCHAR name[MAX_SERVICE_NAME];
void *collected_data = data;
err = RegEnumKeyW(root, i++, name, MAX_SERVICE_NAME);
if (err == ERROR_NO_MORE_ITEMS)
{
err = ERROR_SUCCESS;
break;
}
if (err != ERROR_SUCCESS)
continue;
if (!load_provider(root, name, &provider))
continue;
err = collect_data(&provider, query, &collected_data, &collected_size, &obj_count);
FreeLibrary(provider.perflib);
if (err == ERROR_MORE_DATA)
break;
if (err == ERROR_SUCCESS)
{
PERF_OBJECT_TYPE *obj = (PERF_OBJECT_TYPE *)data;
TRACE("Collect: obj->TotalByteLength %u, collected_size %u\n",
obj->TotalByteLength, collected_size);
data_size -= collected_size;
data = collected_data;
pdb->TotalByteLength += collected_size;
pdb->NumObjectTypes += obj_count;
}
}
RegCloseKey(root);
if (err == ERROR_SUCCESS)
{
*ret_size = pdb->TotalByteLength;
GetSystemTime(&pdb->SystemTime);
GetSystemTimeAsFileTime((FILETIME *)&pdb->PerfTime100nSec);
}
return err;
}
/******************************************************************************
* RegQueryValueExW [ADVAPI32.@]
*
......@@ -1500,6 +1730,10 @@ LSTATUS WINAPI RegQueryValueExW( HKEY hkey, LPCWSTR name, LPDWORD reserved, LPDW
(count && data) ? *count : 0 );
if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
if (hkey == HKEY_PERFORMANCE_DATA)
return query_perf_data(name, type, data, count);
if (!(hkey = get_special_root_hkey( hkey, 0 ))) return ERROR_INVALID_HANDLE;
RtlInitUnicodeString( &name_str, name );
......@@ -1590,7 +1824,8 @@ LSTATUS WINAPI DECLSPEC_HOTPATCH RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWO
hkey, debugstr_a(name), reserved, type, data, count, count ? *count : 0 );
if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
if (!(hkey = get_special_root_hkey( hkey, 0 ))) return ERROR_INVALID_HANDLE;
if (hkey != HKEY_PERFORMANCE_DATA && !(hkey = get_special_root_hkey( hkey, 0 )))
return ERROR_INVALID_HANDLE;
if (count) datalen = *count;
if (!data && count) *count = 0;
......@@ -1602,6 +1837,13 @@ LSTATUS WINAPI DECLSPEC_HOTPATCH RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWO
if ((status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
return RtlNtStatusToDosError(status);
if (hkey == HKEY_PERFORMANCE_DATA)
{
DWORD ret = query_perf_data( nameW.Buffer, type, data, count );
RtlFreeUnicodeString( &nameW );
return ret;
}
status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
buffer, sizeof(buffer), &total_size );
if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
......
......@@ -3637,10 +3637,10 @@ static void test_RegQueryValueExPerformanceData(void)
/* Test with data == NULL */
dwret = RegQueryValueExA( HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, NULL, &cbData );
todo_wine ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret );
ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret );
dwret = RegQueryValueExW( HKEY_PERFORMANCE_DATA, globalW, NULL, NULL, NULL, &cbData );
todo_wine ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret );
ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret );
/* Test ERROR_MORE_DATA, start with small buffer */
len = 10;
......@@ -3648,8 +3648,7 @@ static void test_RegQueryValueExPerformanceData(void)
cbData = len;
type = 0xdeadbeef;
dwret = RegQueryValueExA( HKEY_PERFORMANCE_DATA, "Global", NULL, &type, value, &cbData );
todo_wine ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret );
todo_wine
ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret );
ok(type == REG_BINARY, "got %u\n", type);
while( dwret == ERROR_MORE_DATA && limit)
{
......@@ -3662,14 +3661,13 @@ todo_wine
}
ok(limit > 0, "too many times ERROR_MORE_DATA returned\n");
todo_wine ok(dwret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", dwret);
todo_wine
ok(dwret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", dwret);
ok(type == REG_BINARY, "got %u\n", type);
/* Check returned data */
if (dwret == ERROR_SUCCESS)
{
todo_wine ok(len >= sizeof(PERF_DATA_BLOCK), "got size %d\n", len);
ok(len >= sizeof(PERF_DATA_BLOCK), "got size %d\n", len);
if (len >= sizeof(PERF_DATA_BLOCK)) {
pdb = (PERF_DATA_BLOCK*) value;
ok(pdb->Signature[0] == 'P', "expected Signature[0] = 'P', got 0x%x\n", pdb->Signature[0]);
......@@ -3686,13 +3684,11 @@ todo_wine
{
cbData = 0xdeadbeef;
dwret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, names[i], NULL, NULL, NULL, &cbData);
todo_wine
ok(dwret == ERROR_MORE_DATA, "%u/%s: got %u\n", i, names[i], dwret);
ok(cbData == 0, "got %u\n", cbData);
cbData = 0;
dwret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, names[i], NULL, NULL, NULL, &cbData);
todo_wine
ok(dwret == ERROR_MORE_DATA, "%u/%s: got %u\n", i, names[i], dwret);
ok(cbData == 0, "got %u\n", cbData);
......@@ -3725,9 +3721,7 @@ todo_wine
type = 0xdeadbeef;
cbData = sizeof(buf);
dwret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "invalid counter name", NULL, &type, buf, &cbData);
todo_wine
ok(dwret == ERROR_SUCCESS, "got %u\n", dwret);
todo_wine
ok(type == REG_BINARY, "got %u\n", type);
if (dwret == ERROR_SUCCESS)
{
......@@ -3757,6 +3751,7 @@ todo_wine
ok(pdb->TotalByteLength == len, "got %u vs %u\n", pdb->TotalByteLength, len);
ok(pdb->HeaderLength == pdb->TotalByteLength, "got %u\n", pdb->HeaderLength);
ok(pdb->NumObjectTypes == 0, "got %u\n", pdb->NumObjectTypes);
todo_wine
ok(pdb->DefaultObject != 0, "got %u\n", pdb->DefaultObject);
ok(pdb->SystemTime.wYear == st.wYear, "got %u\n", pdb->SystemTime.wYear);
ok(pdb->SystemTime.wMonth == st.wMonth, "got %u\n", pdb->SystemTime.wMonth);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment