0); define('TRACE_WATCHDOG', count(array_filter(variable_get('trace_watchdog', array()))) > 0); define('TRACE_QUERIES', count(array_filter(variable_get('trace_queries', array()))) > 0); define('TRACE_HOOKS', count(array_filter(variable_get('trace_hooks', array()))) > 0); define('TRACE_REQUEST', variable_get('trace_request', TRUE)); define('TRACE_DEBUG', variable_get('trace_debug', FALSE)); define('TRACE_STACKTRACE', variable_get('trace_stacktrace', FALSE)); define('TRACE_MAX_STRING', (integer)variable_get('trace_max_string', 100)); define('TRACE_ERRORLINE', variable_get('trace_errorline', FALSE)); define('TRACE_ACTIVE', TRACE_ENABLED && trace_path_enabled($_GET['q'])); if (TRACE_ACTIVE) { define('TRACE_TIME', trace_time(FALSE)); define('TRACE_ID', substr(uniqid(''), 5)); require_once TRACE_PATH . '/trace.api.inc'; _trace_ob_start(); } ////////////////////////////////////////////////////////////////////////////// /** * Implements hook_help(). Provide online user help. */ function trace_help($section) { if (TRACE_ACTIVE && TRACE_HOOKS) { trace_hook('help'); } switch ($section) { case 'admin/modules#name': return t('trace'); case 'admin/modules#description': return t('Adds tracing facilities for Drupal hook invocations, database queries and PHP errors.'); } } /** * Implements hook_menu(). Define menu items and page callbacks. */ function trace_menu($may_cache) { if (TRACE_ACTIVE && TRACE_HOOKS) { trace_hook('menu'); } $items = array(); if ($may_cache) { $items[] = array( 'path' => 'admin/settings/trace', 'title' => t('Tracing'), 'description' => t('Enable or disable tracing facilities for Drupal hook invocations, database queries and PHP errors.'), 'callback' => 'drupal_get_form', 'callback arguments' => array('trace_settings'), 'access' => user_access('administer site configuration'), ); } return $items; } /** * Implements hook_settings(). Declare administrative settings for a module. */ function trace_settings() { if (TRACE_ACTIVE && TRACE_HOOKS) { trace_hook('settings'); } require_once TRACE_PATH . '/trace.admin.inc'; return trace_settings_form(); } /** * Implements hook_init(). Perform setup tasks. */ function trace_init() { if (TRACE_ACTIVE) { if (TRACE_DEBUG) { trace('debug', '$_ENV = ' . trace_format_php($_ENV)); trace('debug', '$_SERVER = ' . trace_format_php($_SERVER)); trace('debug', '$_COOKIE = ' . trace_format_php($_COOKIE)); trace('debug', '$_REQUEST = ' . trace_format_php($_REQUEST)); if ($_SERVER['REQUEST_METHOD'] == 'GET') { trace('debug', '$_GET = ' . trace_format_php($_GET)); } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { trace('debug', '$_POST = ' . trace_format_php($_POST)); trace('debug', '$_FILES = ' . trace_format_php($_FILES)); } trace('debug', '$_SESSION = ' . trace_format_php($_SESSION)); } if (TRACE_HOOKS) { trace_hook('init'); } } } /** * Implements hook_exit(). Perform cleanup tasks. */ function trace_exit($destination = NULL) { if (TRACE_ACTIVE) { if (TRACE_HOOKS) { trace_hook('exit'); } if (TRACE_DEBUG) { trace('debug', 'drupal_set_message() = ' . trace_format_php(drupal_set_message())); trace('debug', 'drupal_set_header() = ' . trace_format_php(explode("\n", drupal_set_header()))); } } } ////////////////////////////////////////////////////////////////////////////// // TRACE START/EXIT HANDLERS /** * */ function trace_time($delta = TRUE) { list($usec, $sec) = explode(' ', microtime()); $time = ((double)$usec + (double)$sec); // NOTE: we can't rely on $_SERVER['REQUEST_TIME'] for the request start // time as it isn't accurate enough (no sub-second precision) and it's not // available prior to PHP 5.1, anyway. return !$delta ? $time : $time - TRACE_TIME; } /** * Matches the user's traceability settings against the page path. */ function trace_path_enabled($path) { $alias = drupal_get_path_alias($path); $regexp = '/^(' . preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\($|\|)/'), array('|', '.*', '\1' . preg_quote(variable_get('site_frontpage', 'node'), '/') . '\2'), preg_quote(TRACE_PAGES, '/')) . ')$/'; return !(TRACE_FILTER xor preg_match($regexp, $alias)); } function _trace_ob_start() { $timestamp = _trace_format_timestamp(); if (TRACE_ERRORS) { // Extend Drupal's error handler so that we catch warnings and errors: set_error_handler('trace_error_handler'); } if (TRACE_HOOKS) { // Inject tracing code into all selected Drupal hooks: foreach (trace_hook_list(NULL, TRUE) as $hook) { trace_hook_define('trace', $hook); } } if (TRACE_QUERIES) { variable_set('dev_query', TRUE); // FIXME! } if (TRACE_REQUEST) { $msg = array($timestamp, $_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']); trace('request', implode(' ', $msg), 0); } ob_start('_trace_ob_handler'); } function _trace_ob_handler($buffer) { global $queries; $timestamp = _trace_format_timestamp(); // Ensure we're in the correct working directory, since some web servers // (e.g. Apache) mess this up for exit-time handlers: chdir(dirname($_SERVER['SCRIPT_FILENAME'])); // This is not ideal, but if no hooks were traced, we have to get the // queries here at the end of the page request. Timing information is // going to be entirely unaccurate, of course. if (TRACE_QUERIES) { trace_queries(); } // Output some statistics about the page response if (TRACE_DEBUG && count($queries) > 0) { trace('stats', sprintf('Total SQL queries: %d', count($queries))); } if (TRACE_REQUEST) { $msg = array($timestamp, _trace_get_http_response()); trace('respnse', implode(' ', $msg)); } return $buffer; // Pass through page contents unmodified } //////////////////////////////////////////////////////////////////////////////