зеркало из https://github.com/Azure/c-pal.git
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:
Родитель
96df0cfbf2
Коммит
30a0dd6fde
|
@ -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)
|
Загрузка…
Ссылка в новой задаче