/** BEGIN COPYRIGHT BLOCK * This Program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; version 2 of the License. * * This Program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA. * * In addition, as a special exception, Red Hat, Inc. gives You the additional * right to link the code of this Program with code not covered under the GNU * General Public License ("Non-GPL Code") and to distribute linked combinations * including the two, subject to the limitations in this paragraph. Non-GPL Code * permitted under this exception must only link to the code of this Program * through those well defined interfaces identified in the file named EXCEPTION * found in the source code files (the "Approved Interfaces"). The files of * Non-GPL Code may instantiate templates or use macros or inline functions from * the Approved Interfaces without causing the resulting work to be covered by * the GNU General Public License. Only Red Hat, Inc. may make changes or * additions to the list of Approved Interfaces. You must obey the GNU General * Public License in all respects for all of the Program code and other code used * in conjunction with the Program except the Non-GPL Code covered by this * exception. If you modify this file, you may extend this exception to your * version of the file, but you are not obligated to do so. If you do not wish to * provide this exception without modification, you must delete this exception * statement from your version and license this file solely under the GPL without * exception. * * * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. * Copyright (C) 2005 Red Hat, Inc. * All rights reserved. * END COPYRIGHT BLOCK **/ #ifdef HAVE_CONFIG_H # include #endif /* Database performance counters stuff */ #include "back-ldbm.h" #include "perfctrs.h" #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 4000 #define TXN_STAT(env, statp, flags, malloc) \ (env)->txn_stat((env), (statp), (flags)) #define MEMP_STAT(env, gsp, fsp, flags, malloc) \ (env)->memp_stat((env), (gsp), (fsp), (flags)) #define LOG_STAT(env, spp, flags, malloc) (env)->log_stat((env), (spp), (flags)) #define LOCK_STAT(env, statp, flags, malloc) \ (env)->lock_stat((env), (statp), (flags)) #if DB_VERSION_MINOR >= 4 /* i.e. 4.4 or later */ #define GET_N_LOCK_WAITS(lockstat) lockstat->st_lock_wait #else #define GET_N_LOCK_WAITS(lockstat) lockstat->st_nconflicts #endif #else /* older than db 4.0 */ #if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 3300 #define TXN_STAT(env, statp, flags, malloc) txn_stat((env), (statp)) #define MEMP_STAT(env, gsp, fsp, flags, malloc) memp_stat((env), (gsp), (fsp)) #define LOG_STAT(env, spp, flags, malloc) log_stat((env), (spp)) #define LOCK_STAT(env, statp, flags, malloc) lock_stat((env), (statp)) #define GET_N_LOCK_WAITS(lockstat) lockstat->st_nconflicts #else /* older than db 3.3 */ #define TXN_STAT(env, statp, flags, malloc) txn_stat((env), (statp), (malloc)) #define MEMP_STAT(env, gsp, fsp, flags, malloc) memp_stat((env), (gsp), (fsp), (malloc)) #define LOG_STAT(env, spp, flags, malloc) log_stat((env), (spp), (malloc)) #define LOCK_STAT(env, statp, flags, malloc) lock_stat((env), (statp), (malloc)) #define GET_N_LOCK_WAITS(lockstat) lockstat->st_nconflicts #endif #endif static void perfctrs_update(perfctrs_private *priv, DB_ENV *db_env); static void perfctr_add_to_entry( Slapi_Entry *e, char *type, PRUint32 countervalue ); /* * Win32 specific code (to support the Windows NT/2000 Performance Monitor). */ #if defined(_WIN32) static char * string_concatenate(char *a, char* b) { size_t string_length = 0; char *string = NULL; string_length = strlen(a) + strlen(b) + 1; string = slapi_ch_malloc(string_length); sprintf(string,"%s%s",a,b); return string; } static void init_shared_memory(perfctrs_private *priv) { performance_counters *perf = (performance_counters*)priv->memory; if (NULL != perf) { memset(perf,0,sizeof(performance_counters)); } } static int open_event(char *name, perfctrs_private *priv) { HANDLE hEvent = INVALID_HANDLE_VALUE; hEvent = OpenEvent(EVENT_ALL_ACCESS,FALSE,name); if (NULL == hEvent) { hEvent = CreateEvent(NULL,FALSE,FALSE,name); if (NULL == hEvent) { LDAPDebug(LDAP_DEBUG_ANY,"BAD EV 1, err=%d\n",GetLastError(),0,0); return -1; } } priv->hEvent = hEvent; return 0; } static int open_shared_memory(char *name, perfctrs_private *priv) { HANDLE hMapping = INVALID_HANDLE_VALUE; void *pMemory = NULL; /* We fear a bug in NT where it fails to attach to an existing region on calling CreateFileMapping, so let's call OpenFileMapping first */ hMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS,FALSE,name); if (NULL == hMapping) { hMapping = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,sizeof(performance_counters),name); if (NULL == hMapping) { LDAPDebug(LDAP_DEBUG_ANY,"BAD MAP 1, err=%d\n",GetLastError(),0,0); return -1; } } /* If we got to here, we have the mapping object open */ pMemory = MapViewOfFile(hMapping,FILE_MAP_ALL_ACCESS,0,0,0); if (NULL == pMemory) { LDAPDebug(LDAP_DEBUG_ANY,"BAD MAP 2, err=%d\n",GetLastError(),0,0); return -1; } priv->memory = pMemory; priv->hMemory = hMapping; return 0; } #endif /* Init perf ctrs */ void perfctrs_init(struct ldbminfo *li, perfctrs_private **ret_priv) { perfctrs_private *priv = NULL; #if defined(_WIN32) /* XXX What's my instance name ? */ /* * We have a single DB environment for all backend databases. * Therefore the instance name can be the server instance name. * To match the db perf ctr DLL the instance name should be the * name of a key defined in the registry under: * HKEY_LOCAL_MACHINE\SOFTWARE\Netscape\Directory\5 * i.e. slapd-servername */ char *string = NULL; char *instance_name = li->li_plugin->plg_name; /* XXX does not identify server instance */ #endif *ret_priv = NULL; #if defined(_WIN32) /* * On Windows, the performance counters reside in shared memory. */ if (NULL == instance_name) { goto error; } /* Invent the name for the shared memory region */ string = string_concatenate(instance_name,PERFCTRS_REGION_SUFFIX); if (NULL == string) { goto error; } #endif /* * We need the perfctrs_private area on all platforms. */ priv = (perfctrs_private *)slapi_ch_calloc(1,sizeof(perfctrs_private)); #if defined(_WIN32) /* Try to open the shared memory region */ open_shared_memory(string,priv); free(string); /* Invent the name for the update mutex */ string = string_concatenate(instance_name,PERFCTRS_MUTEX_SUFFIX); if (NULL == string) { goto error; } open_event(string,priv); free(string); init_shared_memory(priv); #else /* * On other platforms, the performance counters reside in regular memory. */ priv->memory = slapi_ch_calloc( 1, sizeof( performance_counters )); #endif *ret_priv = priv; return; #if !defined(_WIN32) if (priv) slapi_ch_free((void**)&priv->memory); #endif slapi_ch_free((void**)&priv); } /* Terminate perf ctrs */ void perfctrs_terminate(perfctrs_private **priv, DB_ENV *db_env) { DB_MPOOL_STAT *mpstat = NULL; DB_TXN_STAT *txnstat = NULL; DB_LOG_STAT *logstat = NULL; DB_LOCK_STAT *lockstat = NULL; MEMP_STAT(db_env, &mpstat, NULL, DB_STAT_CLEAR, (void *)slapi_ch_malloc); slapi_ch_free((void**)&mpstat); TXN_STAT(db_env, &txnstat, DB_STAT_CLEAR, (void *)slapi_ch_malloc); slapi_ch_free((void**)&txnstat); LOG_STAT(db_env, &logstat, DB_STAT_CLEAR, (void *)slapi_ch_malloc); slapi_ch_free((void**)&logstat); LOCK_STAT(db_env, &lockstat, DB_STAT_CLEAR, (void *)slapi_ch_malloc); slapi_ch_free((void**)&lockstat); #if defined(_WIN32) if (NULL != (*priv)->memory) { UnmapViewOfFile((*priv)->memory); } if (NULL != (*priv)->hMemory) { CloseHandle((*priv)->hMemory); } if (NULL != (*priv)->hEvent) { CloseHandle((*priv)->hEvent); } #else if (NULL != (*priv)->memory) { slapi_ch_free(&(*priv)->memory); } #endif slapi_ch_free( (void **)priv ); } /* Wait while checking for perfctr update requests */ void perfctrs_wait(size_t milliseconds,perfctrs_private *priv,DB_ENV *db_env) { #if defined(_WIN32) if (NULL != priv) { DWORD ret = 0; if (NULL != priv->hEvent) { /* Sleep waiting on the perfctrs update event */ ret = WaitForSingleObject(priv->hEvent,milliseconds); /* If we didn't time out, update the perfctrs */ if (ret == WAIT_OBJECT_0) { perfctrs_update(priv,db_env); } } else { Sleep(milliseconds); } } #else /* Just sleep */ PRIntervalTime interval; /*NSPR timeout stuffy*/ interval = PR_MillisecondsToInterval(milliseconds); DS_Sleep(interval); #endif } /* Update perfctrs */ static void perfctrs_update(perfctrs_private *priv, DB_ENV *db_env) { int ret = 0; performance_counters *perf; if (NULL == priv) { return; } if (NULL == db_env) { return; } perf = (performance_counters*)priv->memory; if (NULL == perf) { return; } /* Call libdb to get the various stats */ if (dblayer_db_uses_logging(db_env)) { DB_LOG_STAT *logstat = NULL; ret = LOG_STAT(db_env,&logstat,0,(void *)slapi_ch_malloc); if (0 == ret) { perf->log_region_wait_rate = logstat->st_region_wait; perf->log_write_rate = 1024*1024*logstat->st_w_mbytes + logstat->st_w_bytes; perf->log_bytes_since_checkpoint = 1024*1024*logstat->st_wc_mbytes + logstat->st_wc_bytes; } slapi_ch_free((void **)&logstat); } if (dblayer_db_uses_transactions(db_env)) { DB_TXN_STAT *txnstat = NULL; ret = TXN_STAT(db_env, &txnstat, 0, (void *)slapi_ch_malloc); if (0 == ret) { perf->active_txns = txnstat->st_nactive; perf->commit_rate = txnstat->st_ncommits; perf->abort_rate = txnstat->st_naborts; perf->txn_region_wait_rate = txnstat->st_region_wait; } slapi_ch_free((void **)&txnstat); } if (dblayer_db_uses_locking(db_env)) { DB_LOCK_STAT *lockstat = NULL; ret = LOCK_STAT(db_env,&lockstat,0,(void *)slapi_ch_malloc); if (0 == ret) { perf->lock_region_wait_rate = lockstat->st_region_wait; perf->deadlock_rate = lockstat->st_ndeadlocks; perf->configured_locks = lockstat->st_maxlocks; perf->current_locks = lockstat->st_nlocks; perf->max_locks = lockstat->st_maxnlocks; perf->lockers = lockstat->st_nlockers; perf->lock_conflicts = GET_N_LOCK_WAITS(lockstat); perf->lock_request_rate = lockstat->st_nrequests; perf->current_lock_objects = lockstat->st_nobjects; perf->max_lock_objects = lockstat->st_maxnobjects; } slapi_ch_free((void **)&lockstat); } if (dblayer_db_uses_mpool(db_env)) { DB_MPOOL_STAT *mpstat = NULL; ret = MEMP_STAT(db_env,&mpstat,NULL,0,(void *)slapi_ch_malloc); if (0 == ret) { #define ONEG 1073741824 perf->cache_size_bytes = mpstat->st_gbytes * ONEG + mpstat->st_bytes; perf->page_access_rate = mpstat->st_cache_hit + mpstat->st_cache_miss; perf->cache_hit = mpstat->st_cache_hit; perf->cache_try = mpstat->st_cache_hit + mpstat->st_cache_miss; perf->page_create_rate = mpstat->st_page_create; perf->page_read_rate = mpstat->st_page_in; perf->page_write_rate = mpstat->st_page_out; perf->page_ro_evict_rate = mpstat->st_ro_evict; perf->page_rw_evict_rate = mpstat->st_rw_evict; perf->hash_buckets = mpstat->st_hash_buckets; perf->hash_search_rate = mpstat->st_hash_searches; perf->longest_chain_length = mpstat->st_hash_longest; perf->hash_elements_examine_rate = mpstat->st_hash_examined; perf->pages_in_use = mpstat->st_page_dirty + mpstat->st_page_clean; perf->dirty_pages = mpstat->st_page_dirty; perf->clean_pages = mpstat->st_page_clean; perf->page_trickle_rate = mpstat->st_page_trickle; perf->cache_region_wait_rate = mpstat->st_region_wait; slapi_ch_free((void **)&mpstat); } } /* Place the stats in the shared memory region */ /* Bump the sequence number */ perf->sequence_number++; } /* * Define a map (array of structures) which is used to retrieve performance * counters from the performance_counters structure and map them to an * LDAP attribute type. */ #define SLAPI_LDBM_PERFCTR_AT_PREFIX "nsslapd-db-" typedef struct slapi_ldbm_perfctr_at_map { char *pam_type; /* name of LDAP attribute type */ size_t pam_offset; /* offset into performance_counters struct */ } SlapiLDBMPerfctrATMap; static SlapiLDBMPerfctrATMap perfctr_at_map[] = { { SLAPI_LDBM_PERFCTR_AT_PREFIX "abort-rate", offsetof( performance_counters, abort_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "active-txns", offsetof( performance_counters, active_txns ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "cache-hit", offsetof( performance_counters, cache_hit ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "cache-try", offsetof( performance_counters, cache_try ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "cache-region-wait-rate", offsetof( performance_counters, cache_region_wait_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "cache-size-bytes", offsetof( performance_counters, cache_size_bytes ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "clean-pages", offsetof( performance_counters, clean_pages ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "commit-rate", offsetof( performance_counters, commit_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "deadlock-rate", offsetof( performance_counters, deadlock_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "dirty-pages", offsetof( performance_counters, dirty_pages ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "hash-buckets", offsetof( performance_counters, hash_buckets ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "hash-elements-examine-rate", offsetof( performance_counters, hash_elements_examine_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "hash-search-rate", offsetof( performance_counters, hash_search_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "lock-conflicts", offsetof( performance_counters, lock_conflicts ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "lock-region-wait-rate", offsetof( performance_counters, lock_region_wait_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "lock-request-rate", offsetof( performance_counters, lock_request_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "lockers", offsetof( performance_counters, lockers ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "configured-locks", offsetof( performance_counters, configured_locks ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "current-locks", offsetof( performance_counters, current_locks ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "max-locks", offsetof( performance_counters, max_locks ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "current-lock-objects", offsetof( performance_counters, current_lock_objects ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "max-lock-objects", offsetof( performance_counters, max_lock_objects ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "log-bytes-since-checkpoint", offsetof( performance_counters, log_bytes_since_checkpoint ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "log-region-wait-rate", offsetof( performance_counters, log_region_wait_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "log-write-rate", offsetof( performance_counters, log_write_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "longest-chain-length", offsetof( performance_counters, longest_chain_length ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "objects-locked", offsetof( performance_counters, page_access_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "page-create-rate", offsetof( performance_counters, page_create_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "page-read-rate", offsetof( performance_counters, page_read_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "page-ro-evict-rate", offsetof( performance_counters, page_ro_evict_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "page-rw-evict-rate", offsetof( performance_counters, page_rw_evict_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "page-trickle-rate", offsetof( performance_counters, page_trickle_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "page-write-rate", offsetof( performance_counters, page_write_rate ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "pages-in-use", offsetof( performance_counters, pages_in_use ) }, { SLAPI_LDBM_PERFCTR_AT_PREFIX "txn-region-wait-rate", offsetof( performance_counters, txn_region_wait_rate ) }, }; #define SLAPI_LDBM_PERFCTR_AT_MAP_COUNT \ (sizeof(perfctr_at_map) / sizeof(SlapiLDBMPerfctrATMap)) /* * Set attributes and values in entry `e' based on performance counter * information (from `priv'). */ void perfctrs_as_entry( Slapi_Entry *e, perfctrs_private *priv, DB_ENV *db_env ) { performance_counters *perf; int i; if (priv == NULL) return; perf = (performance_counters*)priv->memory; /* * First, update the values so they are current. */ perfctrs_update( priv, db_env ); /* * Then convert all the counters to attribute values. */ for ( i = 0; i < SLAPI_LDBM_PERFCTR_AT_MAP_COUNT; ++i ) { perfctr_add_to_entry( e, perfctr_at_map[i].pam_type, *((PRUint32 *)((char *)perf + perfctr_at_map[i].pam_offset))); } } static void perfctr_add_to_entry( Slapi_Entry *e, char *type, PRUint32 countervalue ) { /* * XXXmcs: the following line assumes that long's are 32 bits or larger, * which we assume in other places too I am sure. */ slapi_entry_attr_set_ulong( e, type, (unsigned long)countervalue ); }