Skip to content

Commit

Permalink
Adds ability to associate code stack traces to executed SQL queries #51
Browse files Browse the repository at this point in the history
  • Loading branch information
madalinoprea committed Mar 16, 2016
1 parent 00a2cb0 commit f4ea4dd
Show file tree
Hide file tree
Showing 13 changed files with 423 additions and 30 deletions.
78 changes: 72 additions & 6 deletions code/Debug/Helper/Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class Sheep_Debug_Helper_Data extends Mage_Core_Helper_Data
const DEBUG_OPTION_PERSIST_EXPIRATION_PATH = 'sheep_debug/options/persist_expiration';
const DEBUG_OPTION_FORCE_VARIEN_PROFILE_PATH = 'sheep_debug/options/force_varien_profile';
const DEBUG_OPTION_USE_STORE_LOCALE = 'sheep_debug/options/use_store_locale';
const DEBUG_OPTION_CAPTURE_SQL_STACKTRACE = 'sheep_debug/options/capture_sql_stacktrace';
const DEBUG_OPTION_STRIP_ZEND_DB_TRACES = 'sheep_debug/options/strip_zend_db_traces';
const DEBUG_OPTION_TRIM_MAGENTO_DIR = 'sheep_debug/options/trim_magento_basedir';


/**
Expand Down Expand Up @@ -100,7 +103,7 @@ public function getExceptionLogFilename($store)
* Returns results as assoc array for specified SQL query
*
* @param string $query
* @param array $queryParams
* @param array $queryParams
* @return array
* @throws Zend_Db_Statement_Exception
*/
Expand Down Expand Up @@ -133,8 +136,8 @@ public function getSqlProfiler()
* Flattens an xml tree into an associate array where key represents path
*
* @param Mage_Core_Model_Config_Element $xml
* @param array $arr
* @param string $parentKey
* @param array $arr
* @param string $parentKey
* @return array|void
*/
public function xml2array($xml, array &$arr, $parentKey = '')
Expand Down Expand Up @@ -223,7 +226,7 @@ public function isAllowed()
/**
* Returns specified number formatted based on current locale
*
* @param $number
* @param $number
* @param int $precision
* @return string
* @throws Zend_Locale_Exception
Expand Down Expand Up @@ -255,6 +258,36 @@ public function formatMemorySize($size, $precision = 2)
}


/**
* Formats a stack trace array generated by debug_backtrace
*
* Inspired by @see mageDebugBacktrace
*
* @param array $trace
* @param string $stripFilepath
* @return string
*/
public function formatStacktrace(array $trace, $stripFilepath = '', $trimPath='')
{
$out = '';
foreach ($trace as $index => $row) {

if ($stripFilepath && isset($row['file']) && strpos($row['file'], $stripFilepath) !== false) {
continue;
}

if ($trimPath) {
$row['file'] = str_replace($trimPath, '', $row['file']);
}

// sometimes there is undefined index 'file'
@$out .= "[$index] {$row['file']}:{$row['line']}\n";
}

return $out;
}


/**
* Returns peak memory in bytes
*
Expand Down Expand Up @@ -393,7 +426,7 @@ public function getBlockFilename($blockClass)
/**
* Returns all xml files that contains layout updates.
*
* @param int $storeId store identifier
* @param int $storeId store identifier
* @param string $designArea
* @return array
*/
Expand Down Expand Up @@ -465,6 +498,39 @@ public function canEnableVarienProfiler()
}


/**
* Checks if we want to capture stack trace for SQL
*
* @return bool
*/
public function canEnableSqlStacktrace()
{
return (bool)Mage::getStoreConfig(self::DEBUG_OPTION_CAPTURE_SQL_STACKTRACE);
}


/**
* Checks if we should not display traces related to Zend Db files
*
* @return bool
*/
public function canStripZendDbTrace()
{
return (bool)Mage::getStoreConfig(self::DEBUG_OPTION_STRIP_ZEND_DB_TRACES);
}


/**
* Checks if we can remove Magento base dir from stack trace file paths
*
* @return bool
*/
public function canTrimMagentoBaseDir()
{
return (bool)Mage::getStoreConfig(self::DEBUG_OPTION_TRIM_MAGENTO_DIR);
}


/**
* Checks if we need to translate or format extension content based on
* store locale
Expand Down Expand Up @@ -498,7 +564,7 @@ public function getBlockName(Mage_Core_Block_Abstract $block)
{
$blockName = $block->getParentBlock() ?
"{$block->getParentBlock()->getNameInLayout()}_{$block->getNameInLayout()}" :
"{$block->getNameInLayout()}" ;
"{$block->getNameInLayout()}";

if ($block->getBlockAlias()) {
$blockName .= "_{$block->getBlockAlias()}";
Expand Down
14 changes: 14 additions & 0 deletions code/Debug/Helper/Url.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,20 @@ public function getExplainQueryUrl($token, $index)
}


/**
* Returns url that returns SQL stack trace
*
* @see \Sheep_Debug_ModelController::stacktraceSqlAction
* @param string $token
* @param int $index
* @return string
*/
public function getQueryStacktraceUrl($token, $index)
{
return $this->getUrl('model/stacktraceSql', array('token' => $token, 'index' => $index));
}


/**
* Returns url that shows definition for specified block class
*
Expand Down
107 changes: 107 additions & 0 deletions code/Debug/Model/Db/Profiler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

class Sheep_Debug_Model_Db_Profiler extends Zend_Db_Profiler
{
protected $stackTraces = array();
protected $captureStacktraces = false;

/**
* Responsible to copy queries from current profiler and set this instance sql profiler
*
* @throws Zend_Db_Profiler_Exception
*/
public function replaceProfiler()
{
/** @var Magento_Db_Adapter_Pdo_Mysql $connection */
$connection = Mage::getSingleton('core/resource')->getConnection('core_write');
$currentProfile = $connection->getProfiler();

if ($currentProfile) {
// Copy queries
$this->_queryProfiles = $currentProfile->_queryProfiles;
}

$this->setEnabled($currentProfile->getEnabled());
$connection->setProfiler($this);
}


/**
* @param $queryId
* @return string
* @throws Zend_Db_Profiler_Exception
*/
public function parentQueryEnd($queryId)
{
return parent::queryEnd($queryId);
}


/**
* Returns stack trace as array
*
* @return string
*/
public function getStackTrace()
{
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
return array_slice($trace, 2);
}


/**
* Calls parent implementation and saves stack trace
*
* @param int $queryId
* @return string
*/
public function queryEnd($queryId)
{
$result = $this->parentQueryEnd($queryId);

if ($this->captureStacktraces) {
$this->stackTraces[$queryId] = $this->getStackTrace();
}

return $result;
}


/**
* Returns an array of SQL queries
*
* @return Sheep_Debug_Model_Query[]
*/
public function getQueryModels()
{
$queries = array();
foreach ($this->_queryProfiles as $queryId => $queryProfile) {
$queryModel = Mage::getModel('sheep_debug/query');
$stacktrace = array_key_exists($queryId, $this->stackTraces) ? $this->stackTraces[$queryId] : '';
$queryModel->init($queryProfile, $stacktrace);

$queries[] = $queryModel;
}

return $queries;
}


/**
* @param boolean $captureStacktraces
*/
public function setCaptureStacktraces($captureStacktraces)
{
$this->captureStacktraces = $captureStacktraces;
}


/**
* @return boolean
*/
public function isCaptureStacktraces()
{
return $this->captureStacktraces;
}

}
5 changes: 5 additions & 0 deletions code/Debug/Model/Observer.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ public function startProfiling()
if (Mage::helper('sheep_debug')->canEnableVarienProfiler()) {
Varien_Profiler::enable();
}

// use customer Zend Db Profiler that also records stack traces
$stackTraceProfiler = Mage::getModel('sheep_debug/db_profiler');
$stackTraceProfiler->setCaptureStacktraces(Mage::helper('sheep_debug')->canEnableSqlStacktrace());
$stackTraceProfiler->replaceProfiler();
}


Expand Down
13 changes: 12 additions & 1 deletion code/Debug/Model/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,22 @@ class Sheep_Debug_Model_Query
protected $query;
protected $queryParams;
protected $elapsedSecs;
protected $stacktrace;


/**
* Sheep_Debug_Model_Query constructor.
*
* @param Zend_Db_Profiler_Query $profilerQuery
* @param string $stacktrace
*/
public function __construct(Zend_Db_Profiler_Query $profilerQuery)
public function init(Zend_Db_Profiler_Query $profilerQuery, $stacktrace = '')
{
$this->queryType = $profilerQuery->getQueryType();
$this->query = $profilerQuery->getQuery();
$this->queryParams = $profilerQuery->getQueryParams();
$this->elapsedSecs = $profilerQuery->getElapsedSecs();
$this->stacktrace = $stacktrace;
}


Expand Down Expand Up @@ -73,4 +76,12 @@ public function getElapsedSecs()
return $this->elapsedSecs;
}

/**
* @return string
*/
public function getStacktrace()
{
return $this->stacktrace;
}

}
4 changes: 2 additions & 2 deletions code/Debug/Model/RequestInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -428,9 +428,9 @@ public function initQueries()
$this->queries = array();

$profiler = Mage::helper('sheep_debug')->getSqlProfiler();
if ($profiler->getEnabled()) {
if ($profiler->getEnabled() && $profiler instanceof Sheep_Debug_Model_Db_Profiler) {
/** @var Zend_Db_Profiler_Query[] $queries */
$this->queries = $profiler->getQueryProfiles() ?: array();
$this->queries = $profiler->getQueryModels() ?: array();
$this->setQueryCount($profiler->getTotalNumQueries());
$this->setQueryTime($profiler->getTotalElapsedSecs());
}
Expand Down
Loading

0 comments on commit f4ea4dd

Please sign in to comment.