diff --git a/include/common.h b/include/common.h
index ea5070b5c6..662997404e 100644
--- a/include/common.h
+++ b/include/common.h
@@ -440,7 +440,8 @@ zbx_graph_yaxis_types_t;
 #define ZBX_LOG_LEVEL_DECREASE	"log_level_decrease"
 #define ZBX_SNMP_CACHE_RELOAD	"snmp_cache_reload"
 #define ZBX_DIAGINFO		"diaginfo"
-
+#define ZBX_PROF_ENABLE		"prof_enable"
+#define ZBX_PROF_DISABLE	"prof_disable"
 /* value for not supported items */
 #define ZBX_NOTSUPPORTED	"ZBX_NOTSUPPORTED"
 /* the error message for not supported items when reason is unknown */
@@ -933,7 +934,8 @@ zbx_task_t;
 #define ZBX_RTC_CONFIG_CACHE_RELOAD	8
 #define ZBX_RTC_SNMP_CACHE_RELOAD	9
 #define ZBX_RTC_DIAGINFO		10
-
+#define ZBX_RTC_PROF_ENABLE		11
+#define ZBX_RTC_PROF_DISABLE		12
 typedef enum
 {
 	HTTPTEST_AUTH_NONE = 0,
@@ -1096,6 +1098,16 @@ void	zbx_strarr_init(char ***arr);
 void	zbx_strarr_add(char ***arr, const char *entry);
 void	zbx_strarr_free(char **arr);
 
+void	zbx_print_prof(void);
+void	zbx_enable_prof(void);
+void	zbx_disable_prof(void);
+void	*zbx_prof_start(const char *func_name);
+void	zbx_prof_end(void *func_profile);
+void	zbx_prof_end_processing(void *func_profile);
+void	zbx_reset_prof(void);
+
+extern void	*zbx_func_profile;
+
 #if defined(__GNUC__) || defined(__clang__)
 #	define __zbx_attr_format_printf(idx1, idx2) __attribute__((__format__(__printf__, (idx1), (idx2))))
 #else
diff --git a/include/mutexs.h b/include/mutexs.h
index c380f204b5..d138889253 100644
--- a/include/mutexs.h
+++ b/include/mutexs.h
@@ -64,9 +64,9 @@ zbx_rwlock_name_t;
 #	define ZBX_MUTEX_NULL			NULL
 #	define ZBX_RWLOCK_NULL			NULL
 
-#	define zbx_rwlock_wrlock(rwlock)	__zbx_rwlock_wrlock(__FILE__, __LINE__, rwlock)
-#	define zbx_rwlock_rdlock(rwlock)	__zbx_rwlock_rdlock(__FILE__, __LINE__, rwlock)
-#	define zbx_rwlock_unlock(rwlock)	__zbx_rwlock_unlock(__FILE__, __LINE__, rwlock)
+#	define zbx_rwlock_wrlock(rwlock)	do {void	*prof_func = zbx_prof_start(__func__); zbx_func_profile = prof_func; __zbx_rwlock_wrlock(__FILE__, __LINE__, rwlock); zbx_prof_end(prof_func); }while(0);
+#	define zbx_rwlock_rdlock(rwlock)	do {void	*prof_func = zbx_prof_start(__func__); zbx_func_profile = prof_func; __zbx_rwlock_rdlock(__FILE__, __LINE__, rwlock); zbx_prof_end(prof_func); }while(0);
+#	define zbx_rwlock_unlock(rwlock)	do {__zbx_rwlock_unlock(__FILE__, __LINE__, rwlock); zbx_prof_end_processing(zbx_func_profile);}while(0);
 
 typedef pthread_mutex_t * zbx_mutex_t;
 typedef pthread_rwlock_t * zbx_rwlock_t;
@@ -93,8 +93,8 @@ int		zbx_rwlock_create(zbx_rwlock_t *rwlock, zbx_rwlock_name_t name, char **erro
 zbx_mutex_t	zbx_mutex_addr_get(zbx_mutex_name_t mutex_name);
 zbx_rwlock_t	zbx_rwlock_addr_get(zbx_rwlock_name_t rwlock_name);
 #endif	/* _WINDOWS */
-#	define zbx_mutex_lock(mutex)		__zbx_mutex_lock(__FILE__, __LINE__, mutex)
-#	define zbx_mutex_unlock(mutex)		__zbx_mutex_unlock(__FILE__, __LINE__, mutex)
+#	define zbx_mutex_lock(mutex)		do {void	*prof_func = zbx_prof_start(__func__); zbx_func_profile = prof_func; __zbx_mutex_lock(__FILE__, __LINE__, mutex); zbx_prof_end(prof_func); }while(0);
+#	define zbx_mutex_unlock(mutex)		do {__zbx_mutex_unlock(__FILE__, __LINE__, mutex); zbx_prof_end_processing(zbx_func_profile);}while(0);
 
 int	zbx_mutex_create(zbx_mutex_t *mutex, zbx_mutex_name_t name, char **error);
 void	__zbx_mutex_lock(const char *filename, int line, zbx_mutex_t mutex);
diff --git a/src/libs/zbxcommon/Makefile.am b/src/libs/zbxcommon/Makefile.am
index dda144eff4..1246ebfeef 100644
--- a/src/libs/zbxcommon/Makefile.am
+++ b/src/libs/zbxcommon/Makefile.am
@@ -8,6 +8,7 @@ libzbxcommon_a_SOURCES = \
 	file.c \
 	iprange.c \
 	misc.c \
+	prof.c \
 	setproctitle.c \
 	str.c \
 	variant.c \
diff --git a/src/libs/zbxcommon/misc.c b/src/libs/zbxcommon/misc.c
index 0165b413eb..3f15afe1a0 100644
--- a/src/libs/zbxcommon/misc.c
+++ b/src/libs/zbxcommon/misc.c
@@ -3811,7 +3811,7 @@ static void	update_resolver_conf(void)
  ******************************************************************************/
 void	zbx_update_env(double time_now)
 {
-	static double	time_update = 0;
+	static double	time_update = 0, last_update;
 
 	/* handle /etc/resolv.conf update and log rotate less often than once a second */
 	if (1.0 < time_now - time_update)
@@ -3822,6 +3822,12 @@ void	zbx_update_env(double time_now)
 		update_resolver_conf();
 #endif
 	}
+
+	if (30 < time_now - last_update)
+	{
+		last_update = time_now;
+		zbx_print_prof();
+	}
 }
 
 /******************************************************************************
diff --git a/src/libs/zbxcommon/prof.c b/src/libs/zbxcommon/prof.c
new file mode 100644
index 0000000000..81db5f7be3
--- /dev/null
+++ b/src/libs/zbxcommon/prof.c
@@ -0,0 +1,131 @@
+#include "zbxalgo.h"
+#include "log.h"
+
+typedef struct
+{
+	const char	*func_name;
+	double		start;
+	double		sec;
+	double		sec_processing;
+	unsigned int	locked;
+}
+ZBX_FUNC_PROFILE;
+
+static zbx_vector_ptr_t	zbx_func_profiles;
+static volatile int	zbx_prof_enabled;
+void			*zbx_func_profile;
+
+static int	zbx_default_ptr_ptr_compare_func(const void *d1, const void *d2)
+{
+	const char	*p1 = *(const char **)d1;
+	const char	*p2 = *(const char **)d2;
+
+	return zbx_default_ptr_compare_func(p1, p2);
+}
+
+void	*zbx_prof_start(const char *func_name)
+{
+	if (1 == zbx_prof_enabled)
+	{
+		int			i;
+		ZBX_FUNC_PROFILE	*func_profile;
+
+		if (0 == zbx_func_profiles.values_alloc)
+			zbx_vector_ptr_create(&zbx_func_profiles);
+
+		if (FAIL == (i = zbx_vector_ptr_bsearch(&zbx_func_profiles, &func_name,
+			zbx_default_ptr_ptr_compare_func)))
+		{
+			func_profile = zbx_malloc(NULL, sizeof(ZBX_FUNC_PROFILE));
+			func_profile->func_name = func_name;
+			func_profile->sec = 0;
+			func_profile->locked = 0;
+			func_profile->sec_processing = 0;
+
+			zbx_vector_ptr_append(&zbx_func_profiles, func_profile);
+			zbx_vector_ptr_sort(&zbx_func_profiles, zbx_default_ptr_ptr_compare_func);
+		}
+		else
+			func_profile = zbx_func_profiles.values[i];
+
+		func_profile->locked++;
+		func_profile->start = zbx_time();
+
+		return func_profile;
+	}
+
+	return NULL;
+}
+
+void	zbx_prof_end(void *func_profile)
+{
+	if (NULL != func_profile && 1 == zbx_prof_enabled)
+		((ZBX_FUNC_PROFILE *)func_profile)->sec += zbx_time() - ((ZBX_FUNC_PROFILE *)func_profile)->start;
+}
+
+void	zbx_prof_end_processing(void *func_profile)	/* nested locks not supported but currently don't need */
+{
+	if (NULL != func_profile && 1 == zbx_prof_enabled)
+		((ZBX_FUNC_PROFILE *)func_profile)->sec_processing += zbx_time() - ((ZBX_FUNC_PROFILE *)func_profile)->start;
+}
+
+void	zbx_print_prof(void)
+{
+	if (1 == zbx_prof_enabled)
+	{
+		int			i;
+		ZBX_FUNC_PROFILE	*func_profile;
+		static char		*sql = NULL;
+		static size_t		sql_alloc;
+		size_t			sql_offset = 0;
+		double			total_wait_lock = 0, total_busy_lock = 0;
+
+		for (i = 0; i < zbx_func_profiles.values_num; i++)
+		{
+			func_profile = zbx_func_profiles.values[i];
+
+			if (0 == func_profile->sec_processing)
+			{
+				zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%s() busy:" ZBX_FS_DBL "\n",
+						func_profile->func_name, func_profile->sec);
+			}
+			else
+			{
+				zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "%s() locked:%u busy:" ZBX_FS_DBL
+						" wait:"ZBX_FS_DBL "\n",
+						func_profile->func_name, func_profile->locked,
+						func_profile->sec_processing - func_profile->sec, func_profile->sec);
+				total_wait_lock += func_profile->sec;
+				total_busy_lock += func_profile->sec_processing - func_profile->sec;
+			}
+		}
+
+		if (0 != sql_offset)
+		{
+			zabbix_log(LOG_LEVEL_INFORMATION, "Profiling information: %s", sql);
+			zabbix_log(LOG_LEVEL_INFORMATION, "Time spent holding locks:" ZBX_FS_DBL
+					" Time spent waiting for locks:" ZBX_FS_DBL "\n", total_busy_lock, total_wait_lock);
+		}
+	}
+	else
+		zbx_reset_prof();
+}
+
+void	zbx_reset_prof(void)
+{
+	if (0 != zbx_func_profiles.values_alloc)
+	{
+		zbx_vector_ptr_clear_ext(&zbx_func_profiles, zbx_ptr_free);
+		zbx_vector_ptr_destroy(&zbx_func_profiles);
+	}
+}
+
+void	zbx_enable_prof(void)
+{
+	zbx_prof_enabled = 1;
+}
+
+void	zbx_disable_prof(void)
+{
+	zbx_prof_enabled = 0;
+}
diff --git a/src/libs/zbxhistory/history.c b/src/libs/zbxhistory/history.c
index 8215ea49e1..d7d4ca85d4 100644
--- a/src/libs/zbxhistory/history.c
+++ b/src/libs/zbxhistory/history.c
@@ -101,6 +101,7 @@ void	zbx_history_destroy(void)
 int	zbx_history_add_values(const zbx_vector_ptr_t *history)
 {
 	int	i, flags = 0, ret = SUCCEED;
+	void	*prof_func = zbx_prof_start(__func__);
 
 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
 
@@ -121,7 +122,7 @@ int	zbx_history_add_values(const zbx_vector_ptr_t *history)
 	}
 
 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
-
+	zbx_prof_end(prof_func);
 	return ret;
 }
 
diff --git a/src/libs/zbxnix/control.c b/src/libs/zbxnix/control.c
index 0d04cde9d4..19c44306df 100644
--- a/src/libs/zbxnix/control.c
+++ b/src/libs/zbxnix/control.c
@@ -134,6 +134,20 @@ int	parse_rtc_options(const char *opt, unsigned char program_type, int *message)
 		if (SUCCEED != parse_log_level_options(opt, ZBX_CONST_STRLEN(ZBX_LOG_LEVEL_INCREASE), &scope, &data))
 			return FAIL;
 	}
+	else if (0 == strncmp(opt, ZBX_PROF_ENABLE, ZBX_CONST_STRLEN(ZBX_PROF_ENABLE)))
+	{
+		command = ZBX_RTC_PROF_ENABLE;
+
+		if (SUCCEED != parse_log_level_options(opt, ZBX_CONST_STRLEN(ZBX_PROF_ENABLE), &scope, &data))
+			return FAIL;
+	}
+	else if (0 == strncmp(opt, ZBX_PROF_DISABLE, ZBX_CONST_STRLEN(ZBX_PROF_DISABLE)))
+	{
+		command = ZBX_RTC_PROF_DISABLE;
+
+		if (SUCCEED != parse_log_level_options(opt, ZBX_CONST_STRLEN(ZBX_PROF_DISABLE), &scope, &data))
+			return FAIL;
+	}
 	else if (0 == strncmp(opt, ZBX_LOG_LEVEL_DECREASE, ZBX_CONST_STRLEN(ZBX_LOG_LEVEL_DECREASE)))
 	{
 		command = ZBX_RTC_LOG_LEVEL_DECREASE;
diff --git a/src/libs/zbxnix/daemon.c b/src/libs/zbxnix/daemon.c
index 5d16b0918a..1dde386fe0 100644
--- a/src/libs/zbxnix/daemon.c
+++ b/src/libs/zbxnix/daemon.c
@@ -68,6 +68,12 @@ static void	common_sigusr_handler(int flags)
 						zabbix_get_log_level_string());
 			}
 			break;
+		case ZBX_RTC_PROF_ENABLE:
+			zbx_enable_prof();
+			break;
+		case ZBX_RTC_PROF_DISABLE:
+			zbx_disable_prof();
+			break;
 		case ZBX_RTC_LOG_LEVEL_DECREASE:
 			if (SUCCEED != zabbix_decrease_log_level())
 			{
@@ -229,6 +235,8 @@ static void	user1_signal_handler(int sig, siginfo_t *siginfo, void *context)
 			break;
 		case ZBX_RTC_LOG_LEVEL_INCREASE:
 		case ZBX_RTC_LOG_LEVEL_DECREASE:
+		case ZBX_RTC_PROF_ENABLE:
+		case ZBX_RTC_PROF_DISABLE:
 			if ((ZBX_RTC_LOG_SCOPE_FLAG | ZBX_RTC_LOG_SCOPE_PID) == ZBX_RTC_GET_SCOPE(flags))
 				zbx_signal_process_by_pid(ZBX_RTC_GET_DATA(flags), flags);
 			else
diff --git a/src/zabbix_get/Makefile.am b/src/zabbix_get/Makefile.am
index 8ced0057cc..b7c46dbbf2 100644
--- a/src/zabbix_get/Makefile.am
+++ b/src/zabbix_get/Makefile.am
@@ -10,6 +10,7 @@ zabbix_get_LDADD = \
 	$(top_builddir)/src/libs/zbxcommon/libzbxcommon.a \
 	$(top_builddir)/src/libs/zbxlog/libzbxlog.a \
 	$(top_builddir)/src/libs/zbxcrypto/libzbxcrypto.a \
+	$(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \
 	$(top_builddir)/src/libs/zbxsys/libzbxsys.a \
 	$(top_builddir)/src/libs/zbxnix/libzbxnix.a \
 	$(top_builddir)/src/libs/zbxconf/libzbxconf.a \
diff --git a/src/zabbix_server/dbsyncer/dbsyncer.c b/src/zabbix_server/dbsyncer/dbsyncer.c
index 1fa1feb6fd..6521d2115b 100644
--- a/src/zabbix_server/dbsyncer/dbsyncer.c
+++ b/src/zabbix_server/dbsyncer/dbsyncer.c
@@ -121,6 +121,8 @@ ZBX_THREAD_ENTRY(dbsyncer_thread, args)
 
 	for (;;)
 	{
+		void	*prof_func;
+
 		sec = zbx_time();
 		zbx_update_env(sec);
 
@@ -135,9 +137,11 @@ ZBX_THREAD_ENTRY(dbsyncer_thread, args)
 		}
 
 		/* database APIs might not handle signals correctly and hang, block signals to avoid hanging */
+		prof_func = zbx_prof_start(__func__);
 		block_signals();
 		zbx_sync_history_cache(&values_num, &triggers_num, &more);
 		unblock_signals();
+		zbx_prof_end(prof_func);
 
 		total_values_num += values_num;
 		total_triggers_num += triggers_num;
diff --git a/src/zabbix_server/server.c b/src/zabbix_server/server.c
index c87650706c..107117fd22 100644
--- a/src/zabbix_server/server.c
+++ b/src/zabbix_server/server.c
@@ -1197,6 +1197,7 @@ int	MAIN_ZABBIX_ENTRY(int flags)
 	zbx_tls_init_parent();
 #endif
 	zabbix_log(LOG_LEVEL_INFORMATION, "server #0 started [main process]");
+	zbx_reset_prof();
 
 	for (i = 0; i < threads_num; i++)
 	{
