Index: /trunk/admin/xcache.tpl.php
===================================================================
--- /trunk/admin/xcache.tpl.php	(revision 521)
+++ /trunk/admin/xcache.tpl.php	(revision 522)
@@ -17,4 +17,7 @@
 	<col align="right" />
 	<col align="right" />
+	<col />
+	<col align="right" />
+	<col align="right" />
 	<col align="right" />
 	<col align="right" />
@@ -32,4 +35,7 @@
 		<th><?php echo _T('Compiling'); ?></th>
 		<th><?php echo _T('Hits'); ?></th>
+		<th><?php echo _T('Hits/H'); ?></th>
+		<th><?php echo _T('Hits 24H'); ?></th>
+		<th><?php echo _T('Hits/S'); ?></th>
 		<th><?php echo _T('Misses'); ?></th>
 		<th><?php echo _T('Clogs'); ?></th>
@@ -65,4 +71,10 @@
 		$ci_avail = size($ci['avail']);
 		$ci = number_formats($ci, $numkeys);
+
+		$hits_avg_h     = number_format(array_avg($ci['hits_by_hour']), 2);
+		$hits_avg_s     = number_format(array_avg($ci['hits_by_second']), 2);
+		$hits_graph_h   = hits_to_graph($ci['hits_by_hour']);
+		$hits_graph_h_w = count($ci['hits_by_hour']) * 2;
+
 		if (!empty($ci['istotal'])) {
 			$ci['compiling']    = '-';
@@ -95,4 +107,7 @@
 		<td>{$ci['compiling']}</td>
 		<td>{$ci['hits']}</td>
+		<td>{$hits_avg_h}</td>
+		<td><div class="hitsgraph" style="width: {$hits_graph_h_w}px">{$hits_graph_h}</div></td>
+		<td>{$hits_avg_s}</td>
 		<td>{$ci['misses']}</td>
 		<td>{$ci['clogs']}</td>
Index: /trunk/admin/xcache.php
===================================================================
--- /trunk/admin/xcache.php	(revision 521)
+++ /trunk/admin/xcache.php	(revision 522)
@@ -110,4 +110,70 @@
 		$b = $c;
 		$html[] = '<div style="background: rgb(' . "$r,$g,$b" . ')"></div>';
+	}
+	return implode('', $html);
+}
+
+function calc_total(&$total, $data)
+{
+	foreach ($data as $k => $v) {
+		switch ($k) {
+		case 'type':
+		case 'cache_name':
+		case 'cacheid':
+		case 'free_blocks':
+			continue 2;
+		}
+		if (!isset($total[$k])) {
+			$total[$k] = $v;
+		}
+		else {
+			switch ($k) {
+			case 'his_by_hour':
+			case 'his_by_second':
+				foreach ($data[$k] as $kk => $vv) {
+					$total[$k][$kk] += $vv;
+				}
+				break;
+
+			default:
+				$total[$k] += $v;
+			}
+		}
+	}
+}
+
+function array_avg($a)
+{
+	if (count($a) == 0) {
+		return '';
+	}
+	return array_sum($a) / count($a);
+}
+
+function bar_percent($percent)
+{
+	$r = 220 + (int) ($percent * 25);
+	$g = $b = 220 - (int) ($percent * 220);
+	$percent = (int) ($percent * 100);
+	return '<div>'
+		. '<div style="height: ' . (100 - $percent) . '%"></div>'
+		. '<div style="background: rgb(' . "$r,$g,$b" . '); height: ' . $percent . '%"></div>'
+		. '</div>';
+}
+
+function hits_to_graph($hits)
+{
+	$max = 0;
+	foreach ($hits as $v) {
+		if ($max < $v) {
+			$max = $v;
+		}
+	}
+	if (!$max) {
+		return '';
+	}
+	$html = array();
+	foreach ($hits as $v) {
+		$html[] = bar_percent($v / $max);
 	}
 	return implode('', $html);
@@ -196,19 +262,5 @@
 	$cacheinfos[] = $data;
 	if ($pcnt >= 2) {
-		foreach ($data as $k => $v) {
-			switch ($k) {
-			case 'type':
-			case 'cache_name':
-			case 'cacheid':
-			case 'free_blocks':
-				continue 2;
-			}
-			if (!isset($total[$k])) {
-				$total[$k] = $v;
-			}
-			else {
-				$total[$k] += $v;
-			}
-		}
+		calc_total($total, $data);
 	}
 }
@@ -234,20 +286,5 @@
 	$cacheinfos[] = $data;
 	if ($pcnt >= 2) {
-		foreach ($data as $k => $v) {
-			switch ($k) {
-			case 'type':
-			case 'cache_name':
-			case 'cacheid':
-			case 'free_blocks':
-			case 'gc':
-				continue 2;
-			}
-			if (!isset($total[$k])) {
-				$total[$k] = $v;
-			}
-			else {
-				$total[$k] += $v;
-			}
-		}
+		calc_total($total, $data);
 	}
 }
Index: /trunk/admin/xcache.css
===================================================================
--- /trunk/admin/xcache.css	(revision 521)
+++ /trunk/admin/xcache.css	(revision 522)
@@ -28,4 +28,8 @@
 .freeblockgraph { border: 1px solid gray; border-bottom: 0px; }
 
+.hitsgraph { height: 20px; border: solid gray; border-width: 1px 0 1px 0; margin: auto; }
+.hitsgraph div { float: left; width: 2px; height: 100%; }
+.bitsgraph div div { float: none; width: 100%; }
+
 .switcher, h1, h2 { text-align: center; display: block; }
 .switcher * { color: blue; }
Index: /trunk/admin/common-zh-traditional-utf-8.lang.php
===================================================================
--- /trunk/admin/common-zh-traditional-utf-8.lang.php	(revision 521)
+++ /trunk/admin/common-zh-traditional-utf-8.lang.php	(revision 522)
@@ -28,4 +28,10 @@
 		'Hits'
 		=> '命中',
+		'Hits 24H'
+		=> '24H 分布',
+		'Hits/H'
+		=> '命中/H',
+		'Hits/S'
+		=> '命中/S',
 		'Misses'
 		=> '錯過',
Index: /trunk/admin/help-en.lang.php
===================================================================
--- /trunk/admin/help-en.lang.php	(revision 458)
+++ /trunk/admin/help-en.lang.php	(revision 522)
@@ -8,4 +8,7 @@
 <dt><?php echo _T('Compiling'); ?>: </dt><dd>Compiling flag, "yes" if the cache is busy compiling php script</dd>
 <dt><?php echo _T('Hits'); ?>: </dt><dd>Cache Hits, hit=a var/php is loaded from this cache</dd>
+<dt><?php echo _T('Hits/H'); ?>: </dt><dd>Average Hits per Hour. Only last 24 hours is logged</dd>
+<dt><?php echo _T('Hits 24H'); ?>: </dt><dd>Hits 24 Hours. Hits graph of last 24 hours</dd>
+<dt><?php echo _T('Hits/S'); ?>: </dt><dd>Average Hits per Second. Only last 5 seconds is logged</dd>
 <dt><?php echo _T('Misses'); ?>: </dt><dd>Cache Misses, miss=a var/php is requested but not in the cache</dd>
 <dt><?php echo _T('Clogs'); ?>: </dt><dd>Compiling Clogs, clog=compiling is needed but avoided to wait(be blocked) when the cache is busy compiling already</dd>
Index: /trunk/admin/common-zh-simplified-utf-8.lang.php
===================================================================
--- /trunk/admin/common-zh-simplified-utf-8.lang.php	(revision 521)
+++ /trunk/admin/common-zh-simplified-utf-8.lang.php	(revision 522)
@@ -28,4 +28,10 @@
 		'Hits'
 		=> '命中',
+		'Hits 24H'
+		=> '24H 分布',
+		'Hits/H'
+		=> '命中/H',
+		'Hits/S'
+		=> '命中/S',
 		'Misses'
 		=> '错过',
Index: /trunk/admin/help-zh-simplified-utf-8.lang.php
===================================================================
--- /trunk/admin/help-zh-simplified-utf-8.lang.php	(revision 458)
+++ /trunk/admin/help-zh-simplified-utf-8.lang.php	(revision 522)
@@ -8,4 +8,7 @@
 <dt><?php echo _T('Compiling'); ?>: </dt><dd>编译标记, 当共享内存区正在编译 php 脚本时标记为 "yes"</dd>
 <dt><?php echo _T('Hits'); ?>: </dt><dd>共享内存命中次数, 命中=从该共享内存载入php或者变量</dd>
+<dt><?php echo _T('Hits/H'); ?>: </dt><dd>每小时命中次数. 只统计最后 24 小时</dd>
+<dt><?php echo _T('Hits 24H'); ?>: </dt><dd>24 小时命中分布图. 图表现是最后 24 小时的命中次数</dd>
+<dt><?php echo _T('Hits/S'); ?>: </dt><dd>每秒命中次数. 只统计最后 5 秒</dd>
 <dt><?php echo _T('Misses'); ?>: </dt><dd>共享内存错过次数, 错过=请求的php或者变量并不在该共享内存内</dd>
 <dt><?php echo _T('Clogs'); ?>: </dt><dd>编译阻塞跳过, 阻塞=当需该共享内存区负责编译时, 其他进程/现成无法访问此共享内存. 跳过=XCache 自动判断阻塞的共享内存区自动跳过阻塞等待, 直接使用非共享内存方式继续处理请求</dd>
Index: /trunk/run-xcachetest
===================================================================
--- /trunk/run-xcachetest	(revision 271)
+++ /trunk/run-xcachetest	(revision 522)
@@ -19,5 +19,5 @@
 if test -z "$TEST_PHP_USER" ; then
 	TEST_PHP_USER="$PHP_SRC/tests"
-	for i in Zend ZendEngine2 ext/standard/tests ext/reflection/tests ; do
+	for i in Zend ZendEngine2 ext/standard/tests ext/reflection/tests ext/spl/tests; do
 		if test -d "$PHP_SRC/$i" ; then
 			TEST_PHP_USER="$TEST_PHP_USER,$PHP_SRC/$i"
Index: /trunk/utils.h
===================================================================
--- /trunk/utils.h	(revision 506)
+++ /trunk/utils.h	(revision 522)
@@ -100,5 +100,5 @@
 #ifdef E_STRICT
 	int orig_user_error_handler_error_reporting;
-	ZEND_API void (*orig_zend_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args);
+	void (*orig_zend_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args);
 	zend_uint compilererror_cnt;
 	zend_uint compilererror_size;
Index: /trunk/mkopcode_spec.awk
===================================================================
--- /trunk/mkopcode_spec.awk	(revision 393)
+++ /trunk/mkopcode_spec.awk	(revision 522)
@@ -15,5 +15,9 @@
 			exit
 		}
-		printf "\tOPSPEC(%10s, %10s, %10s, %10s)\n", array[1], array[2], array[3], array[4]
+		comment = "";
+		if (match($0, /\/\* (\d+) \*\//, comments)) {
+			comment = comments[1];
+		}
+		printf "\tOPSPEC(%10s, %10s, %10s, %10s)%s\n", array[1], array[2], array[3], array[4], comment;
 		next
 	}
Index: /trunk/phpdc.phpr
===================================================================
--- /trunk/phpdc.phpr	(revision 52)
+++ /trunk/phpdc.phpr	(revision 522)
@@ -13,5 +13,5 @@
 }
 
-$dc = &new Decompiler();
+$dc = new Decompiler();
 if (isset($argv[1])) {
 	$dc->decompileFile($argv[1]);
Index: /trunk/xcache.c
===================================================================
--- /trunk/xcache.c	(revision 516)
+++ /trunk/xcache.c	(revision 522)
@@ -313,4 +313,53 @@
 /* }}} */
 #endif
+static inline zend_uint advance_wrapped(zend_uint val, zend_uint count) /* {{{ */
+{
+	if (val + 1 >= count) {
+		return 0;
+	}
+	return val + 1;
+}
+/* }}} */
+static void xc_counters_inc(time_t *curtime, zend_uint *curslot, time_t period, zend_ulong *counters, zend_uint count TSRMLS_DC) /* {{{ */
+{
+	time_t n = XG(request_time) / period;
+	if (*curtime != n) {
+		zend_uint target_slot = n % count;
+		if (n - *curtime > period) {
+			memset(counters, 0, sizeof(counters[0]) * count);
+		}
+		else {
+			zend_uint slot;
+			for (slot = advance_wrapped(*curslot, count);
+					slot != target_slot;
+					slot = advance_wrapped(slot, count)) {
+				counters[slot] = 0;
+			}
+			counters[target_slot] = 0;
+		}
+		*curtime = n;
+		*curslot = target_slot;
+	}
+	counters[*curslot] ++;
+}
+/* }}} */
+static void xc_cache_hit_dmz(xc_cache_t *cache TSRMLS_DC) /* {{{ */
+{
+	cache->hits ++;
+
+	xc_counters_inc(&cache->hits_by_hour_cur_time
+			, &cache->hits_by_hour_cur_slot, 60 * 60
+			, cache->hits_by_hour
+			, sizeof(cache->hits_by_hour) / sizeof(cache->hits_by_hour[0])
+			TSRMLS_CC);
+
+	xc_counters_inc(&cache->hits_by_second_cur_time
+			, &cache->hits_by_second_cur_slot
+			, 1
+			, cache->hits_by_second
+			, sizeof(cache->hits_by_second) / sizeof(cache->hits_by_second[0])
+			TSRMLS_CC);
+}
+/* }}} */
 
 /* helper function that loop through each entry */
@@ -455,5 +504,6 @@
 static void xc_fillinfo_dmz(int cachetype, xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
 {
-	zval *blocks;
+	zval *blocks, *hits;
+	int i;
 	const xc_block_t *b;
 #ifndef NDEBUG
@@ -487,4 +537,17 @@
 		add_assoc_null_ex(return_value, ZEND_STRS("gc"));
 	}
+	MAKE_STD_ZVAL(hits);
+	array_init(hits);
+	for (i = 0; i < sizeof(cache->hits_by_hour) / sizeof(cache->hits_by_hour[0]); i ++) {
+		add_next_index_long(hits, (long) cache->hits_by_hour[i]);
+	}
+	add_assoc_zval_ex(return_value, ZEND_STRS("hits_by_hour"), hits);
+
+	MAKE_STD_ZVAL(hits);
+	array_init(hits);
+	for (i = 0; i < sizeof(cache->hits_by_second) / sizeof(cache->hits_by_second[0]); i ++) {
+		add_next_index_long(hits, (long) cache->hits_by_second[i]);
+	}
+	add_assoc_zval_ex(return_value, ZEND_STRS("hits_by_second"), hits);
 
 	MAKE_STD_ZVAL(blocks);
@@ -1257,5 +1320,5 @@
 		stored_xce = xc_entry_find_dmz(&xce TSRMLS_CC);
 		if (stored_xce) {
-			cache->hits ++;
+			xc_cache_hit_dmz(cache TSRMLS_CC);
 
 			TRACE("hit %s, holding", stored_xce->name.str.val);
@@ -2113,5 +2176,5 @@
 	} LEAVE_LOCK(xce.cache);
 	if (found) {
-		xce.cache->hits ++;
+		xc_cache_hit_dmz(xce.cache TSRMLS_CC);
 	}
 	else {
@@ -2194,5 +2257,5 @@
 	} LEAVE_LOCK(xce.cache);
 	if (found) {
-		xce.cache->hits ++;
+		xc_cache_hit_dmz(xce.cache TSRMLS_CC);
 	}
 	else {
Index: /trunk/Decompiler.class.php
===================================================================
--- /trunk/Decompiler.class.php	(revision 393)
+++ /trunk/Decompiler.class.php	(revision 522)
@@ -29,6 +29,6 @@
 	if (is_array($src)) {
 		die_error('array str');
-		$src = new Decompiler_Array($src);
-		return $src->__toString($indent);
+		$src = new Decompiler_Array($src, false, $indent);
+		return $src->__toString();
 	}
 
@@ -165,7 +165,7 @@
 	}
 
-	function __toString($indent)
-	{
-		return $this->obj->__toString($indent);
+	function __toString()
+	{
+		return $this->obj->__toString();
 	}
 }
@@ -258,15 +258,17 @@
 {
 	var $needExport = false;
-
-	function Decompiler_Array($value = array(), $needexport = false)
+	var $indent = '';
+
+	function Decompiler_Array($value = array(), $needexport = false, $indent = '')
 	{
 		$this->value = $value;
 		$this->needExport = $needexport;
-	}
-
-	function __toString($indent)
+		$this->indent = $indent;
+	}
+
+	function __toString()
 	{
 		$exp = "array(";
-		$indent .= INDENT;
+		$indent = $this->indent . INDENT;
 		$assoclen = 0;
 		$multiline = 0;
@@ -335,5 +337,5 @@
 	var $iskey;
 
-	function __toString($indent)
+	function __toString()
 	{
 		return 'foreach (' . '';
Index: /trunk/xcache.h
===================================================================
--- /trunk/xcache.h	(revision 506)
+++ /trunk/xcache.h	(revision 522)
@@ -224,4 +224,11 @@
 	time_t     last_gc_deletes;
 	time_t     last_gc_expires;
+
+	time_t     hits_by_hour_cur_time;
+	zend_uint  hits_by_hour_cur_slot;
+	zend_ulong hits_by_hour[24];
+	time_t     hits_by_second_cur_time;
+	zend_uint  hits_by_second_cur_slot;
+	zend_ulong hits_by_second[5];
 } xc_cache_t;
 /* }}} */
