Your IP : 18.116.80.213


Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/perfmon/classes/general/
Upload File :
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/perfmon/classes/general/keeper.php

<?php

function perfmonErrorHandler($errno, $errstr, $errfile, $errline)
{
	global $perfmonErrors;
	//if(count($perfmonErrors) > 100)
	//	return false;
	static $arExclude = [
		'/modules/main/classes/general/cache.php:150' => true,
	];
	$uni_file_name = str_replace('\\', '/', mb_substr($errfile, mb_strlen($_SERVER['DOCUMENT_ROOT'] . BX_ROOT)));
	$bRecord = false;
	switch ($errno)
	{
	case E_WARNING:
		$bRecord = true;
		break;
	case E_NOTICE:
		if (
			(mb_strpos($errstr, 'Undefined index:') === false)
			&& (mb_strpos($errstr, 'Undefined offset:') === false)
			&& !array_key_exists($uni_file_name . ':' . $errline, $arExclude)
		)
		{
			$bRecord = true;
		}
		break;
	default:
		break;
	}
	if ($bRecord)
	{
		$perfmonErrors[] = [
			'ERRNO' => $errno,
			'ERRSTR' => $errstr,
			'ERRFILE' => $errfile,
			'ERRLINE' => $errline,
		];
	}
	//Continue with default handling
	return false;
}

class CPerfomanceKeeper
{
	public static function OnPageStart()
	{
		if (!defined('PERFMON_STOP'))
		{
			$end_time = COption::GetOptionInt('perfmon', 'end_time');
			if (time() > $end_time)
			{
				CPerfomanceKeeper::SetActive(false);
				if (COption::GetOptionString('perfmon', 'total_mark_value', '') == 'measure')
				{
					COption::SetOptionString('perfmon', 'total_mark_value', 'calc');
				}
			}
			else
			{
				self::setDebugModeOn();

				global $perfmonErrors;
				$perfmonErrors = [];
				if (COption::GetOptionString('perfmon', 'warning_log') === 'Y')
				{
					set_error_handler('perfmonErrorHandler');
				}
			}
		}
	}

	public static function setDebugModeOn()
	{
		global $DB, $APPLICATION;

		define('PERFMON_STARTED', $DB->ShowSqlStat . '|' . \Bitrix\Main\Data\Cache::getShowCacheStat() . '|' . $APPLICATION->ShowIncludeStat);

		$DB->ShowSqlStat = true;
		/** @var \Bitrix\Main\DB\Connection $connection */
		$connection = \Bitrix\Main\Application::getConnection();
		$connection->startTracker();

		\Bitrix\Main\Data\Cache::setShowCacheStat(COption::GetOptionString('perfmon', 'cache_log') === 'Y');
		$APPLICATION->ShowIncludeStat = true;
	}

	public static function restoreDebugMode()
	{
		global $DB, $APPLICATION;

		$toRestore = explode('|', constant('PERFMON_STARTED'));

		$DB->ShowSqlStat = $toRestore[0];

		\Bitrix\Main\Data\Cache::setShowCacheStat($toRestore[1]);
		$APPLICATION->ShowIncludeStat = $toRestore[2];
	}

	public static function OnEpilog()
	{
		if (defined('PERFMON_STARTED'))
		{
			self::restoreDebugMode();
		}
	}

	public static function OnBeforeAfterEpilog()
	{
		if (defined('PERFMON_STARTED'))
		{
			global $DB;
			$DB->ShowSqlStat = true;
		}
	}

	public static function OnAfterAfterEpilog()
	{
		if (defined('PERFMON_STARTED'))
		{
			self::restoreDebugMode();
			CPerfomanceKeeper::writeToDatabase();
		}
	}

	public static function writeToDatabase()
	{
		$START_EXEC_CURRENT_TIME = microtime();

		global $DB, $APPLICATION;
		/** @var \Bitrix\Main\DB\Connection $connection */
		$connection = \Bitrix\Main\Application::getConnection();

		$connection->stopTracker();
		$DB->ShowSqlStat = false;
		if ($connection->getTracker())
		{
			$arQueryDebug = $connection->getTracker()->getQueries();
		}
		else
		{
			$arQueryDebug = [];
		}
		$arIncludeDebug = $APPLICATION->arIncludeDebug;

		$cache_log = COption::GetOptionString('perfmon', 'cache_log') === 'Y';
		$large_cache_log = COption::GetOptionString('perfmon', 'large_cache_log') === 'Y';
		$large_cache_size = floatval(COption::GetOptionString('perfmon', 'large_cache_size')) * 1024;
		$sql_log = COption::GetOptionString('perfmon', 'sql_log') === 'Y';
		$slow_sql_log = COption::GetOptionString('perfmon', 'slow_sql_log') === 'Y';
		$slow_sql_time = floatval(COption::GetOptionString('perfmon', 'slow_sql_time'));

		if ($slow_sql_log)
		{
			self::removeQueries($arQueryDebug, $arIncludeDebug, $slow_sql_time, $cache_log);
		}

		$query_count = 0;
		$query_time = 0.0;
		if ($sql_log)
		{
			self::countQueries($query_count, $query_time, $arQueryDebug, $arIncludeDebug);
		}

		$comps_count = 0;
		$comps_time = 0.0;
		if ($sql_log || $cache_log)
		{
			self::countComponents($comps_count, $comps_time, $arIncludeDebug);
		}

		$cache_count = [];
		/** @var \Bitrix\Main\Diag\CacheTracker $arCacheDebug */
		$arCacheDebug = null;
		if ($cache_log)
		{
			$arCacheDebug = \Bitrix\Main\Diag\CacheTracker::getCacheTracking();

			if ($large_cache_log)
			{
				self::removeCaches($large_cache_size, $arCacheDebug, $arIncludeDebug);
			}

			self::countCache($arCacheDebug, $cache_count);
			foreach ($arIncludeDebug as $ar)
			{
				if (array_key_exists('REL_PATH', $ar))
				{
					self::countCache($ar['CACHE'], $cache_count);
				}
			}
		}

		if ($_SERVER['SCRIPT_NAME'] == '/bitrix/urlrewrite.php' && isset($_SERVER['REAL_FILE_PATH']))
		{
			$SCRIPT_NAME = $_SERVER['REAL_FILE_PATH'];
		}
		elseif ($_SERVER['SCRIPT_NAME'] == '/404.php' && isset($_SERVER['REAL_FILE_PATH']))
		{
			$SCRIPT_NAME = $_SERVER['REAL_FILE_PATH'];
		}
		else
		{
			$SCRIPT_NAME = $_SERVER['SCRIPT_NAME'];
		}

		$arFields = [
			'~DATE_HIT' => $DB->GetNowFunction(),
			'IS_ADMIN' => defined('ADMIN_SECTION') ? 'Y' : 'N',
			'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'] ?? '',
			'SERVER_NAME' => $_SERVER['SERVER_NAME'] ?? '',
			'SERVER_PORT' => $_SERVER['SERVER_PORT'] ?? '',
			'SCRIPT_NAME' => $SCRIPT_NAME,
			'REQUEST_URI' => $_SERVER['REQUEST_URI'] ?? '',
			'INCLUDED_FILES' => function_exists('get_included_files') ? count(get_included_files()) : false,
			'MEMORY_PEAK_USAGE' => function_exists('memory_get_peak_usage') ? memory_get_peak_usage() : false,
			'CACHE_TYPE' => COption::GetOptionString('main', 'component_cache_on', 'Y') == 'Y' ? 'Y' : 'N',
			'~CACHE_SIZE' => intval($GLOBALS['CACHE_STAT_BYTES']),
			'~CACHE_COUNT_R' => intval($cache_count['R'] ?? 0),
			'~CACHE_COUNT_W' => intval($cache_count['W'] ?? 0),
			'~CACHE_COUNT_C' => intval($cache_count['C'] ?? 0),
			'QUERIES' => $query_count,
			'~QUERIES_TIME' => $query_time,
			'SQL_LOG' => $sql_log ? 'Y' : 'N',
			'COMPONENTS' => $comps_count,
			'~COMPONENTS_TIME' => $comps_time,
			'~MENU_RECALC' => $APPLICATION->_menu_recalc_counter,
		];
		CPerfomanceKeeper::SetPageTimes($START_EXEC_CURRENT_TIME, $arFields);

		if ($query_count || $comps_count || $cache_count)
		{
			$HIT_ID = $DB->Add('b_perf_hit', $arFields);
		}
		else
		{
			$HIT_ID = false;
		}

		$NN = 0;
		if ($HIT_ID && $cache_log)
		{
			self::saveCaches($HIT_ID, false, $arCacheDebug, $NN);
		}

		$MM = 0;
		if ($HIT_ID && $sql_log)
		{
			if (is_array($arQueryDebug))
			{
				self::saveQueries($HIT_ID, false, $arQueryDebug, $MM);
			}
		}

		if ($HIT_ID && ($sql_log || $cache_log))
		{
			foreach ($arIncludeDebug as $ii => $ar)
			{
				if (!array_key_exists('REL_PATH', $ar))
				{
					continue;
				}

				$cache_count = [];
				if ($cache_log)
				{
					self::countCache($ar['CACHE'], $cache_count);
				}

				$arFields = [
					'HIT_ID' => $HIT_ID,
					'NN' => $ii,
					'CACHE_TYPE' => $ar['CACHE_TYPE'],
					'~CACHE_SIZE' => intval($ar['CACHE_SIZE']),
					'~CACHE_COUNT_R' => intval($cache_count['R'] ?? 0),
					'~CACHE_COUNT_W' => intval($cache_count['W'] ?? 0),
					'~CACHE_COUNT_C' => intval($cache_count['C'] ?? 0),
					'COMPONENT_TIME' => $ar['TIME'],
					'QUERIES' => $ar['QUERY_COUNT'],
					'QUERIES_TIME' => $ar['QUERY_TIME'],
					'COMPONENT_NAME' => $ar['REL_PATH'],
				];
				$COMP_ID = $DB->Add('b_perf_component', $arFields);

				if ($sql_log && is_array($ar['QUERIES']))
				{
					self::saveQueries($HIT_ID, $COMP_ID, $ar['QUERIES'], $MM);
				}

				if ($cache_log && is_array($ar['CACHE']))
				{
					self::saveCaches($HIT_ID, $COMP_ID, $ar['CACHE'], $NN);
				}
			}
		}

		global $perfmonErrors;
		if ($HIT_ID && (count($perfmonErrors) > 0))
		{
			foreach ($perfmonErrors as $arError)
			{
				$arError['HIT_ID'] = $HIT_ID;
				$DB->Add('b_perf_error', $arError);
			}
		}
	}

	public static function SetPageTimes($START_EXEC_CURRENT_TIME, &$arFields)
	{
		list($usec, $sec) = explode(' ', $START_EXEC_CURRENT_TIME);
		$CURRENT_TIME = (float)$sec + (float)$usec;

		if (defined('START_EXEC_PROLOG_BEFORE_1'))
		{
			$PROLOG_BEFORE_1 = (float)START_EXEC_PROLOG_BEFORE_1;

			if (defined('START_EXEC_AGENTS_1') && defined('START_EXEC_AGENTS_2'))
			{
				$AGENTS_2 = (float)constant('START_EXEC_AGENTS_2');
				$AGENTS_1 = (float)constant('START_EXEC_AGENTS_1');
				$arFields['~AGENTS_TIME'] = $AGENTS_2 - $AGENTS_1;
			}
			else
			{
				$arFields['~AGENTS_TIME'] = 0;
			}

			if (defined('START_EXEC_EVENTS_1') && defined('START_EXEC_EVENTS_2'))
			{
				list($usec, $sec) = explode(' ', constant('START_EXEC_EVENTS_2'));
				$EVENTS_2 = (float)$sec + (float)$usec;
				list($usec, $sec) = explode(' ', constant('START_EXEC_EVENTS_1'));
				$EVENTS_1 = (float)$sec + (float)$usec;
				$arFields['~EVENTS_TIME'] = $EVENTS_2 - $EVENTS_1;
			}
			else
			{
				$arFields['~EVENTS_TIME'] = 0;
			}

			if (defined('START_EXEC_PROLOG_AFTER_1') && defined('START_EXEC_PROLOG_AFTER_2'))
			{
				$PROLOG_AFTER_1 = (float)START_EXEC_PROLOG_AFTER_1;
				$PROLOG_AFTER_2 = (float)START_EXEC_PROLOG_AFTER_2;
				$arFields['~PROLOG_AFTER_TIME'] = $PROLOG_AFTER_2 - $PROLOG_AFTER_1;

				$arFields['~PROLOG_BEFORE_TIME'] = $PROLOG_AFTER_1 - $PROLOG_BEFORE_1;

				$arFields['~PROLOG_TIME'] = round($PROLOG_AFTER_2 - $PROLOG_BEFORE_1 - $arFields['~AGENTS_TIME'], 4);

				if (defined('START_EXEC_EPILOG_BEFORE_1'))
				{
					$EPILOG_BEFORE_1 = (float)START_EXEC_EPILOG_BEFORE_1;

					$arFields['~WORK_AREA_TIME'] = $EPILOG_BEFORE_1 - $PROLOG_AFTER_2;

					if (defined('START_EXEC_EPILOG_AFTER_1'))
					{
						$EPILOG_AFTER_1 = (float)START_EXEC_EPILOG_AFTER_1;
						$arFields['~EPILOG_BEFORE_TIME'] = $EPILOG_AFTER_1 - $EPILOG_BEFORE_1;
						$arFields['~EPILOG_AFTER_TIME'] = $CURRENT_TIME - $EPILOG_AFTER_1 - $arFields['~EVENTS_TIME'];
					}
					else
					{
						$arFields['~EPILOG_BEFORE_TIME'] = 0;
						$arFields['~EPILOG_AFTER_TIME'] = 0;
					}

					$arFields['~EPILOG_TIME'] = $CURRENT_TIME - $EPILOG_BEFORE_1;
				}
				else
				{
					$arFields['~WORK_AREA_TIME'] = $CURRENT_TIME - $PROLOG_AFTER_2;
					$arFields['~EPILOG_BEFORE_TIME'] = 0;
					$arFields['~EPILOG_AFTER_TIME'] = 0;
					$arFields['~EPILOG_TIME'] = 0;
				}
			}

			$arFields['~PAGE_TIME'] = $CURRENT_TIME - $PROLOG_BEFORE_1;
		}
		else
		{
			$arFields['~PAGE_TIME'] = $CURRENT_TIME - START_EXEC_TIME;
		}
	}

	public static function removeQueries(&$arQueryDebug, &$arIncludeDebug, $slow_sql_time, $preserveComponents = false)
	{
		if (is_array($arQueryDebug))
		{
			foreach ($arQueryDebug as $i => $arQueryInfo)
			{
				if ($arQueryInfo['TIME'] < $slow_sql_time)
				{
					unset($arQueryDebug[$i]);
				}
			}
		}

		if (is_array($arIncludeDebug))
		{
			foreach ($arIncludeDebug as $i => $ar)
			{
				if (array_key_exists('REL_PATH', $ar) && is_array($ar['QUERIES']))
				{
					foreach ($ar['QUERIES'] as $N => $arQueryInfo)
					{
						if ($arQueryInfo['TIME'] < $slow_sql_time)
						{
							unset($arIncludeDebug[$i]['QUERIES'][$N]);
						}
					}

					if (!$preserveComponents)
					{
						if (empty($arIncludeDebug[$i]['QUERIES']))
						{
							unset($arIncludeDebug[$i]);
						}
					}
				}
				else
				{
					if (!$preserveComponents)
					{
						unset($arIncludeDebug[$i]);
					}
				}
			}
		}
	}

	public static function countQueries(&$query_count, &$query_time, $arQueryDebug, $arIncludeDebug)
	{
		$query_count = 0;
		$query_time = 0.0;

		if (is_array($arQueryDebug))
		{
			foreach ($arQueryDebug as $arQueryInfo)
			{
				$query_count++;
				$query_time += $arQueryInfo['TIME'];
			}
		}

		foreach ($arIncludeDebug as $ar)
		{
			if (array_key_exists('REL_PATH', $ar) && is_array($ar['QUERIES']))
			{
				foreach ($ar['QUERIES'] as $arQueryInfo)
				{
					$query_count++;
					$query_time += $arQueryInfo['TIME'];
				}
			}
		}
	}

	public static function countComponents(&$comps_count, &$comps_time, $arIncludeDebug)
	{
		$comps_count = 0;
		$comps_time = 0.0;

		foreach ($arIncludeDebug as $ar)
		{
			if (array_key_exists('REL_PATH', $ar))
			{
				$comps_count++;
				$comps_time += $ar['TIME'];
			}
		}
	}

	public static function removeCaches($large_cache_size, &$arCacheDebug, &$arIncludeDebug)
	{
		if (is_array($arCacheDebug))
		{
			foreach ($arCacheDebug as $i => $arCacheInfo)
			{
				if (
					(
						$arCacheInfo['cache_size'] > 0
						&& $arCacheInfo['cache_size'] < $large_cache_size
					) || (
						$arCacheInfo['operation'] != 'W'
						&& $arCacheInfo['operation'] != 'R'
					)
				)
				{
					unset($arCacheDebug[$i]);
				}
			}
		}

		if (is_array($arIncludeDebug))
		{
			foreach ($arIncludeDebug as $i => $ar)
			{
				if (array_key_exists('REL_PATH', $ar) && isset($ar['CACHE']) && is_array($ar['CACHE']))
				{
					foreach ($ar['CACHE'] as $N => $arCacheInfo)
					{
						if (
							(
								$arCacheInfo['cache_size'] > 0
								&& $arCacheInfo['cache_size'] < $large_cache_size
							) || (
								$arCacheInfo['operation'] != 'W'
								&& $arCacheInfo['operation'] != 'R'
							)
						)
						{
							unset($arIncludeDebug[$i]['CACHE'][$N]);
						}
					}
				}
			}
		}
	}

	public static function countCache($arCacheDebug, &$cache_count)
	{
		if (is_array($arCacheDebug))
		{
			foreach ($arCacheDebug as $arCacheInfo)
			{
				if (isset($cache_count[$arCacheInfo['operation']]))
				{
					$cache_count[$arCacheInfo['operation']]++;
				}
				else
				{
					$cache_count[$arCacheInfo['operation']] = 1;
				}
			}
		}
	}

	public static function findCaller($trace, &$module_id, &$comp_id)
	{
		$module_id = false;
		$comp_id = false;
		foreach ($trace as $arCallInfo)
		{
			if (array_key_exists('file', $arCallInfo))
			{
				$file = mb_strtolower(str_replace('\\', '/', $arCallInfo['file']));

				if (
					!$module_id
					&& !preg_match('/\\/(database|cache|managedcache)\\.php$/', $file)
				)
				{
					$match = [];
					if (preg_match('#.*/bitrix/modules/(.+?)/#', $file, $match))
					{
						$module_id = $match[1];
					}
				}

				$match = [];
				if (
					!$comp_id
					&& preg_match('#.*/(?:bitrix|install)/components/(.+?)/(.+?)/#', $file, $match)
				)
				{
					$comp_id = $match[1] . ':' . $match[2];
				}

				if ($module_id && $comp_id)
				{
					break;
				}
			}
		}
	}

	public static function saveQueries($HIT_ID, $COMP_ID, $arQueryDebug, &$NN)
	{
		global $DB;

		foreach ($arQueryDebug as $arQueryInfo)
		{
			self::findCaller($arQueryInfo['TRACE'], $module_id, $comp_id);

			$arFields = [
				'HIT_ID' => $HIT_ID,
				'COMPONENT_ID' => $COMP_ID,
				'NN' => ++$NN,
				'QUERY_TIME' => $arQueryInfo['TIME'],
				'NODE_ID' => intval($arQueryInfo['NODE_ID']),
				'MODULE_NAME' => $module_id,
				'COMPONENT_NAME' => $comp_id,
				'SQL_TEXT' => $arQueryInfo['QUERY'],
			];
			$SQL_ID = $DB->Add('b_perf_sql', $arFields, ['SQL_TEXT']);

			if ($SQL_ID && COption::GetOptionString('perfmon', 'sql_backtrace') === 'Y')
			{
				$pl = mb_strlen(rtrim($_SERVER['DOCUMENT_ROOT'], '/'));
				foreach ($arQueryInfo['TRACE'] as $i => $arCallInfo)
				{
					$DB->Add('b_perf_sql_backtrace', [
						'ID' => 1,
						'SQL_ID' => $SQL_ID,
						'NN' => $i,
						'FILE_NAME' => mb_substr($arCallInfo['file'], $pl),
						'LINE_NO' => $arCallInfo['line'],
						'CLASS_NAME' => $arCallInfo['class'],
						'FUNCTION_NAME' => $arCallInfo['function'],
					]);
				}
			}
		}
	}

	public static function saveCaches($HIT_ID, $COMP_ID, $arCacheDebug, &$NN)
	{
		global $DB;

		foreach ($arCacheDebug as $arCacheInfo)
		{
			self::findCaller($arCacheInfo['TRACE'], $module_id, $comp_id);

			$arFields = [
				'HIT_ID' => $HIT_ID,
				'COMPONENT_ID' => $COMP_ID,
				'NN' => ++$NN,
				'CACHE_SIZE' => $arCacheInfo['cache_size'],
				'OP_MODE' => $arCacheInfo['operation'],
				'MODULE_NAME' => $module_id,
				'COMPONENT_NAME' => $comp_id,
				'BASE_DIR' => $arCacheInfo['basedir'],
				'INIT_DIR' => $arCacheInfo['initdir'],
				'FILE_NAME' => $arCacheInfo['filename'],
				'FILE_PATH' => $arCacheInfo['path'],
			];
			$DB->Add('b_perf_cache', $arFields);
		}
	}

	public static function IsActive()
	{
		$bActive = false;
		foreach (GetModuleEvents('main', 'OnPageStart', true) as $arEvent)
		{
			if (isset($arEvent['TO_MODULE_ID']) && $arEvent['TO_MODULE_ID'] == 'perfmon')
			{
				$bActive = true;
				break;
			}
		}
		return $bActive;
	}

	public static function SetActive($bActive = false, $end_time = 0)
	{
		if ($bActive)
		{
			if (!CPerfomanceKeeper::IsActive())
			{
				RegisterModuleDependences('main', 'OnPageStart', 'perfmon', 'CPerfomanceKeeper', 'OnPageStart', '1');
				RegisterModuleDependences('main', 'OnEpilog', 'perfmon', 'CPerfomanceKeeper', 'OnEpilog', '1000');
				RegisterModuleDependences('main', 'OnAfterEpilog', 'perfmon', 'CPerfomanceKeeper', 'OnBeforeAfterEpilog', '1');
				RegisterModuleDependences('main', 'OnAfterEpilog', 'perfmon', 'CPerfomanceKeeper', 'OnAfterAfterEpilog', '1000');
				RegisterModuleDependences('main', 'OnLocalRedirect', 'perfmon', 'CPerfomanceKeeper', 'OnAfterAfterEpilog', '1000');
			}
			COption::SetOptionInt('perfmon', 'end_time', $end_time);
		}
		else
		{
			if (CPerfomanceKeeper::IsActive())
			{
				UnRegisterModuleDependences('main', 'OnPageStart', 'perfmon', 'CPerfomanceKeeper', 'OnPageStart');
				UnRegisterModuleDependences('main', 'OnEpilog', 'perfmon', 'CPerfomanceKeeper', 'OnEpilog');
				UnRegisterModuleDependences('main', 'OnAfterEpilog', 'perfmon', 'CPerfomanceKeeper', 'OnBeforeAfterEpilog');
				UnRegisterModuleDependences('main', 'OnAfterEpilog', 'perfmon', 'CPerfomanceKeeper', 'OnAfterAfterEpilog');
				UnRegisterModuleDependences('main', 'OnLocalRedirect', 'perfmon', 'CPerfomanceKeeper', 'OnAfterAfterEpilog');
			}
		}
	}
}