Add single_performance_counter to c-pal. (#272)

* Moving single_performance_counter to c-pal. Currently, this is a
Windows-only implementation.
This commit is contained in:
David Robson 2023-10-02 13:50:23 -07:00 коммит произвёл GitHub
Родитель 96df0cfbf2
Коммит 30a0dd6fde
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 1045 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,44 @@
// Copyright (C) Microsoft Corporation. All rights reserved.
#ifndef SINGLE_PERFORMANCE_COUNTER_H
#define SINGLE_PERFORMANCE_COUNTER_H
#ifdef __cplusplus
#include <cinttypes>
#include <cstddef>
#include <cstdint>
#else
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#endif
#include "macro_utils/macro_utils.h"
#include "c_pal/thandle.h"
#include "umock_c/umock_c_prod.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct SINGLE_PERFORMANCE_COUNTER_TAG* SINGLE_PERFORMANCE_COUNTER_HANDLE;
#define SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT_VALUES \
SINGLE_PERFORMANCE_COUNTER_SAMPLE_SUCCESS, \
SINGLE_PERFORMANCE_COUNTER_SAMPLE_ERROR, \
SINGLE_PERFORMANCE_COUNTER_SAMPLE_COLLECT_FAILED, \
SINGLE_PERFORMANCE_COUNTER_SAMPLE_FORMAT_FAILED
MU_DEFINE_ENUM(SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT, SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT_VALUES);
MOCKABLE_FUNCTION(, SINGLE_PERFORMANCE_COUNTER_HANDLE, single_performance_counter_create, const char*, performance_object, const char*, performance_counter);
MOCKABLE_FUNCTION(, SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT, single_performance_counter_sample_double, SINGLE_PERFORMANCE_COUNTER_HANDLE, handle, double*, sample);
MOCKABLE_FUNCTION(, void, single_performance_counter_destroy, SINGLE_PERFORMANCE_COUNTER_HANDLE, handle);
#ifdef __cplusplus
}
#endif
#endif /* SINGLE_PERFORMANCE_COUNTER_H */

Просмотреть файл

@ -54,6 +54,7 @@ set(pal_linux_c_files
src/file_util_linux.c
src/pipe_linux.c
src/platform_linux.c
src/single_performance_counter_linux.c
src/srw_lock_linux.c
src/srw_lock_ll_linux.c
src/string_utils.c

Просмотреть файл

@ -0,0 +1,36 @@
// Copyright (C) Microsoft Corporation. All rights reserved.
#include <stddef.h>
#include "c_logging/logger.h"
#include "c_pal/single_performance_counter.h"
#include "umock_c/umock_c_prod.h"
// Stubbed functions for Linux
typedef struct SINGLE_PERFORMANCE_COUNTER_TAG
{
int reserved;
} SINGLE_PERFORMANCE_COUNTER;
MU_DEFINE_ENUM_STRINGS(SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT, SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT_VALUES);
IMPLEMENT_MOCKABLE_FUNCTION(, SINGLE_PERFORMANCE_COUNTER_HANDLE, single_performance_counter_create, const char*, performance_object, const char*, performance_counter)
{
LogError("single_performance_counter_create not implemented");
return NULL;
}
IMPLEMENT_MOCKABLE_FUNCTION(, void, single_performance_counter_destroy, SINGLE_PERFORMANCE_COUNTER_HANDLE, handle)
{
LogError("single_performance_counter_destroy not implemented");
(void)handle;
}
IMPLEMENT_MOCKABLE_FUNCTION(, SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT, single_performance_counter_sample_double, SINGLE_PERFORMANCE_COUNTER_HANDLE, handle, double*, sample)
{
SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT result = SINGLE_PERFORMANCE_COUNTER_SAMPLE_ERROR;
LogError("single_performance_counter_sample_double not implemented");
return result;
}

Просмотреть файл

@ -43,6 +43,7 @@ set(pal_win32_c_files
src/threadpool_win32.c
src/pipe_win32.c
src/platform_win32.c
src/single_performance_counter_win32.c
src/srw_lock_win32.c
src/srw_lock_ll_win32.c
src/string_utils.c

Просмотреть файл

@ -0,0 +1,113 @@
# `single_performance_counter` Requirements
## Overview
`single_performance_counter` is a unit which accesses Windows performance libraries to sample a single performance counter.
Single Performance counter is named because it samples a single performance counter. It is created with a performance object and counter name.
Examples of the performance object and counter name would be "Process V2" and "% Processor Time", used to get CPU usage. It is a wrapper around the
Performance Data Helper (Pdh) library. See [Using the PDH Functions to Consume Counter Data](https://learn.microsoft.com/en-us/windows/win32/perfctrs/using-the-pdh-functions-to-consume-counter-data)
for more information on the Pdh library.
The Pdh library expects a path to a performance counter, and the proper path for the CPU usage is `"\[performance object]([app_name]:[pid])\[counter]"`.
See [Specifying a counter path](https://learn.microsoft.com/en-us/windows/win32/perfctrs/specifying-a-counter-path). There is a machine name component to
the path, but it should not be neccessary because we are querying the local system. The app name is derived from `GetModuleFileNameA` and the pid is
derived from `GetCurrentProcessId`. Validaton of the path name is handled by the first call to `PdhCollectQueryData` during creation, which also primes
the query as it requires two samples to generate a formatted value.
## Exposed API
```c
typedef struct SINGLE_PERFORMANCE_COUNTER* SINGLE_PERFORMANCE_COUNTER_HANDLE;
#define SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT_VALUES \
SINGLE_PERFORMANCE_COUNTER_SAMPLE_SUCCESS, \
SINGLE_PERFORMANCE_COUNTER_SAMPLE_ERROR, \
SINGLE_PERFORMANCE_COUNTER_SAMPLE_COLLECT_FAILED, \
SINGLE_PERFORMANCE_COUNTER_SAMPLE_FORMAT_FAILED
MU_DEFINE_ENUM(SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT, SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT_VALUES);
MOCKABLE_FUNCTION(, SINGLE_PERFORMANCE_COUNTER_HANDLE, single_performance_counter_create, const char*, performance_object, const char*, performance_counter);
MOCKABLE_FUNCTION(, SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT, single_performance_counter_sample_double, SINGLE_PERFORMANCE_COUNTER_HANDLE, handle, double*, sample);
MOCKABLE_FUNCTION(, void, single_performance_counter_destroy, SINGLE_PERFORMANCE_COUNTER_HANDLE, handle);
```
### single_performance_counter_create
```c
MOCKABLE_FUNCTION(, SINGLE_PERFORMANCE_COUNTER_HANDLE, single_performance_counter_create, const char*, performance_object, const char*, performance_counter);
```
`single_performance_counter_create` creates a handle to manage a single performance counter.
**SRS_SINGLE_PERFORMANCE_COUNTER_45_002: [** `single_performance_counter_create` shall return `NULL` if `performance_object` is `NULL`. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_003: [** `single_performance_counter_create` shall return `NULL` if `performance_counter` is `NULL`. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_004: [** `single_performance_counter_create` shall call `malloc` to create a new `SINGLE_PERFORMANCE_COUNTER` struct. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_005: [** `single_performance_counter_create` shall call `GetCurrentProcessId` to get the current process id. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_006: [** `single_performance_counter_create` shall call `GetModuleFileNameA` to get the current executable path. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_007: [** `single_performance_counter_create` shall call `strrchr` on the executable path with '\\' to get the executable name. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_008: [** `single_performance_counter_create` shall call `strrchr` on the executable name with '.' to get the executable name without extension. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_009: [** `single_performance_counter_create` shall call `snprintf` to copy the executable name without extension. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_010: [** `single_performance_counter_create` shall call `snprintf` to copy the `performance_object`, `performance_counter`, pid, and the executable name without extension into a single string. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_011: [** `single_performance_counter_create` shall call `PdhOpenQueryA` to create a new query. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_012: [** `single_performance_counter_create` shall call `PdhAddCounterA` to add the counter to the query. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_013: [** `single_performance_counter_create` shall call `PdhCollectQueryData` to prime the query. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_014: [** `single_performance_counter_create` shall save `PDH_HQUERY` handle, and counter `HCOUNTER` in the `SINGLE_PERFORMANCE_COUNTER` struct. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_015: [** `single_performance_counter_create` shall return a non-null to the `SINGLE_PERFORMANCE_COUNTER_HANDLE` struct on success. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_016: [** `single_performance_counter_create` shall return `NULL` if any step fails. **]**
### single_performance_counter_destroy
```c
MOCKABLE_FUNCTION(, void, single_performance_counter_destroy, SINGLE_PERFORMANCE_COUNTER_HANDLE, handle);
```
`single_performance_counter_destroy` disposes of any Pdh library handles.
**SRS_SINGLE_PERFORMANCE_COUNTER_45_017: [** `single_performance_counter_destroy` shall return if `handle` is `NULL`. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_018: [** `single_performance_counter_destroy` shall call `PdhCloseQuery` with the `PDH_HQUERY` handle. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_019: [** `single_performance_counter_destroy` shall call free on the `handle` **]**
### single_performance_counter_sample_double
```c
MOCKABLE_FUNCTION(, SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT, single_performance_counter_sample_double, SINGLE_PERFORMANCE_COUNTER_HANDLE, handle, double*, sample);
```
`single_performance_counter_sample_double` provides a sample of the current performance counter as a double value.
**SRS_SINGLE_PERFORMANCE_COUNTER_45_020: [** `single_performance_counter_sample_double` shall return `SINGLE_PERFORMANCE_COUNTER_SAMPLE_ERROR` if `handle` is `NULL`. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_027: [** `single_performance_counter_sample_double` shall return `SINGLE_PERFORMANCE_COUNTER_SAMPLE_ERROR` if `sample` is `NULL`. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_021: [** `single_performance_counter_sample_double` shall call `PdhCollectQueryData` on the `PDH_HQUERY` handle. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_022: [** `single_performance_counter_sample_double` shall return `SINGLE_PERFORMANCE_COUNTER_SAMPLE_COLLECT_FAILED` if `PdhCollectQueryData` fails. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_023: [** `single_performance_counter_sample_double` shall call `PdhGetFormattedCounterValue` on the counter `HCOUNTER`. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_024: [** `single_performance_counter_sample_double` shall return `SINGLE_PERFORMANCE_COUNTER_SAMPLE_FORMAT_FAILED` if `PdhGetFormattedCounterValue` fails. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_025: [** `single_performance_counter_sample_double` shall set the `sample` to the `double` value given by `PdhGetFormattedCounterValue`. **]**
**SRS_SINGLE_PERFORMANCE_COUNTER_45_026: [** `single_performance_counter_sample_double` shall return `SINGLE_PERFORMANCE_COUNTER_SAMPLE_SUCCESS` on success. **]**

Просмотреть файл

@ -0,0 +1,281 @@
// Copyright (C) Microsoft Corporation. All rights reserved.
#include <inttypes.h>
#include "macro_utils/macro_utils.h"
#include "c_logging/logger.h"
#include "c_pal/gballoc_hl.h"
#include "c_pal/gballoc_hl_redirect.h"
#include "c_pal/srw_lock.h"
#include "pdh.h"
#include "c_pal/single_performance_counter.h"
typedef struct SINGLE_PERFORMANCE_COUNTER_TAG
{
PDH_HQUERY perf_query_handle;
HCOUNTER current_counter;
} SINGLE_PERFORMANCE_COUNTER;
MU_DEFINE_ENUM_STRINGS(SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT, SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT_VALUES);
static int get_current_app_name(char* app_name, uint32_t app_name_size)
{
int result;
char executable_full_file_name[MAX_PATH + 1];
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_006: [ single_performance_counter_create shall call GetModuleFileNameA to get the current executable path. ]
DWORD get_module_file_name_a_result = GetModuleFileNameA(NULL, executable_full_file_name, MAX_PATH + 1);
if (get_module_file_name_a_result == MAX_PATH + 1)
{
LogLastError("failure in GetModuleFileNameA(NULL, fullExecutableFileName=%p, MAX_PATH=%d + 1), it returned %" PRIu32 " which means location did not have enough space to hold the path", executable_full_file_name, MAX_PATH, get_module_file_name_a_result);
result = MU_FAILURE;
}
else if (get_module_file_name_a_result == 0)
{
LogLastError("failure in GetModuleFileNameA(NULL, fullExecutableFileName=%p, MAX_PATH=%d + 1), it returned %" PRIu32 "", executable_full_file_name, MAX_PATH, get_module_file_name_a_result);
result = MU_FAILURE;
}
else
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_007: [ single_performance_counter_create shall call strrchr on the executable path with '\' to get the executable name. ]
const char* where_is_last_backslash = strrchr(executable_full_file_name, '\\');
if (where_is_last_backslash == NULL)
{
LogError("unexpected not to have a \\ character in fullExecutableFileName=%s", executable_full_file_name);
result = MU_FAILURE;
}
else
{
// Move past last backslash.
where_is_last_backslash++;
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_008: [ single_performance_counter_create shall call strrchr on the executable name with '.' to get the executable name without extension. ]
const char* where_is_last_dot = strrchr(where_is_last_backslash, '.');
if (where_is_last_dot == NULL)
{
LogError("unexpected not to have a . character in where_is_last_backslash=%s", where_is_last_backslash);
result = MU_FAILURE;
}
else if (where_is_last_dot <= where_is_last_backslash)
{
LogError("unexpected not to have a . character in where_is_last_backslash=%s", where_is_last_backslash);
result = MU_FAILURE;
}
else
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_009: [ single_performance_counter_create shall call snprintf to copy the executable name without extension. ]
int bytes_printed = snprintf(app_name, app_name_size, "%*.*s",
(int)(where_is_last_dot - where_is_last_backslash), (int)(where_is_last_dot - where_is_last_backslash), where_is_last_backslash);
if (bytes_printed < 0)
{
LogError("sprintf failure: app_name = %p, size = %" PRIu32 ", where_is_last_backslash = %s, precision = %" PRIi32 "",
app_name, app_name_size, where_is_last_backslash, (int)(where_is_last_dot - where_is_last_backslash));
result = MU_FAILURE;
}
else if ((uint32_t)bytes_printed >= app_name_size - 1)
{
LogError("given buffer for app_name, size = %" PRIu32 "is smaller than found module name where_is_last_backslash = % s", app_name_size, where_is_last_backslash);
result = MU_FAILURE;
}
else
{
result = 0;
}
}
}
}
return result;
}
static int get_current_counter_path_string(const char* performance_object, const char* performance_counter, char* path_string, uint32_t max_path_size)
{
int result;
char app_name[MAX_PATH];
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_005: [ single_performance_counter_create shall call GetCurrentProcessId to get the current process id. ]
DWORD pid = GetCurrentProcessId();
result = get_current_app_name(app_name, MAX_PATH);
if (result != 0)
{
LogError("get_current_app_name failed, result = %" PRIi32 "", result);
}
else
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_010: [ single_performance_counter_create shall call snprintf to copy the performance_object, performance_counter, pid, and the executable name without extension into a single string. ]
int bytes_printed = snprintf(path_string, max_path_size, "\\%s(%s:%d)\\%s",
performance_object, app_name, pid, performance_counter);
if (bytes_printed < 0)
{
LogError("sprintf failure: path_string = %p, size = %" PRIu32 ", app_name = %s",
path_string, max_path_size, app_name);
result = MU_FAILURE;
}
else if ((uint32_t)bytes_printed >= max_path_size - 1)
{
LogError("given buffer for path_string, size = %" PRIu32 "is smaller than counter path value = %" PRIi32 "", max_path_size, bytes_printed);
result = MU_FAILURE;
}
else
{
result = 0;
}
}
return result;
}
IMPLEMENT_MOCKABLE_FUNCTION(, SINGLE_PERFORMANCE_COUNTER_HANDLE, single_performance_counter_create, const char*, performance_object, const char*, performance_counter)
{
SINGLE_PERFORMANCE_COUNTER_HANDLE result;
if
(
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_002: [ single_performance_counter_create shall return NULL if performance_object is NULL. ]
performance_object == NULL ||
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_003: [ single_performance_counter_create shall return NULL if performance_counter is NULL. ]
performance_counter == NULL
)
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_016: [ single_performance_counter_create shall return NULL if any step fails. ]
LogError("Invalid Arguments: const char* performance_object = %p, const char* performance_counter = %p",
performance_object, performance_counter);
result = NULL;
}
else
{
PDH_HQUERY perf_query_handle;
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_011: [ single_performance_counter_create shall call PdhOpenQueryA to create a new query. ]
PDH_STATUS perf_status = PdhOpenQueryA(NULL, (DWORD_PTR)NULL, &perf_query_handle);
if (perf_status != ERROR_SUCCESS)
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_016: [ single_performance_counter_create shall return NULL if any step fails. ]
LogError("Failure in PdhOpenQueryA, status = %" PRIx32"", perf_status);
result = NULL;
}
else
{
// See: https://learn.microsoft.com/en-us/windows/win32/perfctrs/specifying-a-counter-path
// Path should be: "\Process V2([app_name]:[pid])\% Processor Time"
// Getting local machine value, should NOT need "\\[hostname] prefix.
char counter_path_buffer[MAX_PATH]; // PDH_MAX_COUNTER_PATH is a bit large, we expect this to be < MAX_PATH
if (get_current_counter_path_string(performance_object, performance_counter, counter_path_buffer, MAX_PATH) != 0)
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_016: [ single_performance_counter_create shall return NULL if any step fails. ]
LogError("Failure in get_current_counter_path_string");
result = NULL;
}
else
{
HCOUNTER counter;
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_012: [ single_performance_counter_create shall call PdhAddCounterA to add the counter to the query. ]
perf_status = PdhAddCounterA(perf_query_handle, counter_path_buffer, 0, &counter);
if (perf_status != ERROR_SUCCESS)
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_016: [ single_performance_counter_create shall return NULL if any step fails. ]
LogError("Failure in PdhAddCounterA, status = %" PRIx32"", perf_status);
result = NULL;
}
else
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_013: [ single_performance_counter_create shall call PdhCollectQueryData to prime the query. ]
perf_status = PdhCollectQueryData(perf_query_handle);
if (perf_status != ERROR_SUCCESS)
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_016: [ single_performance_counter_create shall return NULL if any step fails. ]
LogError("Failure in PdhCollectQueryData, status = %" PRIx32"", perf_status);
result = NULL;
}
else
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_004: [ single_performance_counter_create shall call malloc to create a new SINGLE_PERFORMANCE_COUNTER struct. ]
result = malloc(sizeof(SINGLE_PERFORMANCE_COUNTER));
if (result == NULL)
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_016: [ single_performance_counter_create shall return NULL if any step fails. ]
LogError("Unable to allocate SINGLE_PERFORMANCE_COUNTER");
}
else
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_014: [ single_performance_counter_create shall save PDH_HQUERY handle, and counter HCOUNTER in the SINGLE_PERFORMANCE_COUNTER struct. ]
result->perf_query_handle = perf_query_handle;
result->current_counter = counter;
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_015: [ single_performance_counter_create shall return a non-null to the SINGLE_PERFORMANCE_COUNTER_HANDLE struct on success. ]
goto all_ok;
}
}
}
}
PdhCloseQuery(perf_query_handle);
}
}
all_ok:
return result;
}
IMPLEMENT_MOCKABLE_FUNCTION(, void, single_performance_counter_destroy, SINGLE_PERFORMANCE_COUNTER_HANDLE, handle)
{
if (handle == NULL)
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_017: [ single_performance_counter_destroy shall return if handle is NULL. ]
LogError("NULL handle");
}
else
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_018: [ single_performance_counter_destroy shall call PdhCloseQuery with the PDH_HQUERY handle. ]
PdhCloseQuery(handle->perf_query_handle);
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_019: [ single_performance_counter_destroy shall call free on the handle ]
free(handle);
}
}
IMPLEMENT_MOCKABLE_FUNCTION(, SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT, single_performance_counter_sample_double, SINGLE_PERFORMANCE_COUNTER_HANDLE, handle, double*, sample)
{
SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT result;
if
(
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_020: [ single_performance_counter_sample_double shall return SINGLE_PERFORMANCE_COUNTER_SAMPLE_ERROR if handle is NULL. ]
handle == NULL ||
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_027: [ single_performance_counter_sample_double shall return SINGLE_PERFORMANCE_COUNTER_SAMPLE_ERROR if sample is NULL. ]
sample == NULL
)
{
LogError("Invalid Arguments: SINGLE_PERFORMANCE_COUNTER_HANDLE handle = %p, double* sample = %p",
handle, sample);
result = SINGLE_PERFORMANCE_COUNTER_SAMPLE_ERROR;
}
else
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_021: [ single_performance_counter_sample_double shall call PdhCollectQueryData on the PDH_HQUERY handle. ]
PDH_STATUS perf_status = PdhCollectQueryData(handle->perf_query_handle);
if (perf_status != ERROR_SUCCESS)
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_022: [ single_performance_counter_sample_double shall return SINGLE_PERFORMANCE_COUNTER_SAMPLE_COLLECT_FAILED if PdhCollectQueryData fails. ]
LogError("Failure in PdhCollectQueryData, status = %" PRIx32 "", perf_status);
result = SINGLE_PERFORMANCE_COUNTER_SAMPLE_COLLECT_FAILED;
}
else
{
PDH_FMT_COUNTERVALUE formatted_value;
DWORD counter_type;
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_023: [ single_performance_counter_sample_double shall call PdhGetFormattedCounterValue on the counter HCOUNTER. ]
perf_status = PdhGetFormattedCounterValue(handle->current_counter,
PDH_FMT_DOUBLE,
&counter_type,
&formatted_value);
if (perf_status != ERROR_SUCCESS)
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_024: [ single_performance_counter_sample_double shall return SINGLE_PERFORMANCE_COUNTER_SAMPLE_FORMAT_FAILED if PdhGetFormattedCounterValue fails. ]
LogError("Failure in PdhGetFormattedCounterValue, status = %" PRIx32"", perf_status);
result = SINGLE_PERFORMANCE_COUNTER_SAMPLE_FORMAT_FAILED;
}
else
{
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_026: [ single_performance_counter_sample_double shall return SINGLE_PERFORMANCE_COUNTER_SAMPLE_SUCCESS on success. ]
result = SINGLE_PERFORMANCE_COUNTER_SAMPLE_SUCCESS;
// Codes_SRS_SINGLE_PERFORMANCE_COUNTER_45_025: [ single_performance_counter_sample_double shall set the sample to the double value given by PdhGetFormattedCounterValue. ]
*sample = formatted_value.doubleValue;
}
}
}
return result;
}

Просмотреть файл

@ -13,6 +13,7 @@ if(${run_unittests})
build_test_folder(reals_win32_ut)
build_test_folder(sysinfo_win32_ut)
build_test_folder(file_win32_ut)
build_test_folder(single_performance_counter_win32_ut)
build_test_folder(string_utils_win32_ut)
build_test_folder(uuid_win32_ut)

Просмотреть файл

@ -0,0 +1,17 @@
#Copyright (c) Microsoft. All rights reserved.
set(theseTestsName single_performance_counter_win32_ut)
set(${theseTestsName}_test_files
${theseTestsName}.c
)
set(${theseTestsName}_c_files
single_performance_counter_mocked.c
)
set(${theseTestsName}_h_files
../../../interfaces/inc/c_pal/single_performance_counter.h
)
build_test_artifacts(${theseTestsName} "tests/c_pal" ADDITIONAL_LIBS c_pal_reals c_pal_umocktypes)

Просмотреть файл

@ -0,0 +1,14 @@
// Copyright (c) Microsoft. All rights reserved.
#define GetModuleFileNameA mocked_GetModuleFileNameA
#define GetCurrentProcessId mocked_GetCurrentProcessId
#define PdhOpenQueryA mocked_PdhOpenQueryA
#define PdhAddCounterA mocked_PdhAddCounterA
#define PdhCollectQueryData mocked_PdhCollectQueryData
#define PdhCloseQuery mocked_PdhCloseQuery
#define PdhGetFormattedCounterValue mocked_PdhGetFormattedCounterValue
#include "windows.h"
#include "pdh.h"
#include "../../src/single_performance_counter_win32.c"

Просмотреть файл

@ -0,0 +1,537 @@
// Copyright (c) Microsoft. All rights reserved.
#include <stdlib.h>
#include <stddef.h>
#include <stdbool.h>
#include "windows.h"
#include "pdh.h"
#include "macro_utils/macro_utils.h"
#include "testrunnerswitcher.h"
#include "umock_c/umock_c.h"
#include "umock_c/umocktypes.h"
#include "umock_c/umocktypes_bool.h"
#include "umock_c/umocktypes_charptr.h"
#include "umock_c/umocktypes_stdint.h"
#include "umock_c/umocktypes_struct.h"
#include "umock_c/umock_c_negative_tests.h"
#define ENABLE_MOCKS
#include "c_pal/gballoc_hl.h"
#include "c_pal/gballoc_hl_redirect.h"
#include "c_pal/interlocked.h"
#include "c_pal/interlocked_hl.h"
#include "c_pal/srw_lock.h"
//
// Mocks for system calls
//
MOCKABLE_FUNCTION(WINAPI, DWORD, mocked_GetCurrentProcessId);
MOCKABLE_FUNCTION(WINAPI, DWORD, mocked_GetModuleFileNameA, HMODULE, hModule, LPSTR, lpFilename, DWORD, nSize);
MOCKABLE_FUNCTION(WINAPI, PDH_STATUS, mocked_PdhOpenQueryA, LPCSTR, szDataSource, DWORD*, dwUserData, PDH_HQUERY*, phQuery);
MOCKABLE_FUNCTION(WINAPI, PDH_STATUS, mocked_PdhAddCounterA, PDH_HQUERY, hQuery, LPCSTR, szFullCounterPath, DWORD*, dwUserData, PDH_HCOUNTER*, phCounter);
MOCKABLE_FUNCTION(WINAPI, PDH_STATUS, mocked_PdhCollectQueryData, PDH_HQUERY, hQuery);
MOCKABLE_FUNCTION(WINAPI, PDH_STATUS, mocked_PdhCloseQuery, PDH_HQUERY, hQuery);
MOCKABLE_FUNCTION(WINAPI, PDH_STATUS, mocked_PdhGetFormattedCounterValue, PDH_HCOUNTER, hCounter, DWORD, dwFormat, DWORD*, lpdwType, PPDH_FMT_COUNTERVALUE, pValue);
#undef ENABLE_MOCKS
// Must include umock_c_prod so mocks are not expanded in reals
#include "umock_c/umock_c_prod.h"
#include "real_gballoc_hl.h"
#include "real_interlocked.h"
#include "real_interlocked_hl.h"
#include "real_srw_lock.h"
#include "c_pal/single_performance_counter.h"
IMPLEMENT_UMOCK_C_ENUM_TYPE(SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT, SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT_VALUES);
//
// Hooks for system calls
//
#define DEFAULT_MOCKED_GETCURRENTPROCESSID_PID 1;
static DWORD mocked_GetCurrentProcessId_pid = DEFAULT_MOCKED_GETCURRENTPROCESSID_PID;
static DWORD hook_mocked_GetCurrentProcessId(void)
{
return mocked_GetCurrentProcessId_pid;
}
#define DEFAULT_MOCKED_GETMODULEFILENAMEA_FILENAME "c:\\test\\Azure.Messaging.ElasticLog.exe";
static char* mocked_mocked_GetModuleFileNameA_filename = DEFAULT_MOCKED_GETMODULEFILENAMEA_FILENAME;
static DWORD hook_mocked_GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize)
{
(void)hModule;
(void)nSize;
DWORD result = (DWORD)((mocked_mocked_GetModuleFileNameA_filename != NULL) ? strlen(mocked_mocked_GetModuleFileNameA_filename) : 0);
if (result >= MAX_PATH + 1)
{
result = MAX_PATH + 1;
}
else if (result != 0)
{
strcpy(lpFilename, mocked_mocked_GetModuleFileNameA_filename);
}
return result;
}
static PDH_STATUS hook_mocked_PdhOpenQueryA(LPCSTR szDataSource, DWORD* dwUserData, PDH_HQUERY* phQuery)
{
(void)szDataSource;
(void)dwUserData;
*phQuery = (PDH_HQUERY)real_gballoc_hl_malloc(1);
(void)phQuery;
return ERROR_SUCCESS;
}
static PDH_STATUS hook_mocked_PdhAddCounterA(PDH_HQUERY hQuery, LPCSTR szFullCounterPath, DWORD* dwUserData, PDH_HCOUNTER* phCounter)
{
(void)hQuery;
(void)szFullCounterPath;
(void)dwUserData;
(void)phCounter;
return ERROR_SUCCESS;
}
static PDH_STATUS hook_mocked_PdhCollectQueryData(PDH_HQUERY hQuery)
{
(void)hQuery;
return ERROR_SUCCESS;
}
static PDH_STATUS hook_mocked_PdhCloseQuery(PDH_HQUERY hQuery)
{
real_gballoc_hl_free((void*)hQuery);
return ERROR_SUCCESS;
}
static double mocked_PdhGetFormattedCounterValue_pValue_double = 0.1;
static PDH_STATUS hook_mocked_PdhGetFormattedCounterValue(PDH_HCOUNTER hCounter, DWORD dwFormat, DWORD* lpdwType, PPDH_FMT_COUNTERVALUE pValue)
{
(void)hCounter;
(void)dwFormat;
(void)lpdwType;
if (pValue != NULL)
{
pValue->doubleValue = mocked_PdhGetFormattedCounterValue_pValue_double;
}
return ERROR_SUCCESS;
}
static void default_single_performance_counter_create_mocks()
{
// "performance_object", "performance_counter"
// default path: "c:\\test\\Azure.Messaging.ElasticLog.exe", default pid = 1
char * default_expected_pdh_path = "\\performance_object(Azure.Messaging.ElasticLog:1)\\performance_counter";
STRICT_EXPECTED_CALL(mocked_PdhOpenQueryA(NULL, 0, IGNORED_ARG));
STRICT_EXPECTED_CALL(mocked_GetCurrentProcessId());
STRICT_EXPECTED_CALL(mocked_GetModuleFileNameA(NULL, IGNORED_ARG, MAX_PATH + 1));
STRICT_EXPECTED_CALL(mocked_PdhAddCounterA(IGNORED_ARG, default_expected_pdh_path, 0, IGNORED_ARG));
STRICT_EXPECTED_CALL(mocked_PdhCollectQueryData(IGNORED_ARG));
STRICT_EXPECTED_CALL(malloc(IGNORED_ARG));
}
static SINGLE_PERFORMANCE_COUNTER_HANDLE create_default_single_performance_counter()
{
default_single_performance_counter_create_mocks();
SINGLE_PERFORMANCE_COUNTER_HANDLE result = single_performance_counter_create("performance_object", "performance_counter");
ASSERT_IS_NOT_NULL(result);
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
return result;
}
MU_DEFINE_ENUM_STRINGS(UMOCK_C_ERROR_CODE, UMOCK_C_ERROR_CODE_VALUES)
static void on_umock_c_error(UMOCK_C_ERROR_CODE error_code)
{
ASSERT_FAIL("umock_c reported error :%" PRI_MU_ENUM "", MU_ENUM_VALUE(UMOCK_C_ERROR_CODE, error_code));
}
BEGIN_TEST_SUITE(TEST_SUITE_NAME_FROM_CMAKE)
TEST_SUITE_INITIALIZE(suite_init)
{
ASSERT_ARE_EQUAL(int, 0, real_gballoc_hl_init(NULL, NULL));
ASSERT_ARE_EQUAL(int, 0, umock_c_init(on_umock_c_error), "umock_c_init");
ASSERT_ARE_EQUAL(int, 0, umocktypes_bool_register_types(), "umocktypes_bool_register_types");
ASSERT_ARE_EQUAL(int, 0, umocktypes_stdint_register_types(), "umocktypes_charptr_register_types");
ASSERT_ARE_EQUAL(int, 0, umocktypes_charptr_register_types(), "umocktypes_charptr_register_types");
REGISTER_UMOCK_ALIAS_TYPE(SINGLE_PERFORMANCE_COUNTER_HANDLE, void*);
REGISTER_UMOCK_ALIAS_TYPE(SRW_LOCK_HANDLE, void*);
REGISTER_UMOCK_ALIAS_TYPE(PDH_HQUERY, void*);
REGISTER_UMOCK_ALIAS_TYPE(HCOUNTER, void*);
REGISTER_UMOCK_ALIAS_TYPE(PDH_HCOUNTER, void*);
REGISTER_UMOCK_ALIAS_TYPE(HMODULE, void*);
REGISTER_UMOCK_ALIAS_TYPE(PPDH_FMT_COUNTERVALUE, void*);
REGISTER_UMOCK_ALIAS_TYPE(LPSTR, char*);
REGISTER_UMOCK_ALIAS_TYPE(LPCSTR, const char*);
REGISTER_UMOCK_ALIAS_TYPE(DWORD, uint32_t);
REGISTER_UMOCK_ALIAS_TYPE(PDH_STATUS, int);
REGISTER_GBALLOC_HL_GLOBAL_MOCK_HOOK();
REGISTER_SRW_LOCK_GLOBAL_MOCK_HOOK();
REGISTER_TYPE(SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT, SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT);
REGISTER_GLOBAL_MOCK_FAIL_RETURN(malloc, NULL);
REGISTER_GLOBAL_MOCK_FAIL_RETURN(mocked_GetModuleFileNameA, 0);
REGISTER_GLOBAL_MOCK_FAIL_RETURN(mocked_PdhOpenQueryA, ERROR_BAD_FORMAT);
REGISTER_GLOBAL_MOCK_FAIL_RETURN(mocked_PdhAddCounterA, ERROR_BAD_FORMAT);
REGISTER_GLOBAL_MOCK_FAIL_RETURN(mocked_PdhCollectQueryData, ERROR_BAD_FORMAT);
REGISTER_GLOBAL_MOCK_FAIL_RETURN(mocked_PdhGetFormattedCounterValue, ERROR_BAD_FORMAT);
REGISTER_GLOBAL_MOCK_HOOK(mocked_GetModuleFileNameA, hook_mocked_GetModuleFileNameA);
REGISTER_GLOBAL_MOCK_HOOK(mocked_GetCurrentProcessId, hook_mocked_GetCurrentProcessId);
REGISTER_GLOBAL_MOCK_HOOK(mocked_PdhOpenQueryA, hook_mocked_PdhOpenQueryA);
REGISTER_GLOBAL_MOCK_HOOK(mocked_PdhAddCounterA, hook_mocked_PdhAddCounterA);
REGISTER_GLOBAL_MOCK_HOOK(mocked_PdhCollectQueryData, hook_mocked_PdhCollectQueryData);
REGISTER_GLOBAL_MOCK_HOOK(mocked_PdhCloseQuery, hook_mocked_PdhCloseQuery);
REGISTER_GLOBAL_MOCK_HOOK(mocked_PdhGetFormattedCounterValue, hook_mocked_PdhGetFormattedCounterValue);
}
TEST_SUITE_CLEANUP(suite_cleanup)
{
umock_c_deinit();
real_gballoc_hl_deinit();
}
TEST_FUNCTION_INITIALIZE(method_init)
{
umock_c_reset_all_calls();
umock_c_negative_tests_init();
// reset the mocked value returns to default
mocked_mocked_GetModuleFileNameA_filename = DEFAULT_MOCKED_GETMODULEFILENAMEA_FILENAME;
mocked_GetCurrentProcessId_pid = DEFAULT_MOCKED_GETCURRENTPROCESSID_PID;
}
TEST_FUNCTION_CLEANUP(method_cleanup)
{
umock_c_negative_tests_deinit();
}
//
// single_performance_counter_create
//
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_002: [ single_performance_counter_create shall return NULL if performance_object is NULL. ]
TEST_FUNCTION(single_performance_counter_create_returns_null_if_performance_object_is_null)
{
// arrange
// act
SINGLE_PERFORMANCE_COUNTER_HANDLE result = single_performance_counter_create(NULL, "performance_counter");
// assert
ASSERT_IS_NULL(result);
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
// ablution
}
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_003: [ single_performance_counter_create shall return NULL if performance_counter is NULL. ]
TEST_FUNCTION(single_performance_counter_create_returns_null_if_performance_counter_is_null)
{
// arrange
// act
SINGLE_PERFORMANCE_COUNTER_HANDLE result = single_performance_counter_create("performance_object", NULL);
// assert
ASSERT_IS_NULL(result);
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
// ablution
}
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_004 : [single_performance_counter_create shall call malloc to create a new SINGLE_PERFORMANCE_COUNTER struct.]
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_005 : [single_performance_counter_create shall call GetCurrentProcessId to get the current process id.]
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_006 : [single_performance_counter_create shall call GetModuleFileNameA to get the current executable path.]
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_007 : [single_performance_counter_create shall call strrchr on the executable path with '\' to get the executable name. ]
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_008 : [single_performance_counter_create shall call strrchr on the executable name with '.' to get the executable name without extension.]
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_009 : [single_performance_counter_create shall call snprintf to copy the executable name without extension.]
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_010 : [single_performance_counter_create shall call snprintf to copy the performance_object, performance_counter, pid, and the executable name without extension into a single string.]
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_011 : [single_performance_counter_create shall call PdhOpenQueryA to create a new query.]
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_012 : [single_performance_counter_create shall call PdhAddCounterA to add the counter to the query.]
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_013 : [single_performance_counter_create shall call PdhCollectQueryData to prime the query.]
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_014 : [single_performance_counter_create shall save PDH_HQUERY handle, and counter HCOUNTER in the SINGLE_PERFORMANCE_COUNTER struct.]
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_015 : [single_performance_counter_create shall return a non - null to the SINGLE_PERFORMANCE_COUNTER_HANDLE struct on success.]
TEST_FUNCTION(single_performance_counter_create_succeeds)
{
// arrange
default_single_performance_counter_create_mocks();
// act
SINGLE_PERFORMANCE_COUNTER_HANDLE result = single_performance_counter_create("performance_object", "performance_counter");
// assert
ASSERT_IS_NOT_NULL(result);
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
// ablution
single_performance_counter_destroy(result);
}
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_016: [ single_performance_counter_create shall return NULL if any step fails. ]
TEST_FUNCTION(single_performance_counter_create_negative_tests)
{
// arrange
PDH_HQUERY* perf_query_handle;
STRICT_EXPECTED_CALL(mocked_PdhOpenQueryA(NULL, 0, IGNORED_ARG)).CaptureArgumentValue_phQuery(&perf_query_handle);
STRICT_EXPECTED_CALL(mocked_GetCurrentProcessId()).CallCannotFail();
STRICT_EXPECTED_CALL(mocked_GetModuleFileNameA(NULL, IGNORED_ARG, IGNORED_ARG));
STRICT_EXPECTED_CALL(mocked_PdhAddCounterA(IGNORED_ARG, IGNORED_ARG, 0, IGNORED_ARG));
STRICT_EXPECTED_CALL(mocked_PdhCollectQueryData(IGNORED_ARG));
STRICT_EXPECTED_CALL(malloc(IGNORED_ARG));
umock_c_negative_tests_snapshot();
size_t i = 0;
for (i = 0; i < umock_c_negative_tests_call_count(); i++)
{
if (umock_c_negative_tests_can_call_fail(i))
{
umock_c_negative_tests_reset();
umock_c_negative_tests_fail_call(i);
// act
SINGLE_PERFORMANCE_COUNTER_HANDLE result = single_performance_counter_create("performance_object", "performance_counter");
// assert
ASSERT_IS_NULL(result);
// ablution
}
}
}
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_016: [ single_performance_counter_create shall return NULL if any step fails. ]
TEST_FUNCTION(single_performance_counter_create_module_name_too_large)
{
// arrange
char new_mocked_name[MAX_PATH + 2];
memset(new_mocked_name, 'a', MAX_PATH + 2);
new_mocked_name[0] = '\\'; // slash to make it a full path
new_mocked_name[MAX_PATH + 1] = '\0'; // null terminate
mocked_mocked_GetModuleFileNameA_filename = new_mocked_name;
STRICT_EXPECTED_CALL(mocked_PdhOpenQueryA(NULL, 0, IGNORED_ARG));
STRICT_EXPECTED_CALL(mocked_GetCurrentProcessId());
STRICT_EXPECTED_CALL(mocked_GetModuleFileNameA(NULL, IGNORED_ARG, IGNORED_ARG));
STRICT_EXPECTED_CALL(mocked_PdhCloseQuery(IGNORED_ARG));
// act
SINGLE_PERFORMANCE_COUNTER_HANDLE result = single_performance_counter_create("performance_object", "performance_counter");
// assert
ASSERT_IS_NULL(result);
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
//ablution
}
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_007 : [single_performance_counter_create shall call strrchr on the executable path with '\' to get the executable name. ]
TEST_FUNCTION(single_performance_counter_create_app_name_unexpectedly_not_full_path)
{
// arrange
char* new_mocked_name = "name_without_a_slash.exe";
mocked_mocked_GetModuleFileNameA_filename = new_mocked_name;
STRICT_EXPECTED_CALL(mocked_PdhOpenQueryA(NULL, 0, IGNORED_ARG));
STRICT_EXPECTED_CALL(mocked_GetCurrentProcessId());
STRICT_EXPECTED_CALL(mocked_GetModuleFileNameA(NULL, IGNORED_ARG, IGNORED_ARG));
STRICT_EXPECTED_CALL(mocked_PdhCloseQuery(IGNORED_ARG));
// act
SINGLE_PERFORMANCE_COUNTER_HANDLE result = single_performance_counter_create("performance_object", "performance_counter");
// assert
ASSERT_IS_NULL(result);
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
//ablution
}
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_008 : [single_performance_counter_create shall call strrchr on the executable name with '.' to get the executable name without extension.]
TEST_FUNCTION(single_performance_counter_create_app_name_unexpectedly_has_no_extension)
{
// arrange
char* new_mocked_name = "\\name_without_extension";
mocked_mocked_GetModuleFileNameA_filename = new_mocked_name;
STRICT_EXPECTED_CALL(mocked_PdhOpenQueryA(NULL, 0, IGNORED_ARG));
STRICT_EXPECTED_CALL(mocked_GetCurrentProcessId());
STRICT_EXPECTED_CALL(mocked_GetModuleFileNameA(NULL, IGNORED_ARG, IGNORED_ARG));
STRICT_EXPECTED_CALL(mocked_PdhCloseQuery(IGNORED_ARG));
// act
SINGLE_PERFORMANCE_COUNTER_HANDLE result = single_performance_counter_create("performance_object", "performance_counter");
// assert
ASSERT_IS_NULL(result);
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
//ablution
}
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_016: [ single_performance_counter_create shall return NULL if any step fails. ]
TEST_FUNCTION(single_performance_counter_create_module_name_makes_perf_path_too_large)
{
// arrange
char new_mocked_name[MAX_PATH + 1];
memset(new_mocked_name, 'a', MAX_PATH + 1);
new_mocked_name[0] = '\\'; // slash to make it a full path
new_mocked_name[MAX_PATH -1] = '.'; // dot to give it an extension
new_mocked_name[MAX_PATH ] = '\0'; // null terminate
mocked_mocked_GetModuleFileNameA_filename = new_mocked_name;
STRICT_EXPECTED_CALL(mocked_PdhOpenQueryA(NULL, 0, IGNORED_ARG));
STRICT_EXPECTED_CALL(mocked_GetCurrentProcessId());
STRICT_EXPECTED_CALL(mocked_GetModuleFileNameA(NULL, IGNORED_ARG, IGNORED_ARG));
STRICT_EXPECTED_CALL(mocked_PdhCloseQuery(IGNORED_ARG));
// act
SINGLE_PERFORMANCE_COUNTER_HANDLE result = single_performance_counter_create("performance_object", "performance_counter");
// assert
ASSERT_IS_NULL(result);
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
//ablution
}
//
// single_performance_counter_destroy
//
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_017: [ single_performance_counter_destroy shall return if handle is NULL. ]
TEST_FUNCTION(single_performance_counter_destroy_returns_if_handle_is_null)
{
// arrange
// act
single_performance_counter_destroy(NULL);
// assert
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
// ablution
}
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_018: [ single_performance_counter_destroy shall call PdhCloseQuery with the PDH_HQUERY handle. ]
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_019: [ single_performance_counter_destroy shall call free on the handle ]
TEST_FUNCTION(single_performance_counter_destroy_succeeds)
{
// arrange
SINGLE_PERFORMANCE_COUNTER_HANDLE handle = create_default_single_performance_counter();
STRICT_EXPECTED_CALL(mocked_PdhCloseQuery(IGNORED_ARG));
STRICT_EXPECTED_CALL(free(IGNORED_ARG));
// act
single_performance_counter_destroy(handle);
// assert
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
// ablution
}
//
// single_performance_counter_sample_double
//
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_020: [ single_performance_counter_sample_double shall return SINGLE_PERFORMANCE_COUNTER_SAMPLE_ERROR if handle is NULL. ]
TEST_FUNCTION(single_performance_counter_sample_double_returns_error_if_handle_is_null)
{
// arrange
// act
double sample;
SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT result = single_performance_counter_sample_double(NULL, &sample);
// assert
ASSERT_ARE_EQUAL(int, SINGLE_PERFORMANCE_COUNTER_SAMPLE_ERROR, result);
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
// ablution
}
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_027: [ single_performance_counter_sample_double shall return SINGLE_PERFORMANCE_COUNTER_SAMPLE_ERROR if sample is NULL. ]
TEST_FUNCTION(single_performance_counter_sample_double_returns_error_if_sample_is_null)
{
// arrange
SINGLE_PERFORMANCE_COUNTER_HANDLE handle = create_default_single_performance_counter();
// act
SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT result = single_performance_counter_sample_double(handle, NULL);
// assert
ASSERT_ARE_EQUAL(int, SINGLE_PERFORMANCE_COUNTER_SAMPLE_ERROR, result);
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
// ablution
single_performance_counter_destroy(handle);
}
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_021: [ single_performance_counter_sample_double shall call PdhCollectQueryData on the PDH_HQUERY handle. ]
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_023: [ single_performance_counter_sample_double shall call PdhGetFormattedCounterValue on the counter HCOUNTER. ]
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_025: [ single_performance_counter_sample_double shall set the sample to the double value given by PdhGetFormattedCounterValue. ]
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_026: [ single_performance_counter_sample_double shall return SINGLE_PERFORMANCE_COUNTER_SAMPLE_SUCCESS on success. ]
TEST_FUNCTION(single_performance_counter_sample_double_succeeds)
{
// arrange
SINGLE_PERFORMANCE_COUNTER_HANDLE handle = create_default_single_performance_counter();
STRICT_EXPECTED_CALL(mocked_PdhCollectQueryData(IGNORED_ARG));
STRICT_EXPECTED_CALL(mocked_PdhGetFormattedCounterValue(IGNORED_ARG, PDH_FMT_DOUBLE, IGNORED_ARG, IGNORED_ARG));
// act
double sample;
SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT result = single_performance_counter_sample_double(handle, &sample);
// assert
ASSERT_ARE_EQUAL(int, SINGLE_PERFORMANCE_COUNTER_SAMPLE_SUCCESS, result);
ASSERT_ARE_EQUAL(double, mocked_PdhGetFormattedCounterValue_pValue_double, sample);
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
// ablution
single_performance_counter_destroy(handle);
}
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_022: [ single_performance_counter_sample_double shall return SINGLE_PERFORMANCE_COUNTER_SAMPLE_COLLECT_FAILED if PdhCollectQueryData fails. ]
// Tests_SRS_SINGLE_PERFORMANCE_COUNTER_45_024: [ single_performance_counter_sample_double shall return SINGLE_PERFORMANCE_COUNTER_SAMPLE_FORMAT_FAILED if PdhGetFormattedCounterValue fails. ]
TEST_FUNCTION(single_performance_counter_sample_double_negative_tests)
{
// arrange
SINGLE_PERFORMANCE_COUNTER_HANDLE handle = create_default_single_performance_counter();
umock_c_reset_all_calls();
STRICT_EXPECTED_CALL(mocked_PdhCollectQueryData(IGNORED_ARG));
STRICT_EXPECTED_CALL(mocked_PdhGetFormattedCounterValue(IGNORED_ARG, PDH_FMT_DOUBLE, IGNORED_ARG, IGNORED_ARG));
umock_c_negative_tests_snapshot();
size_t i = 0;
for (i = 0; i < umock_c_negative_tests_call_count(); i++)
{
if (umock_c_negative_tests_can_call_fail(i))
{
umock_c_negative_tests_reset();
umock_c_negative_tests_fail_call(i);
// act
double sample;
SINGLE_PERFORMANCE_COUNTER_SAMPLE_RESULT result = single_performance_counter_sample_double(handle, &sample);
// assert
ASSERT_ARE_NOT_EQUAL(int, result, SINGLE_PERFORMANCE_COUNTER_SAMPLE_SUCCESS);
}
}
// ablution
single_performance_counter_destroy(handle);
}
END_TEST_SUITE(TEST_SUITE_NAME_FROM_CMAKE)