You are here

memcache_admin.module in Memcache API and Integration 7

For the collection and display of memcache stats.

This module adds a small .js file to makes sure that the HTML displaying the stats is inside of the <body> part of the HTML document.

File

memcache_admin/memcache_admin.module
View source
<?php

/**
 * @file
 * For the collection and display of memcache stats.
 *
 * This module adds a small .js file to makes sure that the HTML displaying the
 * stats is inside of the <body> part of the HTML document.
 */

/**
 * Implements hook_init().
 */
function memcache_admin_init() {
  global $user;
  if ($user->uid == 0 || strstr($_SERVER['PHP_SELF'], 'update.php') || isset($_GET['q']) && (in_array($_GET['q'], array(
    'upload/js',
    'admin/content/node-settings/rebuild',
  )) || substr($_GET['q'], 0, strlen('system/files')) == 'system/files' || substr($_GET['q'], 0, strlen('batch')) == 'batch' || strstr($_GET['q'], 'autocomplete'))) {

    // update.php relies on standard error handler.
  }
  else {
    if ($user->uid) {
      drupal_add_js(drupal_get_path('module', 'memcache_admin') . '/memcache.js');
    }
    register_shutdown_function('memcache_admin_shutdown');
  }
}

/**
 * Implements hook_perm().
 */
function memcache_admin_permission() {
  return array(
    'access memcache statistics' => array(
      'title' => t('Access memcache statistics'),
    ),
    'access slab cachedump' => array(
      'title' => t('Access cachedump of memcache slab'),
      'restrict access' => TRUE,
    ),
  );
}

/**
 * Implements hook_menu().
 */
function memcache_admin_menu() {
  $items['admin/config/system/memcache'] = array(
    'title' => 'Memcache',
    'description' => 'Show or hide memcache statistics at the bottom of each page.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'memcache_admin_admin_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
  );
  $items['admin/reports/memcache'] = array(
    'title' => 'Memcache statistics',
    'description' => "View statistics for all configured memcache servers.",
    'page callback' => 'memcache_admin_stats',
    'access arguments' => array(
      'access memcache statistics',
    ),
    'weight' => 1,
  );
  $memcache_servers = variable_get('memcache_servers', array(
    '127.0.0.1:11211' => 'default',
  ));
  $clusters = array();
  foreach ($memcache_servers as $server => $bin) {
    if (function_exists('dmemcache_object_cluster') && ($cluster = dmemcache_object_cluster($bin))) {
      $name = $cluster['cluster'];
      $clusters[$name]['servers'][] = $server;
      $clusters[$name]['bin'] = _memcache_admin_get_bin_for_cluster($cluster['cluster']);
    }
  }
  $count = 0;
  foreach ($clusters as $cluster => $cluster_info) {
    if ($cluster_info['bin']) {
      if (empty($current_cluster)) {
        $current_cluster = arg(3);
        if (empty($current_cluster)) {
          $current_cluster = $cluster;
        }
      }
      $items["admin/reports/memcache/{$cluster}"] = array(
        'title' => $cluster,
        'type' => $count == 0 ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
        'page callback' => 'memcache_admin_stats',
        'page arguments' => array(
          $cluster,
        ),
        'access arguments' => array(
          'access memcache statistics',
        ),
        'weight' => $count++,
      );
      foreach ($cluster_info['servers'] as $server) {
        $items["admin/reports/memcache/{$cluster}/{$server}"] = array(
          'title' => check_plain($server),
          'type' => MENU_CALLBACK,
          'page callback' => 'memcache_admin_stats_raw',
          'page arguments' => array(
            $cluster,
            $server,
          ),
          'access arguments' => array(
            'access memcache statistics',
          ),
        );
        foreach (memcache_admin_stats_types($cluster) as $type) {
          $items["admin/reports/memcache/{$cluster}/{$server}/{$type}"] = array(
            'type' => MENU_CALLBACK,
            'page callback' => 'memcache_admin_stats_raw',
            'page arguments' => array(
              $cluster,
              $server,
              $type,
            ),
            'title' => $type,
            'access arguments' => array(
              'access memcache statistics',
            ),
          );
        }
      }
    }
  }
  return $items;
}

/**
 * Settings form.
 */
function memcache_admin_admin_settings() {
  $form['show_memcache_statistics'] = array(
    '#type' => 'checkbox',
    '#title' => t('Show memcache statistics at the bottom of each page'),
    '#default_value' => variable_get('show_memcache_statistics', FALSE),
    '#description' => t("These statistics will be visible to users with the 'access memcache statistics' permission."),
  );
  return system_settings_form($form);
}

/**
 * Helper function. Returns the bin name.
 */
function _memcache_admin_default_bin($bin) {
  if ($bin == 'default') {
    return 'cache';
  }
  return $bin;
}

/**
 * Statistics report: format total and open connections.
 */
function _memcache_admin_stats_connections($stats) {
  return t('!current open of !total total', array(
    '!current' => number_format($stats['curr_connections']),
    '!total' => number_format($stats['total_connections']),
  ));
}

/**
 * Statistics report: calculate # of set cmds and total cmds.
 */
function _memcache_admin_stats_sets($stats) {
  if ($stats['cmd_set'] + $stats['cmd_get'] == 0) {
    $sets = 0;
  }
  else {
    $sets = $stats['cmd_set'] / ($stats['cmd_set'] + $stats['cmd_get']) * 100;
  }
  if (empty($stats['uptime'])) {
    $average = 0;
  }
  else {
    $average = $sets / $stats['uptime'];
  }
  return t('!average/s; !set sets (!sets%) of !total commands', array(
    '!average' => number_format($average, 2),
    '!sets' => number_format($sets, 2),
    '!set' => number_format($stats['cmd_set']),
    '!total' => number_format($stats['cmd_set'] + $stats['cmd_get']),
  ));
}

/**
 * Statistics report: calculate # of get cmds, broken down by hits and misses.
 */
function _memcache_admin_stats_gets($stats) {
  if ($stats['cmd_set'] + $stats['cmd_get'] == 0) {
    $gets = 0;
  }
  else {
    $gets = $stats['cmd_get'] / ($stats['cmd_set'] + $stats['cmd_get']) * 100;
  }
  if (empty($stats['uptime'])) {
    $average = 0;
  }
  else {
    $average = $stats['cmd_get'] / $stats['uptime'];
  }
  return t('!average/s; !total gets (!gets%); !hit hits (!percent_hit%) !miss misses (!percent_miss%)', array(
    '!average' => number_format($average, 2),
    '!gets' => number_format($gets, 2),
    '!hit' => number_format($stats['get_hits']),
    '!percent_hit' => $stats['cmd_get'] > 0 ? number_format($stats['get_hits'] / $stats['cmd_get'] * 100, 2) : '0.00',
    '!miss' => number_format($stats['get_misses']),
    '!percent_miss' => $stats['cmd_get'] > 0 ? number_format($stats['get_misses'] / $stats['cmd_get'] * 100, 2) : '0.00',
    '!total' => number_format($stats['cmd_get']),
  ));
}

/**
 * Statistics report: calculate # of increments and decrements.
 */
function _memcache_admin_stats_counters($stats) {
  if (!is_array($stats)) {
    $stats = array();
  }
  $stats += array(
    'incr_hits' => 0,
    'incr_misses' => 0,
    'decr_hits' => 0,
    'decr_misses' => 0,
  );
  return t('!incr increments, !decr decrements', array(
    '!incr' => number_format($stats['incr_hits'] + $stats['incr_misses']),
    '!decr' => number_format($stats['decr_hits'] + $stats['decr_misses']),
  ));
}

/**
 * Statistics report: calculate bytes transferred.
 */
function _memcache_admin_stats_transfer($stats) {
  if ($stats['bytes_written'] == 0) {
    $written = 0;
  }
  else {
    $written = $stats['bytes_read'] / $stats['bytes_written'] * 100;
  }
  return t('!to:!from (!written% to cache)', array(
    '!to' => format_size((int) $stats['bytes_read']),
    '!from' => format_size((int) $stats['bytes_written']),
    '!written' => number_format($written, 2),
  ));
}

/**
 * Statistics report: calculate per-connection averages.
 */
function _memcache_admin_stats_average($stats) {
  if ($stats['total_connections'] == 0) {
    $get = 0;
    $set = 0;
    $read = 0;
    $write = 0;
  }
  else {
    $get = $stats['cmd_get'] / $stats['total_connections'];
    $set = $stats['cmd_set'] / $stats['total_connections'];
    $read = $stats['bytes_written'] / $stats['total_connections'];
    $write = $stats['bytes_read'] / $stats['total_connections'];
  }
  return t('!read in !get gets; !write in !set sets', array(
    '!get' => number_format($get, 2),
    '!set' => number_format($set, 2),
    '!read' => format_size(number_format($read, 2)),
    '!write' => format_size(number_format($write, 2)),
  ));
}

/**
 * Statistics report: calculate available memory.
 */
function _memcache_admin_stats_memory($stats) {
  if ($stats['limit_maxbytes'] == 0) {
    $percent = 0;
  }
  else {
    $percent = 100 - $stats['bytes'] / $stats['limit_maxbytes'] * 100;
  }
  return t('!available (!percent%) of !total', array(
    '!available' => format_size($stats['limit_maxbytes'] - $stats['bytes']),
    '!percent' => number_format($percent, 2),
    '!total' => format_size($stats['limit_maxbytes']),
  ));
}

/**
 * Helper function, reverse map the memcache_bins variable.
 */
function memcache_admin_bin_mapping($bin = 'cache') {
  $bins = array_flip(variable_get('memcache_bins', array(
    'cache' => 'default',
  )));
  if (isset($bins[$bin])) {
    return $bins[$bin];
  }
  else {

    // The default bin is 'cache'.
    return _memcache_admin_default_bin($bin);
  }
}

/**
 * Callback for the Memcache Stats page.
 *
 * @return string
 *   The page output.
 */
function memcache_admin_stats($bin = 'default') {
  if (!class_exists('MemCacheDrupal', FALSE)) {
    return t('There is a problem with your memcache configuration. Please review !readme for help resolving the following issue: %error.', array(
      '!readme' => l('README.txt', 'http://cgit.drupalcode.org/memcache/tree/README.txt?id=refs/heads;id2=7.x-1.x'),
      '%error' => t('cache_backends not properly configured in settings.php, failed to load required file memcache.inc'),
    ));
  }
  if ($memcache_debug_log = variable_get('memcache_debug_log', FALSE)) {
    if (variable_get('memcache_debug_verbose', FALSE)) {
      $verbose = t('verbose');
    }
    else {
      $verbose = '';
    }
    drupal_set_message(t('You are writing !verbose debug logs to !debug_log.', array(
      '!verbose' => $verbose,
      '!debug_log' => $memcache_debug_log,
    )), 'warning');
  }
  $bin = memcache_admin_bin_mapping($bin);
  $output = '';
  $server = array();
  $stats = dmemcache_stats($bin, 'default', TRUE);
  if (empty($stats[$bin])) {

    // Failed to load statistics. Provide a useful error about where to get
    // more information and help.
    drupal_set_message(t('Failed to retreive statistics. There may be a problem with your Memcache configuration. Please review %readme and !more for more information.', array(
      '%readme' => 'README.txt',
      'admin/reports/status',
      '!more' => module_exists('memcache') ? t('visit the Drupal admin !status page', array(
        '!status' => l(t('status report'), 'admin/reports/status'),
      )) : t('!enable the memcache module', array(
        '!enable' => l(t('enable'), 'admin/modules', array(
          'fragment' => 'edit-modules-performance-and-scalability',
        )),
      )),
    )), 'error');
  }
  else {
    $stats = $stats[$bin];
    $aggregate = array_pop($stats);
    $mc = dmemcache_object($bin);
    if ($mc instanceof Memcached) {
      $version = t('Memcached v!version', array(
        '!version' => phpversion('Memcached'),
      ));
    }
    elseif ($mc instanceof Memcache) {
      $version = t('Memcache v!version', array(
        '!version' => phpversion('Memcache'),
      ));
    }
    else {
      $version = t('Unknown');
      drupal_set_message(t('Failed to detect the memcache PECL extension.'), 'error');
    }
    $memcache_servers = variable_get('memcache_servers', array(
      '127.0.0.1:11211' => 'default',
    ));
    foreach ($stats as $server => $statistics) {
      if (empty($statistics['uptime'])) {
        drupal_set_message(t('Failed to connect to server at %address.', array(
          '%address' => $server,
        )), 'error');
      }
      else {
        $servers[] = $server;
        $data['server_overview'][$server] = t('v!version running !uptime', array(
          '!version' => check_plain($statistics['version']),
          '!uptime' => format_interval($statistics['uptime']),
        ));
        $data['server_pecl'][$server] = t('n/a');
        $data['server_serialize'][$server] = t('n/a');
        $data['server_time'][$server] = format_date($statistics['time']);
        $data['server_connections'][$server] = _memcache_admin_stats_connections($statistics);
        $data['cache_sets'][$server] = _memcache_admin_stats_sets($statistics);
        $data['cache_gets'][$server] = _memcache_admin_stats_gets($statistics);
        $data['cache_counters'][$server] = _memcache_admin_stats_counters($statistics);
        $data['cache_transfer'][$server] = _memcache_admin_stats_transfer($statistics);
        $data['cache_average'][$server] = _memcache_admin_stats_average($statistics);
        $data['memory_available'][$server] = _memcache_admin_stats_memory($statistics);
        $data['memory_evictions'][$server] = number_format($statistics['evictions']);
      }
    }

    // Don't display aggregate totals if there's only one server.
    if (count($servers) == 1) {
      $aggregate = array();
    }

    // Build a custom report array.
    $report = array();

    // Report server uptime.
    $item = array(
      'label' => t('Uptime'),
      'servers' => $data['server_overview'],
    );
    if (count($aggregate)) {
      $item['total'] = t('n/a');
    }
    $report['uptime'][] = $item;

    // Report server PECL extension.
    $item = array(
      'label' => t('PECL extension'),
    );
    if (count($aggregate)) {
      $item['servers'] = $data['server_pecl'];
      $item['total'] = $version;
    }
    else {
      $item['servers'] = array(
        $servers[0] => $version,
      );
    }
    $report['uptime'][] = $item;

    // Report which serialize function is being used.
    $item = array(
      'label' => t('Serialize function'),
    );
    $serialize_function = dmemcache_serialize();
    if ($serialize_function != 'serialize') {
      $serialize_function = t('!function v!version', array(
        '!function' => $serialize_function,
        '!version' => phpversion(dmemcache_serialize_extension()),
      ));
    }
    if (count($aggregate)) {
      $item['servers'] = $data['server_serialize'];
      $item['total'] = $serialize_function;
    }
    else {
      $item['servers'] = array(
        $servers[0] => $serialize_function,
      );
    }
    $report['uptime'][] = $item;

    // Report server time.
    $item = array(
      'label' => t('Time'),
      'servers' => $data['server_time'],
    );
    if (count($aggregate)) {
      $item['total'] = t('n/a');
    }
    $report['uptime'][] = $item;

    // Report number of connections.
    $item = array(
      'label' => t('Connections'),
      'servers' => $data['server_connections'],
    );
    if (count($aggregate)) {
      $item['total'] = _memcache_admin_stats_connections($aggregate);
    }
    $report['uptime'][] = $item;
    $stats = array(
      'sets' => t('Sets'),
      'gets' => t('Gets'),
      'counters' => t('Counters'),
      'transfer' => t('Transferred'),
      'average' => t('Per-connection average'),
    );
    foreach ($stats as $type => $label) {
      $item = array(
        'label' => $label,
        'servers' => $data["cache_{$type}"],
      );
      if (count($aggregate)) {
        $func = "_memcache_admin_stats_{$type}";
        $item['total'] = $func($aggregate);
      }
      $report['stats'][] = $item;
    }

    // Report on available memory.
    $item = array(
      'label' => t('Available memory'),
      'servers' => $data['memory_available'],
    );
    if (count($aggregate)) {
      $item['total'] = _memcache_admin_stats_memory($aggregate);
    }
    $report['memory'][] = $item;

    // Report on memory evictions.
    $item = array(
      'label' => t('Evictions'),
      'servers' => $data['memory_evictions'],
    );
    if (count($aggregate)) {
      $item['total'] = number_format($aggregate['evictions']);
    }
    $report['memory'][] = $item;
    $output = theme('memcache_admin_stats_table', array(
      'bin' => $bin,
      'servers' => $servers,
      'report' => $report,
    ));
  }
  return $output;
}

/**
 * Callback for the server statistics page.
 */
function memcache_admin_stats_raw($bin, $server, $type = 'default') {
  $cluster = memcache_admin_bin_mapping($bin);
  $slab = (int) arg(7);
  if (arg(6) == 'cachedump' && !empty($slab) && user_access('access slab cachedump')) {
    $stats = dmemcache_stats($cluster, arg(7), FALSE);
  }
  else {
    $stats = dmemcache_stats($cluster, $type, FALSE);
  }
  $breadcrumbs = array(
    l(t('Home'), NULL),
    l(t('Administer'), 'admin'),
    l(t('Reports'), 'admin/reports'),
    l(t('Memcache'), 'admin/reports/memcache'),
    l(t($bin), "admin/reports/memcache/{$bin}"),
  );
  if ($type == 'slabs' && arg(6) == 'cachedump' && user_access('access slab cachedump')) {
    $breadcrumbs[] = l($server, "admin/reports/memcache/{$bin}/{$server}");
    $breadcrumbs[] = l(t('slabs'), "admin/reports/memcache/{$bin}/{$server}/{$type}");
  }
  drupal_set_breadcrumb($breadcrumbs);
  if (isset($stats[$cluster][$server]) && is_array($stats[$cluster][$server]) && count($stats[$cluster][$server])) {
    $output = theme('memcache_admin_stats_raw_table', array(
      'cluster' => $cluster,
      'server' => $server,
      'stats' => $stats[$cluster][$server],
      'type' => $type,
    ));
  }
  elseif ($type == 'slabs' && is_array($stats[$cluster]) && count($stats[$cluster])) {
    $output = theme('memcache_admin_stats_raw_table', array(
      'cluster' => $cluster,
      'server' => $server,
      'stats' => $stats[$cluster],
      'type' => $type,
    ));
  }
  else {
    $output = theme('memcache_admin_stats_raw_table', array(
      'cluster' => $cluster,
      'server' => $server,
      'stats' => array(),
      'type' => $type,
    ));
    drupal_set_message(t('No @type statistics for this bin.', array(
      '@type' => $type,
    )));
  }
  return $output;
}

/**
 * Implements hook_theme().
 */
function memcache_admin_theme() {
  return array(
    'memcache_admin_stats_table' => array(
      'variables' => array(
        'bin' => NULL,
        'servers' => NULL,
        'report' => NULL,
      ),
    ),
    'memcache_admin_stats_raw_table' => array(
      'variables' => array(
        'bin' => NULL,
        'server' => NULL,
        'stats' => NULL,
        'type' => NULL,
      ),
    ),
  );
}

/**
 * Theme function for rendering the output from memcache_admin_stats.
 */
function theme_memcache_admin_stats_table($variables) {
  $bin = $variables['bin'];
  $servers = $variables['servers'];
  $stats = $variables['report'];
  $output = '';
  $links = array();
  $memcache_bins = variable_get('memcache_bins', array(
    'cache' => 'default',
  ));
  foreach ($servers as $server) {
    $link_bin = $memcache_bins[$bin];
    $links[] = l($server, check_plain("admin/reports/memcache/{$link_bin}/{$server}"));
  }
  if (count($servers) > 1) {
    $headers = array_merge(array(
      '',
      t('Totals'),
    ), $links);
  }
  else {
    $headers = array_merge(array(
      '',
    ), $links);
  }
  foreach ($stats as $table => $data) {
    $rows = array();
    foreach ($data as $row) {
      $r = array();
      $r[] = $row['label'];
      if (isset($row['total'])) {
        $r[] = $row['total'];
      }
      foreach ($row['servers'] as $server) {
        $r[] = $server;
      }
      $rows[] = $r;
    }
    $output .= theme('table', array(
      'header' => $headers,
      'rows' => $rows,
    ));
  }
  return $output;
}

/**
 * Returns an array of available statistics types.
 */
function memcache_admin_stats_types($bin) {
  module_load_include('inc', 'memcache', 'dmemcache');
  if ($mc = dmemcache_object($bin)) {
    if ($mc instanceof Memcache) {

      // TODO: Determine which versions of the PECL memcache extension have
      // these other stats types: 'malloc', 'maps', optionally detect this
      // version and expose them.  These stats are "subject to change without
      // warning" unfortunately.
      return array(
        'default',
        'slabs',
        'items',
        'sizes',
      );
    }
    else {

      // The Memcached PECL extension only offers the default statistics.
      return array(
        'default',
      );
    }
  }
  else {
    return array();
  }
}

/**
 * Theme function to produce a table of statistics.
 */
function theme_memcache_admin_stats_raw_table($variables) {
  $cluster = $variables['cluster'];
  $server = $variables['server'];
  $stats = $variables['stats'];
  $current_type = isset($variables['type']) ? $variables['type'] : 'default';
  $memcache_bins = variable_get('memcache_bins', array());
  $bin = isset($memcache_bins[$cluster]) ? $memcache_bins[$cluster] : 'default';

  // Provide navigation for the various memcache stats types.
  if (count(memcache_admin_stats_types($bin)) > 1) {
    foreach (memcache_admin_stats_types($bin) as $type) {
      if ($current_type == $type) {
        $links[] = '<strong>' . l(t($type), "admin/reports/memcache/{$bin}/{$server}/" . ($type == 'default' ? '' : $type)) . '</strong>';
      }
      else {
        $links[] = l(t($type), "admin/reports/memcache/{$bin}/{$server}/" . ($type == 'default' ? '' : $type));
      }
    }
  }
  $output = !empty($links) ? implode(' | ', $links) : '';
  $headers = array(
    t('Property'),
    t('Value'),
  );
  $rows = array();

  // Items are returned as an array within an array within an array.  We step
  // in one level to properly display the contained statistics.
  if ($current_type == 'items' && isset($stats['items'])) {
    $stats = $stats['items'];
  }
  foreach ($stats as $key => $value) {

    // Add navigation for getting a cachedump of individual slabs.
    if (($current_type == 'slabs' || $current_type == 'items') && is_int($key) && user_access('access slab cachedump')) {
      $key = l($key, "admin/reports/memcache/{$bin}/{$server}/slabs/cachedump/{$key}");
    }
    if (is_array($value)) {
      $rs = array();
      foreach ($value as $k => $v) {

        // Format timestamp when viewing cachedump of individual slabs.
        if ($current_type == 'slabs' && user_access('access slab cachedump') && arg(6) == 'cachedump' && $k == 0) {
          $k = t('Size');
          $v = format_size($v);
        }
        elseif ($current_type == 'slabs' && user_access('access slab cachedump') && arg(6) == 'cachedump' && $k == 1) {
          $k = t('Expire');
          $full_stats = dmemcache_stats($cluster, 'default');
          $infinite = $full_stats[$cluster][$server]['time'] - $full_stats[$cluster][$server]['uptime'];
          if ($v == $infinite) {
            $v = t('infinite');
          }
          else {
            $v = t('in @time', array(
              '@time' => format_interval($v - time()),
            ));
          }
        }
        $rs[] = array(
          check_plain($k),
          check_plain($v),
        );
      }
      $rows[] = array(
        $key,
        theme('table', array(
          'rows' => $rs,
        )),
      );
    }
    else {
      $rows[] = array(
        check_plain($key),
        check_plain($value),
      );
    }
  }
  $output .= theme('table', array(
    'header' => $headers,
    'rows' => $rows,
  ));
  return $output;
}

/**
 * Retrieve the bin for any given cluster.
 *
 * @param string $cluster
 *   Cluster ID
 *
 * @return string
 *   The name of the bin.
 */
function _memcache_admin_get_bin_for_cluster($cluster) {
  static $cluster_map = array();
  if (!isset($cluster_map[$cluster])) {
    $memcache_bins = variable_get('memcache_bins', array());
    if ($mapping = array_search($cluster, $memcache_bins)) {
      $cluster_map[$cluster] = $mapping;
    }
    else {
      $cluster_map[$cluster] = 'default';
    }
  }
  return $cluster_map[$cluster];
}

/**
 * Helper function. Calculate a percentage.
 */
function _memcache_admin_stats_percent($a, $b) {
  if ($a == 0) {
    return 0;
  }
  elseif ($b == 0) {
    return 100;
  }
  else {
    return $a / ($a + $b) * 100;
  }
}

/**
 * Displays memcache stats in the footer. This is run as a shutdown function.
 *
 * @see memcache_admin_init()
 */
function memcache_admin_shutdown() {
  global $_dmemcache_stats;

  // Don't call theme() during shutdown if the registry has been rebuilt (such
  // as when enabling/disabling modules on admin/build/modules) as things break.
  // Instead, simply exit without displaying admin statistics for this page
  // load.  See http://drupal.org/node/616282 for discussion.
  if (!function_exists('theme_get_registry') || !theme_get_registry()) {
    return;
  }

  // Try not to break non-HTML pages.
  if (function_exists('drupal_get_http_header')) {
    $header = drupal_get_http_header('content-type');
    if ($header) {
      $formats = array(
        'xml',
        'javascript',
        'json',
        'plain',
        'image',
        'application',
        'csv',
        'x-comma-separated-values',
      );
      foreach ($formats as $format) {
        if (strstr($header, $format)) {
          return;
        }
      }
    }
    else {

      // Workaround for CKEditor, see https://www.drupal.org/node/2556999
      return;
    }
  }
  if (variable_get('show_memcache_statistics', FALSE) && function_exists('user_access') && user_access('access memcache statistics')) {
    $output = '';
    if (!empty($_dmemcache_stats['ops'])) {
      foreach ($_dmemcache_stats['ops'] as $row => $stats) {
        $_dmemcache_stats['ops'][$row][0] = check_plain($stats[0]);
        $_dmemcache_stats['ops'][$row][1] = number_format($stats[1], 2);
        $hits = number_format(_memcache_admin_stats_percent($stats[2], $stats[3]), 1);
        $misses = number_format(_memcache_admin_stats_percent($stats[3], $stats[2]), 1);
        $_dmemcache_stats['ops'][$row][2] = number_format($stats[2]) . " ({$hits}%)";
        $_dmemcache_stats['ops'][$row][3] = number_format($stats[3]) . " ({$misses}%)";
      }
      $variables = array(
        'header' => array(
          t('operation'),
          t('total ms'),
          t('total hits'),
          t('total misses'),
        ),
        'rows' => $_dmemcache_stats['ops'],
      );
      $output .= theme('table', $variables);
    }
    if (!empty($_dmemcache_stats['all'])) {
      foreach ($_dmemcache_stats['all'] as $row => $stats) {
        $_dmemcache_stats['all'][$row][1] = check_plain($stats[1]);
        $_dmemcache_stats['all'][$row][2] = check_plain($stats[2]);
        $_dmemcache_stats['all'][$row][3] = check_plain($stats[3]);
      }
      $variables = array(
        'header' => array(
          t('ms'),
          t('operation'),
          t('bin'),
          t('key'),
          t('status'),
        ),
        'rows' => $_dmemcache_stats['all'],
      );
      $output .= theme('table', $variables);
    }
    if (!empty($output)) {

      // This makes sure all of the HTML is within the <body> even though this
      // <script> is outside it.
      print '<div id="memcache-devel"><h2>' . t('Memcache statistics for @url', array(
        '@url' => current_path(),
      )) . '</h2>' . $output . '</div>';
    }
  }
}

Functions

Namesort descending Description
memcache_admin_admin_settings Settings form.
memcache_admin_bin_mapping Helper function, reverse map the memcache_bins variable.
memcache_admin_init Implements hook_init().
memcache_admin_menu Implements hook_menu().
memcache_admin_permission Implements hook_perm().
memcache_admin_shutdown Displays memcache stats in the footer. This is run as a shutdown function.
memcache_admin_stats Callback for the Memcache Stats page.
memcache_admin_stats_raw Callback for the server statistics page.
memcache_admin_stats_types Returns an array of available statistics types.
memcache_admin_theme Implements hook_theme().
theme_memcache_admin_stats_raw_table Theme function to produce a table of statistics.
theme_memcache_admin_stats_table Theme function for rendering the output from memcache_admin_stats.
_memcache_admin_default_bin Helper function. Returns the bin name.
_memcache_admin_get_bin_for_cluster Retrieve the bin for any given cluster.
_memcache_admin_stats_average Statistics report: calculate per-connection averages.
_memcache_admin_stats_connections Statistics report: format total and open connections.
_memcache_admin_stats_counters Statistics report: calculate # of increments and decrements.
_memcache_admin_stats_gets Statistics report: calculate # of get cmds, broken down by hits and misses.
_memcache_admin_stats_memory Statistics report: calculate available memory.
_memcache_admin_stats_percent Helper function. Calculate a percentage.
_memcache_admin_stats_sets Statistics report: calculate # of set cmds and total cmds.
_memcache_admin_stats_transfer Statistics report: calculate bytes transferred.