diff options
Diffstat (limited to 'src/collectors/windows.plugin/perflib-mssql.c')
-rw-r--r-- | src/collectors/windows.plugin/perflib-mssql.c | 1413 |
1 files changed, 1413 insertions, 0 deletions
diff --git a/src/collectors/windows.plugin/perflib-mssql.c b/src/collectors/windows.plugin/perflib-mssql.c new file mode 100644 index 000000000..f6f5c434d --- /dev/null +++ b/src/collectors/windows.plugin/perflib-mssql.c @@ -0,0 +1,1413 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "windows_plugin.h" +#include "windows-internals.h" + +// https://learn.microsoft.com/en-us/sql/sql-server/install/instance-configuration?view=sql-server-ver16 +#define NETDATA_MAX_INSTANCE_NAME 32 +#define NETDATA_MAX_INSTANCE_OBJECT 128 + +BOOL is_sqlexpress = FALSE; + +enum netdata_mssql_metrics { + NETDATA_MSSQL_GENERAL_STATS, + NETDATA_MSSQL_SQL_ERRORS, + NETDATA_MSSQL_DATABASE, + NETDATA_MSSQL_LOCKS, + NETDATA_MSSQL_MEMORY, + NETDATA_MSSQL_BUFFER_MANAGEMENT, + NETDATA_MSSQL_SQL_STATS, + NETDATA_MSSQL_ACCESS_METHODS, + + NETDATA_MSSQL_METRICS_END +}; + +struct mssql_instance { + char *instanceID; + + char *objectName[NETDATA_MSSQL_METRICS_END]; + + RRDSET *st_user_connections; + RRDDIM *rd_user_connections; + + RRDSET *st_process_blocked; + RRDDIM *rd_process_blocked; + + RRDSET *st_stats_auto_param; + RRDDIM *rd_stats_auto_param; + + RRDSET *st_stats_batch_request; + RRDDIM *rd_stats_batch_request; + + RRDSET *st_stats_safe_auto; + RRDDIM *rd_stats_safe_auto; + + RRDSET *st_stats_compilation; + RRDDIM *rd_stats_compilation; + + RRDSET *st_stats_recompiles; + RRDDIM *rd_stats_recompiles; + + RRDSET *st_buff_cache_hits; + RRDDIM *rd_buff_cache_hits; + + RRDSET *st_buff_cache_page_life_expectancy; + RRDDIM *rd_buff_cache_page_life_expectancy; + + RRDSET *st_buff_checkpoint_pages; + RRDDIM *rd_buff_checkpoint_pages; + + RRDSET *st_buff_page_iops; + RRDDIM *rd_buff_page_reads; + RRDDIM *rd_buff_page_writes; + + RRDSET *st_access_method_page_splits; + RRDDIM *rd_access_method_page_splits; + + RRDSET *st_sql_errors; + RRDDIM *rd_sql_errors; + + RRDSET *st_lockWait; + RRDSET *st_deadLocks; + DICTIONARY *locks_instances; + + DICTIONARY *databases; + + RRDSET *st_conn_memory; + RRDDIM *rd_conn_memory; + + RRDSET *st_ext_benefit_mem; + RRDDIM *rd_ext_benefit_mem; + + RRDSET *st_pending_mem_grant; + RRDDIM *rd_pending_mem_grant; + + RRDSET *st_mem_tot_server; + RRDDIM *rd_mem_tot_server; + + COUNTER_DATA MSSQLAccessMethodPageSplits; + COUNTER_DATA MSSQLBufferCacheHits; + COUNTER_DATA MSSQLBufferCheckpointPages; + COUNTER_DATA MSSQLBufferPageLifeExpectancy; + COUNTER_DATA MSSQLBufferPageReads; + COUNTER_DATA MSSQLBufferPageWrites; + COUNTER_DATA MSSQLBlockedProcesses; + COUNTER_DATA MSSQLUserConnections; + COUNTER_DATA MSSQLConnectionMemoryBytes; + COUNTER_DATA MSSQLExternalBenefitOfMemory; + COUNTER_DATA MSSQLPendingMemoryGrants; + COUNTER_DATA MSSQLSQLErrorsTotal; + COUNTER_DATA MSSQLTotalServerMemory; + COUNTER_DATA MSSQLStatsAutoParameterization; + COUNTER_DATA MSSQLStatsBatchRequests; + COUNTER_DATA MSSQLStatSafeAutoParameterization; + COUNTER_DATA MSSQLCompilations; + COUNTER_DATA MSSQLRecompilations; +}; + +enum lock_instance_idx { + NETDATA_MSSQL_ENUM_MLI_IDX_WAIT, + NETDATA_MSSQL_ENUM_MLI_IDX_DEAD_LOCKS, + + NETDATA_MSSQL_ENUM_MLI_IDX_END +}; + +struct mssql_lock_instance { + struct mssql_instance *parent; + + COUNTER_DATA lockWait; + COUNTER_DATA deadLocks; + + RRDDIM *rd_lockWait; + RRDDIM *rd_deadLocks; + + uint32_t updated; +}; + +enum db_instance_idx { + NETDATA_MSSQL_ENUM_MDI_IDX_FILE_SIZE, + NETDATA_MSSQL_ENUM_MDI_IDX_ACTIVE_TRANSACTIONS, + NETDATA_MSSQL_ENUM_MDI_IDX_BACKUP_RESTORE_OP, + NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHED, + NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHES, + NETDATA_MSSQL_ENUM_MDI_IDX_TRANSACTIONS, + NETDATA_MSSQL_ENUM_MDI_IDX_WRITE_TRANSACTIONS, + + NETDATA_MSSQL_ENUM_MDI_IDX_END +}; + +struct mssql_db_instance { + struct mssql_instance *parent; + + RRDSET *st_db_data_file_size; + RRDSET *st_db_active_transactions; + RRDSET *st_db_backup_restore_operations; + RRDSET *st_db_log_flushed; + RRDSET *st_db_log_flushes; + RRDSET *st_db_transactions; + RRDSET *st_db_write_transactions; + + RRDDIM *rd_db_data_file_size; + RRDDIM *rd_db_active_transactions; + RRDDIM *rd_db_backup_restore_operations; + RRDDIM *rd_db_log_flushed; + RRDDIM *rd_db_log_flushes; + RRDDIM *rd_db_transactions; + RRDDIM *rd_db_write_transactions; + + COUNTER_DATA MSSQLDatabaseActiveTransactions; + COUNTER_DATA MSSQLDatabaseBackupRestoreOperations; + COUNTER_DATA MSSQLDatabaseDataFileSize; + COUNTER_DATA MSSQLDatabaseLogFlushed; + COUNTER_DATA MSSQLDatabaseLogFlushes; + COUNTER_DATA MSSQLDatabaseTransactions; + COUNTER_DATA MSSQLDatabaseWriteTransactions; + + uint32_t updated; +}; + +static DICTIONARY *mssql_instances = NULL; + +static void initialize_mssql_objects(struct mssql_instance *p, const char *instance) { + char prefix[NETDATA_MAX_INSTANCE_NAME]; + if (!strcmp(instance, "MSSQLSERVER")) { + strncpyz(prefix, "SQLServer:", sizeof(prefix) - 1); + } else if (!strcmp(instance, "SQLEXPRESS")) { + strncpyz(prefix, "MSSQL$SQLEXPRESS:", sizeof(prefix) - 1); + } else { + char *express = (!is_sqlexpress) ? "" : "SQLEXPRESS"; + snprintfz(prefix, sizeof(prefix) - 1, "MSSQL$%s:%s:", express, instance); + } + + size_t length = strlen(prefix); + char name[NETDATA_MAX_INSTANCE_OBJECT]; + snprintfz(name, sizeof(name) - 1, "%s%s", prefix, "General Statistics"); + p->objectName[NETDATA_MSSQL_GENERAL_STATS] = strdup(name); + + strncpyz(&name[length], "SQL Errors", sizeof(name) - length); + p->objectName[NETDATA_MSSQL_SQL_ERRORS] = strdup(name); + + strncpyz(&name[length], "Databases", sizeof(name) - length); + p->objectName[NETDATA_MSSQL_DATABASE] = strdup(name); + + strncpyz(&name[length], "SQL Statistics", sizeof(name) - length); + p->objectName[NETDATA_MSSQL_SQL_STATS] = strdup(name); + + strncpyz(&name[length], "Buffer Manager", sizeof(name) - length); + p->objectName[NETDATA_MSSQL_BUFFER_MANAGEMENT] = strdup(name); + + strncpyz(&name[length], "Memory Manager", sizeof(name) - length); + p->objectName[NETDATA_MSSQL_MEMORY] = strdup(name); + + strncpyz(&name[length], "Locks", sizeof(name) - length); + p->objectName[NETDATA_MSSQL_LOCKS] = strdup(name); + + strncpyz(&name[length], "Access Methods", sizeof(name) - length); + p->objectName[NETDATA_MSSQL_ACCESS_METHODS] = strdup(name); + + p->instanceID = strdup(instance); +} + +static inline void initialize_mssql_keys(struct mssql_instance *p) { + // General Statistics + p->MSSQLUserConnections.key = "User Connections"; + p->MSSQLBlockedProcesses.key = "Processes blocked"; + + // SQL Statistics + p->MSSQLStatsAutoParameterization.key = "Auto-Param Attempts/sec"; + p->MSSQLStatsBatchRequests.key = "Batch Requests/sec"; + p->MSSQLStatSafeAutoParameterization.key = "Safe Auto-Params/sec"; + p->MSSQLCompilations.key = "SQL Compilations/sec"; + p->MSSQLRecompilations.key = "SQL Re-Compilations/sec"; + + // Buffer Management + p->MSSQLBufferCacheHits.key = "Buffer cache hit ratio"; + p->MSSQLBufferPageLifeExpectancy.key = "Page life expectancy"; + p->MSSQLBufferCheckpointPages.key = "Checkpoint pages/sec"; + p->MSSQLBufferPageReads.key = "Page reads/sec"; + p->MSSQLBufferPageWrites.key = "Page writes/sec"; + + // Access Methods + p->MSSQLAccessMethodPageSplits.key = "Page Splits/sec"; + + // Errors + p->MSSQLSQLErrorsTotal.key = "Errors/sec"; + + p->MSSQLConnectionMemoryBytes.key = "Connection Memory (KB)"; + p->MSSQLExternalBenefitOfMemory.key = "External benefit of memory"; + p->MSSQLPendingMemoryGrants.key = "Memory Grants Pending"; + p->MSSQLTotalServerMemory.key = "Total Server Memory (KB)"; +} + +void dict_mssql_insert_locks_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct mssql_lock_instance *ptr = value; + ptr->deadLocks.key = "Number of Deadlocks/sec"; + ptr->lockWait.key = "Lock Waits/sec"; +} + +void dict_mssql_insert_databases_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct mssql_db_instance *ptr = value; + + ptr->MSSQLDatabaseDataFileSize.key = "Data File(s) Size (KB)"; + ptr->MSSQLDatabaseActiveTransactions.key = "Active Transactions"; + ptr->MSSQLDatabaseBackupRestoreOperations.key = "Backup/Restore Throughput/sec"; + ptr->MSSQLDatabaseLogFlushed.key = "Log Bytes Flushed/sec"; + ptr->MSSQLDatabaseLogFlushes.key = "Log Flushes/sec"; + ptr->MSSQLDatabaseTransactions.key = "Transactions/sec"; + ptr->MSSQLDatabaseWriteTransactions.key = "Write Transactions/sec"; +} + +void dict_mssql_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct mssql_instance *p = value; + const char *instance = dictionary_acquired_item_name((DICTIONARY_ITEM *)item); + + if (!p->locks_instances) { + p->locks_instances = dictionary_create_advanced( + DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct mssql_lock_instance)); + dictionary_register_insert_callback(p->locks_instances, dict_mssql_insert_locks_cb, NULL); + } + + if (!p->databases) { + p->databases = dictionary_create_advanced( + DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct mssql_db_instance)); + dictionary_register_insert_callback(p->databases, dict_mssql_insert_databases_cb, NULL); + } + + initialize_mssql_objects(p, instance); + initialize_mssql_keys(p); +} + +static int mssql_fill_dictionary() { + HKEY hKey; + LSTATUS ret = RegOpenKeyExA( + HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Microsoft SQL Server\\Instance Names\\SQL", 0, KEY_READ, &hKey); + if (ret != ERROR_SUCCESS) + return -1; + + DWORD values = 0; + + ret = RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &values, NULL, NULL, NULL, NULL); + if (ret != ERROR_SUCCESS) { + goto endMSSQLFillDict; + } + + if (!values) { + ret = ERROR_PATH_NOT_FOUND; + goto endMSSQLFillDict; + } + +// https://learn.microsoft.com/en-us/windows/win32/sysinfo/enumerating-registry-subkeys +#define REGISTRY_MAX_VALUE 16383 + + DWORD i; + char avalue[REGISTRY_MAX_VALUE] = {'\0'}; + DWORD length = REGISTRY_MAX_VALUE; + for (i = 0; i < values; i++) { + avalue[0] = '\0'; + + ret = RegEnumValue(hKey, i, avalue, &length, NULL, NULL, NULL, NULL); + if (ret != ERROR_SUCCESS) + continue; + + if (!strcmp(avalue, "SQLEXPRESS")) { + is_sqlexpress = TRUE; + } + + struct mssql_instance *p = dictionary_set(mssql_instances, avalue, NULL, sizeof(*p)); + } + +endMSSQLFillDict: + RegCloseKey(hKey); + + return (ret == ERROR_SUCCESS) ? 0 : -1; +} + +static int initialize(void) { + mssql_instances = dictionary_create_advanced( + DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct mssql_instance)); + + dictionary_register_insert_callback(mssql_instances, dict_mssql_insert_cb, NULL); + + if (mssql_fill_dictionary()) { + return -1; + } + + return 0; +} + +static void do_mssql_general_stats(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *p, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->objectName[NETDATA_MSSQL_GENERAL_STATS]); + if (!pObjectType) + return; + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLUserConnections)) { + if (!p->st_user_connections) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_user_connections", p->instanceID); + netdata_fix_chart_name(id); + p->st_user_connections = rrdset_create_localhost( + "mssql", + id, + NULL, + "connections", + "mssql.instance_user_connections", + "User connections", + "connections", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_USER_CONNECTIONS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_user_connections = rrddim_add(p->st_user_connections, "user", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_user_connections->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_user_connections, p->rd_user_connections, (collected_number)p->MSSQLUserConnections.current.Data); + rrdset_done(p->st_user_connections); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLBlockedProcesses)) { + if (!p->st_process_blocked) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_blocked_process", p->instanceID); + netdata_fix_chart_name(id); + p->st_process_blocked = rrdset_create_localhost( + "mssql", + id, + NULL, + "processes", + "mssql.instance_blocked_processes", + "Blocked processes", + "process", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_BLOCKED_PROCESSES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_process_blocked = rrddim_add(p->st_process_blocked, "blocked", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_process_blocked->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_process_blocked, p->rd_process_blocked, (collected_number)p->MSSQLBlockedProcesses.current.Data); + rrdset_done(p->st_process_blocked); + } +} + +static void do_mssql_sql_statistics(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *p, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->objectName[NETDATA_MSSQL_SQL_STATS]); + if (!pObjectType) + return; + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLStatsAutoParameterization)) { + if (!p->st_stats_auto_param) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_sqlstats_auto_parameterization_attempts", p->instanceID); + netdata_fix_chart_name(id); + p->st_stats_auto_param = rrdset_create_localhost( + "mssql", + id, + NULL, + "sql activity", + "mssql.instance_sqlstats_auto_parameterization_attempts", + "Failed auto-parameterization attempts", + "attempts/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_STATS_AUTO_PARAMETRIZATION, + update_every, + RRDSET_TYPE_LINE); + + p->rd_stats_auto_param = + rrddim_add(p->st_stats_auto_param, "failed", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_stats_auto_param->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_stats_auto_param, + p->rd_stats_auto_param, + (collected_number)p->MSSQLStatsAutoParameterization.current.Data); + rrdset_done(p->st_stats_auto_param); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLStatsBatchRequests)) { + if (!p->st_stats_batch_request) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_sqlstats_batch_requests", p->instanceID); + netdata_fix_chart_name(id); + p->st_stats_batch_request = rrdset_create_localhost( + "mssql", + id, + NULL, + "sql activity", + "mssql.instance_sqlstats_batch_requests", + "Total of batches requests", + "requests/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_STATS_BATCH_REQUEST, + update_every, + RRDSET_TYPE_LINE); + + p->rd_stats_batch_request = + rrddim_add(p->st_stats_batch_request, "batch", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_stats_batch_request->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_stats_batch_request, + p->rd_stats_batch_request, + (collected_number)p->MSSQLStatsBatchRequests.current.Data); + rrdset_done(p->st_stats_batch_request); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLStatSafeAutoParameterization)) { + if (!p->st_stats_safe_auto) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_sqlstats_safe_auto_parameterization_attempts", p->instanceID); + netdata_fix_chart_name(id); + p->st_stats_safe_auto = rrdset_create_localhost( + "mssql", + id, + NULL, + "sql activity", + "mssql.instance_sqlstats_safe_auto_parameterization_attempts", + "Safe auto-parameterization attempts", + "attempts/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_STATS_SAFE_AUTO_PARAMETRIZATION, + update_every, + RRDSET_TYPE_LINE); + + p->rd_stats_safe_auto = rrddim_add(p->st_stats_safe_auto, "safe", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_stats_safe_auto->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_stats_safe_auto, + p->rd_stats_safe_auto, + (collected_number)p->MSSQLStatSafeAutoParameterization.current.Data); + rrdset_done(p->st_stats_safe_auto); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLCompilations)) { + if (!p->st_stats_compilation) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_sqlstats_sql_compilations", p->instanceID); + netdata_fix_chart_name(id); + p->st_stats_compilation = rrdset_create_localhost( + "mssql", + id, + NULL, + "sql activity", + "mssql.instance_sqlstats_sql_compilations", + "SQL compilations", + "compilations/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_STATS_COMPILATIONS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_stats_compilation = + rrddim_add(p->st_stats_compilation, "compilations", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_stats_compilation->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_stats_compilation, p->rd_stats_compilation, (collected_number)p->MSSQLCompilations.current.Data); + rrdset_done(p->st_stats_compilation); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLRecompilations)) { + if (!p->st_stats_recompiles) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_sqlstats_sql_recompilations", p->instanceID); + netdata_fix_chart_name(id); + p->st_stats_recompiles = rrdset_create_localhost( + "mssql", + id, + NULL, + "sql activity", + "mssql.instance_sqlstats_sql_recompilations", + "SQL re-compilations", + "recompiles/", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_STATS_RECOMPILATIONS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_stats_recompiles = + rrddim_add(p->st_stats_recompiles, "recompiles", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_stats_recompiles->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_stats_recompiles, p->rd_stats_recompiles, (collected_number)p->MSSQLRecompilations.current.Data); + rrdset_done(p->st_stats_recompiles); + } +} + +static void do_mssql_buffer_management(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *p, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + PERF_OBJECT_TYPE *pObjectType = + perflibFindObjectTypeByName(pDataBlock, p->objectName[NETDATA_MSSQL_BUFFER_MANAGEMENT]); + if (!pObjectType) + return; + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLBufferCacheHits)) { + if (!p->st_buff_cache_hits) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_cache_hit_ratio", p->instanceID); + netdata_fix_chart_name(id); + p->st_buff_cache_hits = rrdset_create_localhost( + "mssql", + id, + NULL, + "buffer cache", + "mssql.instance_cache_hit_ratio", + "Buffer Cache hit ratio", + "percentage", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_BUFF_CACHE_HIT_RATIO, + update_every, + RRDSET_TYPE_LINE); + + p->rd_buff_cache_hits = rrddim_add(p->st_buff_cache_hits, "hit_ratio", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_buff_cache_hits->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_buff_cache_hits, p->rd_buff_cache_hits, (collected_number)p->MSSQLBufferCacheHits.current.Data); + rrdset_done(p->st_buff_cache_hits); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLBufferCheckpointPages)) { + if (!p->st_buff_checkpoint_pages) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_bufman_checkpoint_pages", p->instanceID); + netdata_fix_chart_name(id); + p->st_buff_checkpoint_pages = rrdset_create_localhost( + "mssql", + id, + NULL, + "buffer cache", + "mssql.instance_bufman_checkpoint_pages", + "Flushed pages", + "pages/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_BUFF_CHECKPOINT_PAGES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_buff_checkpoint_pages = + rrddim_add(p->st_buff_checkpoint_pages, "log", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_buff_checkpoint_pages->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_buff_checkpoint_pages, + p->rd_buff_checkpoint_pages, + (collected_number)p->MSSQLBufferCheckpointPages.current.Data); + rrdset_done(p->st_buff_checkpoint_pages); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLBufferPageLifeExpectancy)) { + if (!p->st_buff_cache_page_life_expectancy) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_bufman_page_life_expectancy", p->instanceID); + netdata_fix_chart_name(id); + p->st_buff_cache_page_life_expectancy = rrdset_create_localhost( + "mssql", + id, + NULL, + "buffer cache", + "mssql.instance_bufman_page_life_expectancy", + "Page life expectancy", + "seconds", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_BUFF_PAGE_LIFE_EXPECTANCY, + update_every, + RRDSET_TYPE_LINE); + + p->rd_buff_cache_page_life_expectancy = rrddim_add( + p->st_buff_cache_page_life_expectancy, "life_expectancy", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add( + p->st_buff_cache_page_life_expectancy->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_buff_cache_page_life_expectancy, + p->rd_buff_cache_page_life_expectancy, + (collected_number)p->MSSQLBufferPageLifeExpectancy.current.Data); + rrdset_done(p->st_buff_cache_page_life_expectancy); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLBufferPageReads) && + perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLBufferPageWrites)) { + if (!p->st_buff_page_iops) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_bufman_iops", p->instanceID); + netdata_fix_chart_name(id); + p->st_buff_page_iops = rrdset_create_localhost( + "mssql", + id, + NULL, + "buffer cache", + "mssql.instance_bufman_iops", + "Number of pages input and output", + "pages/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_BUFF_MAN_IOPS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_buff_page_reads = rrddim_add(p->st_buff_page_iops, "read", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + p->rd_buff_page_writes = + rrddim_add(p->st_buff_page_iops, "written", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_buff_page_iops->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_buff_page_iops, p->rd_buff_page_reads, (collected_number)p->MSSQLBufferPageReads.current.Data); + rrddim_set_by_pointer( + p->st_buff_page_iops, p->rd_buff_page_writes, (collected_number)p->MSSQLBufferPageWrites.current.Data); + + rrdset_done(p->st_buff_page_iops); + } +} + +static void do_mssql_access_methods(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *p, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + PERF_OBJECT_TYPE *pObjectType = + perflibFindObjectTypeByName(pDataBlock, p->objectName[NETDATA_MSSQL_ACCESS_METHODS]); + if (!pObjectType) + return; + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLAccessMethodPageSplits)) { + if (!p->st_access_method_page_splits) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_accessmethods_page_splits", p->instanceID); + netdata_fix_chart_name(id); + p->st_access_method_page_splits = rrdset_create_localhost( + "mssql", + id, + NULL, + "buffer cache", + "mssql.instance_accessmethods_page_splits", + "Page splits", + "splits/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_BUFF_METHODS_PAGE_SPLIT, + update_every, + RRDSET_TYPE_LINE); + + p->rd_access_method_page_splits = + rrddim_add(p->st_access_method_page_splits, "page", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add( + p->st_access_method_page_splits->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_access_method_page_splits, + p->rd_access_method_page_splits, + (collected_number)p->MSSQLAccessMethodPageSplits.current.Data); + rrdset_done(p->st_access_method_page_splits); + } +} + +static void do_mssql_errors(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *p, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->objectName[NETDATA_MSSQL_SQL_ERRORS]); + if (!pObjectType) + return; + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLSQLErrorsTotal)) { + if (!p->st_sql_errors) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_sql_errors_total", p->instanceID); + netdata_fix_chart_name(id); + p->st_sql_errors = rrdset_create_localhost( + "mssql", + id, + NULL, + "errors", + "mssql.instance_sql_errors", + "Errors", + "errors/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_SQL_ERRORS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_sql_errors = rrddim_add(p->st_sql_errors, "errors", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + + rrdlabels_add(p->st_sql_errors->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_sql_errors, p->rd_sql_errors, (collected_number)p->MSSQLAccessMethodPageSplits.current.Data); + rrdset_done(p->st_sql_errors); + } +} + +int dict_mssql_locks_charts_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + char id[RRD_ID_LENGTH_MAX + 1]; + + struct mssql_lock_instance *mli = value; + const char *instance = dictionary_acquired_item_name((DICTIONARY_ITEM *)item); + + int *update_every = data; + + if (!mli->parent->st_lockWait) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_locks_lock_wait", mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->parent->st_lockWait = rrdset_create_localhost( + "mssql", + id, + NULL, + "locks", + "mssql.instance_locks_lock_wait", + "Lock requests that required the caller to wait.", + "locks/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_LOCKS_WAIT, + *update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add( + mli->parent->st_lockWait->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_lockWait) { + mli->rd_lockWait = rrddim_add(mli->parent->st_lockWait, instance, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MLI_IDX_WAIT)) { + rrddim_set_by_pointer( + mli->parent->st_lockWait, mli->rd_lockWait, (collected_number)(mli->lockWait.current.Data)); + } + + if (!mli->parent->st_deadLocks) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_locks_deadlocks", mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->parent->st_deadLocks = rrdset_create_localhost( + "mssql", + id, + NULL, + "locks", + "mssql.instance_locks_deadlocks", + "Lock requests that resulted in deadlock.", + "deadlocks/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_LOCKS_DEADLOCK, + *update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add( + mli->parent->st_deadLocks->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_deadLocks) { + mli->rd_deadLocks = rrddim_add(mli->parent->st_deadLocks, instance, NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MLI_IDX_DEAD_LOCKS)) { + rrddim_set_by_pointer( + mli->parent->st_deadLocks, mli->rd_deadLocks, (collected_number)mli->deadLocks.current.Data); + } + + return 1; +} + +static void do_mssql_locks(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *p, int update_every) { + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->objectName[NETDATA_MSSQL_LOCKS]); + if (!pObjectType) + return; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); + + if (!strcasecmp(windows_shared_buffer, "_Total")) + continue; + + struct mssql_lock_instance *mli = dictionary_set(p->locks_instances, windows_shared_buffer, NULL, sizeof(*mli)); + if (!mli) + continue; + + if (!mli->parent) { + mli->parent = p; + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mli->lockWait)) + mli->updated |= (1 << NETDATA_MSSQL_ENUM_MLI_IDX_WAIT); + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mli->deadLocks)) + mli->updated |= (1 << NETDATA_MSSQL_ENUM_MLI_IDX_DEAD_LOCKS); + } + + dictionary_sorted_walkthrough_read(p->locks_instances, dict_mssql_locks_charts_cb, &update_every); + rrdset_done(p->st_lockWait); + rrdset_done(p->st_deadLocks); +} + +static void mssql_database_backup_restore_chart(struct mssql_db_instance *mli, const char *db, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + if (!mli->st_db_backup_restore_operations) { + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_backup_restore_operations", db, mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->st_db_backup_restore_operations = rrdset_create_localhost( + "mssql", + id, + NULL, + "transactions", + "mssql.database_backup_restore_operations", + "Backup IO per database", + "operations/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_DATABASE_BACKUP_RESTORE_OPERATIONS, + update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add( + mli->st_db_backup_restore_operations->rrdlabels, + "mssql_instance", + mli->parent->instanceID, + RRDLABEL_SRC_AUTO); + rrdlabels_add(mli->st_db_backup_restore_operations->rrdlabels, "database", db, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_db_backup_restore_operations) { + mli->rd_db_backup_restore_operations = + rrddim_add(mli->st_db_backup_restore_operations, "backup", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_BACKUP_RESTORE_OP)) { + rrddim_set_by_pointer( + mli->st_db_backup_restore_operations, + mli->rd_db_backup_restore_operations, + (collected_number)mli->MSSQLDatabaseBackupRestoreOperations.current.Data); + } + + rrdset_done(mli->st_db_backup_restore_operations); +} + +static void mssql_database_log_flushes_chart(struct mssql_db_instance *mli, const char *db, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + if (!mli->st_db_log_flushes) { + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_log_flushes", db, mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->st_db_log_flushes = rrdset_create_localhost( + "mssql", + id, + NULL, + "transactions", + "mssql.database_log_flushes", + "Log flushes", + "flushes/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_DATABASE_LOG_FLUSHES, + update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add(mli->st_db_log_flushes->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO); + rrdlabels_add(mli->st_db_log_flushes->rrdlabels, "database", db, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_db_log_flushes) { + mli->rd_db_log_flushes = rrddim_add(mli->st_db_log_flushes, "flushes", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHES)) { + rrddim_set_by_pointer( + mli->st_db_log_flushes, + mli->rd_db_log_flushes, + (collected_number)mli->MSSQLDatabaseLogFlushes.current.Data); + } + + rrdset_done(mli->st_db_log_flushes); +} + +static void mssql_database_log_flushed_chart(struct mssql_db_instance *mli, const char *db, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + if (!mli->st_db_log_flushed) { + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_log_flushed", db, mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->st_db_log_flushed = rrdset_create_localhost( + "mssql", + id, + NULL, + "transactions", + "mssql.database_log_flushed", + "Log flushed", + "bytes/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_DATABASE_LOG_FLUSHED, + update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add(mli->st_db_log_flushed->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO); + rrdlabels_add(mli->st_db_log_flushed->rrdlabels, "database", db, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_db_log_flushed) { + mli->rd_db_log_flushed = rrddim_add(mli->st_db_log_flushed, "flushed", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHED)) { + rrddim_set_by_pointer( + mli->st_db_log_flushed, + mli->rd_db_log_flushed, + (collected_number)mli->MSSQLDatabaseLogFlushed.current.Data); + } + + rrdset_done(mli->st_db_log_flushed); +} + +static void mssql_transactions_chart(struct mssql_db_instance *mli, const char *db, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + if (!mli->st_db_transactions) { + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_transactions", db, mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->st_db_transactions = rrdset_create_localhost( + "mssql", + id, + NULL, + "transactions", + "mssql.database_transactions", + "Transactions", + "transactions/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_DATABASE_TRANSACTIONS, + update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add(mli->st_db_transactions->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO); + rrdlabels_add(mli->st_db_transactions->rrdlabels, "database", db, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_db_transactions) { + mli->rd_db_transactions = + rrddim_add(mli->st_db_transactions, "transactions", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_TRANSACTIONS)) { + rrddim_set_by_pointer( + mli->st_db_transactions, + mli->rd_db_transactions, + (collected_number)mli->MSSQLDatabaseTransactions.current.Data); + } + + rrdset_done(mli->st_db_transactions); +} + +static void mssql_write_transactions_chart(struct mssql_db_instance *mli, const char *db, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + if (!mli->st_db_write_transactions) { + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_write_transactions", db, mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->st_db_write_transactions = rrdset_create_localhost( + "mssql", + id, + NULL, + "transactions", + "mssql.database_write_transactions", + "Write transactions", + "transactions/s", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_DATABASE_WRITE_TRANSACTIONS, + update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add( + mli->st_db_write_transactions->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO); + rrdlabels_add(mli->st_db_write_transactions->rrdlabels, "database", db, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_db_write_transactions) { + mli->rd_db_write_transactions = + rrddim_add(mli->st_db_write_transactions, "write", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); + } + + if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_WRITE_TRANSACTIONS)) { + rrddim_set_by_pointer( + mli->st_db_write_transactions, + mli->rd_db_write_transactions, + (collected_number)mli->MSSQLDatabaseWriteTransactions.current.Data); + } + + rrdset_done(mli->st_db_write_transactions); +} + +static void mssql_active_transactions_chart(struct mssql_db_instance *mli, const char *db, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + if (!mli->st_db_active_transactions) { + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_active_transactions", db, mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->st_db_active_transactions = rrdset_create_localhost( + "mssql", + id, + NULL, + "transactions", + "mssql.database_active_transactions", + "Active transactions per database", + "transactions", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_DATABASE_ACTIVE_TRANSACTIONS, + update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add( + mli->st_db_active_transactions->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO); + rrdlabels_add(mli->st_db_active_transactions->rrdlabels, "database", db, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_db_active_transactions) { + mli->rd_db_active_transactions = + rrddim_add(mli->st_db_active_transactions, "active", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + + if (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_ACTIVE_TRANSACTIONS)) { + rrddim_set_by_pointer( + mli->st_db_active_transactions, + mli->rd_db_active_transactions, + (collected_number)mli->MSSQLDatabaseActiveTransactions.current.Data); + } + + rrdset_done(mli->st_db_active_transactions); +} + +static void mssql_data_file_size_chart(struct mssql_db_instance *mli, const char *db, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + if (!mli->st_db_data_file_size) { + snprintfz(id, RRD_ID_LENGTH_MAX, "db_%s_instance_%s_data_files_size", db, mli->parent->instanceID); + netdata_fix_chart_name(id); + mli->st_db_data_file_size = rrdset_create_localhost( + "mssql", + id, + NULL, + "size", + "mssql.database_data_files_size", + "Current database size.", + "bytes", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_DATABASE_DATA_FILE_SIZE, + update_every, + RRDSET_TYPE_LINE); + + rrdlabels_add( + mli->st_db_data_file_size->rrdlabels, "mssql_instance", mli->parent->instanceID, RRDLABEL_SRC_AUTO); + rrdlabels_add(mli->st_db_data_file_size->rrdlabels, "database", db, RRDLABEL_SRC_AUTO); + } + + if (!mli->rd_db_data_file_size) { + mli->rd_db_data_file_size = rrddim_add(mli->st_db_data_file_size, "size", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + } + + // FIXME: If the value cannot be retrieved, remove the chart instead of displaying a 0 value. + collected_number data = + (mli->updated & (1 << NETDATA_MSSQL_ENUM_MDI_IDX_FILE_SIZE)) ? mli->MSSQLDatabaseDataFileSize.current.Data : 0; + rrddim_set_by_pointer(mli->st_db_data_file_size, mli->rd_db_data_file_size, data); + + rrdset_done(mli->st_db_data_file_size); +} + +int dict_mssql_databases_charts_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct mssql_db_instance *mli = value; + const char *db = dictionary_acquired_item_name((DICTIONARY_ITEM *)item); + + int *update_every = data; + + void (*transaction_chart[])(struct mssql_db_instance *, const char *, int) = { + // FIXME: allegedly Netdata collects negative values (MSSQLDatabaseDataFileSize). + // something is wrong, perflibdump shows correct values. + // mssql_data_file_size_chart, + mssql_transactions_chart, + mssql_database_backup_restore_chart, + mssql_database_log_flushed_chart, + mssql_database_log_flushes_chart, + mssql_active_transactions_chart, + mssql_write_transactions_chart, + + // Last function pointer must be NULL + NULL}; + + int i; + for (i = 0; transaction_chart[i]; i++) { + transaction_chart[i](mli, db, *update_every); + } + + return 1; +} + +static void do_mssql_databases(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *p, int update_every) { + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->objectName[NETDATA_MSSQL_DATABASE]); + if (!pObjectType) + return; + + PERF_INSTANCE_DEFINITION *pi = NULL; + for (LONG i = 0; i < pObjectType->NumInstances; i++) { + pi = perflibForEachInstance(pDataBlock, pObjectType, pi); + if (!pi) + break; + + if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer))) + strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1); + + if (!strcasecmp(windows_shared_buffer, "_Total")) + continue; + + struct mssql_db_instance *mdi = dictionary_set(p->databases, windows_shared_buffer, NULL, sizeof(*mdi)); + if (!mdi) + continue; + + mdi->updated = 0; + if (!mdi->parent) { + mdi->parent = p; + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseDataFileSize)) { + LONGLONG value = (LONGLONG)mdi->MSSQLDatabaseDataFileSize.current.Data; + if (value > 0) + mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_FILE_SIZE); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseActiveTransactions)) + mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_ACTIVE_TRANSACTIONS); + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseBackupRestoreOperations)) + mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_BACKUP_RESTORE_OP); + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseLogFlushed)) + mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHED); + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseLogFlushes)) + mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_LOG_FLUSHES); + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseTransactions)) + mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_TRANSACTIONS); + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &mdi->MSSQLDatabaseWriteTransactions)) + mdi->updated |= (1 << NETDATA_MSSQL_ENUM_MDI_IDX_WRITE_TRANSACTIONS); + } + + dictionary_sorted_walkthrough_read(p->databases, dict_mssql_databases_charts_cb, &update_every); +} + +static void do_mssql_memory_mgr(PERF_DATA_BLOCK *pDataBlock, struct mssql_instance *p, int update_every) { + char id[RRD_ID_LENGTH_MAX + 1]; + + PERF_OBJECT_TYPE *pObjectType = perflibFindObjectTypeByName(pDataBlock, p->objectName[NETDATA_MSSQL_MEMORY]); + if (!pObjectType) + return; + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLConnectionMemoryBytes)) { + if (!p->st_conn_memory) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_memmgr_connection_memory_bytes", p->instanceID); + netdata_fix_chart_name(id); + p->st_conn_memory = rrdset_create_localhost( + "mssql", + id, + NULL, + "memory", + "mssql.instance_memmgr_connection_memory_bytes", + "Amount of dynamic memory to maintain connections", + "bytes", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_MEMMGR_CONNECTION_MEMORY_BYTES, + update_every, + RRDSET_TYPE_LINE); + + p->rd_conn_memory = rrddim_add(p->st_conn_memory, "memory", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_conn_memory->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_conn_memory, + p->rd_conn_memory, + (collected_number)(p->MSSQLConnectionMemoryBytes.current.Data * 1024)); + rrdset_done(p->st_conn_memory); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLExternalBenefitOfMemory)) { + if (!p->st_ext_benefit_mem) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_memmgr_external_benefit_of_memory", p->instanceID); + netdata_fix_chart_name(id); + p->st_ext_benefit_mem = rrdset_create_localhost( + "mssql", + id, + NULL, + "memory", + "mssql.instance_memmgr_external_benefit_of_memory", + "Performance benefit from adding memory to a specific cache", + "bytes", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_MEMMGR_EXTERNAL_BENEFIT_OF_MEMORY, + update_every, + RRDSET_TYPE_LINE); + + p->rd_ext_benefit_mem = rrddim_add(p->st_ext_benefit_mem, "benefit", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_ext_benefit_mem->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_ext_benefit_mem, + p->rd_ext_benefit_mem, + (collected_number)p->MSSQLExternalBenefitOfMemory.current.Data); + rrdset_done(p->st_ext_benefit_mem); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLPendingMemoryGrants)) { + if (!p->st_pending_mem_grant) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_memmgr_pending_memory_grants", p->instanceID); + netdata_fix_chart_name(id); + p->st_pending_mem_grant = rrdset_create_localhost( + "mssql", + id, + NULL, + "memory", + "mssql.instance_memmgr_pending_memory_grants", + "Process waiting for memory grant", + "processes", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_MEMMGR_PENDING_MEMORY_GRANTS, + update_every, + RRDSET_TYPE_LINE); + + p->rd_pending_mem_grant = + rrddim_add(p->st_pending_mem_grant, "pending", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_pending_mem_grant->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_pending_mem_grant, + p->rd_pending_mem_grant, + (collected_number)p->MSSQLPendingMemoryGrants.current.Data); + + rrdset_done(p->st_pending_mem_grant); + } + + if (perflibGetObjectCounter(pDataBlock, pObjectType, &p->MSSQLTotalServerMemory)) { + if (!p->st_mem_tot_server) { + snprintfz(id, RRD_ID_LENGTH_MAX, "instance_%s_memmgr_server_memory", p->instanceID); + netdata_fix_chart_name(id); + p->st_mem_tot_server = rrdset_create_localhost( + "mssql", + id, + NULL, + "memory", + "mssql.instance_memmgr_server_memory", + "Memory committed", + "bytes", + PLUGIN_WINDOWS_NAME, + "PerflibMSSQL", + PRIO_MSSQL_MEMMGR_TOTAL_SERVER, + update_every, + RRDSET_TYPE_LINE); + + p->rd_mem_tot_server = rrddim_add(p->st_mem_tot_server, "memory", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); + + rrdlabels_add(p->st_mem_tot_server->rrdlabels, "mssql_instance", p->instanceID, RRDLABEL_SRC_AUTO); + } + + rrddim_set_by_pointer( + p->st_mem_tot_server, + p->rd_mem_tot_server, + (collected_number)(p->MSSQLTotalServerMemory.current.Data * 1024)); + + rrdset_done(p->st_mem_tot_server); + } +} + +int dict_mssql_charts_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) { + struct mssql_instance *p = value; + int *update_every = data; + + static void (*doMSSQL[])(PERF_DATA_BLOCK *, struct mssql_instance *, int) = { + do_mssql_general_stats, + do_mssql_errors, + do_mssql_databases, + do_mssql_locks, + do_mssql_memory_mgr, + do_mssql_buffer_management, + do_mssql_sql_statistics, + do_mssql_access_methods}; + + DWORD i; + for (i = 0; i < NETDATA_MSSQL_METRICS_END; i++) { + if (!doMSSQL[i]) + continue; + + DWORD id = RegistryFindIDByName(p->objectName[i]); + if (id == PERFLIB_REGISTRY_NAME_NOT_FOUND) + return -1; + + PERF_DATA_BLOCK *pDataBlock = perflibGetPerformanceData(id); + if (!pDataBlock) + return -1; + + doMSSQL[i](pDataBlock, p, *update_every); + } + + return 1; +} + +int do_PerflibMSSQL(int update_every, usec_t dt __maybe_unused) { + static bool initialized = false; + + if (unlikely(!initialized)) { + if (initialize()) + return -1; + + initialized = true; + } + + dictionary_sorted_walkthrough_read(mssql_instances, dict_mssql_charts_cb, &update_every); + + return 0; +} |