advapi32: Add initial support for querying performance counters data.
Signed-off-by: Dmitry Timoshkov <dmitry@baikal.ru> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
4adfa3dc26
commit
903de9b678
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue