Usage.php 4.75 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
<?php

/*
	Question2Answer (c) Gideon Greenspan
	http://www.question2answer.org/
	License: GPLv2+ <http://www.question2answer.org/license.php>

	File: qa-include/qa-util-debug.php
	Description: Debugging stuff, currently used for tracking resource usage
*/

class Q2A_Util_Usage
{
14 15 16 17 18
	private $stages;
	private $startUsage;
	private $prevUsage;
	private $databaseUsage;
	private $databaseQueryLog;
19 20

	/**
21
	 * Initialize the counts of resource usage.
22 23 24
	 */
	public function __construct()
	{
25 26 27
		$this->stages = array();
		$this->databaseUsage = array('queries'=>0, 'clock'=>0);
		$this->databaseQueryLog = '';
28

29
		$this->prevUsage = $this->startUsage = $this->getCurrent();
30 31 32
	}

	/**
33
	 * Return an array representing the resource usage as of now.
34 35 36 37 38
	 */
	public function getCurrent()
	{
		$usage = array(
			'files' => count(get_included_files()),
39
			'queries' => $this->databaseUsage['queries'],
40 41
			'ram' => function_exists('memory_get_usage') ? memory_get_usage() : 0,
			'clock' => array_sum(explode(' ', microtime())),
42
			'mysql' => $this->databaseUsage['clock'],
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
		);

		if (function_exists('getrusage')) {
			$rusage = getrusage();
			$usage['cpu'] = $rusage["ru_utime.tv_sec"] + $rusage["ru_stime.tv_sec"]
				+ ($rusage["ru_utime.tv_usec"] + $rusage["ru_stime.tv_usec"]) / 1000000;
		}
		else
			$usage['cpu'] = 0;

		$usage['other'] = $usage['clock'] - $usage['cpu'] - $usage['mysql'];

		return $usage;
	}

	/**
59
	 * Mark the beginning of a new stage of script execution and store usages accordingly.
60 61 62 63
	 */
	public function mark($stage)
	{
		$usage = $this->getCurrent();
64 65 66 67 68
		$this->stages[$stage] = $this->delta($this->prevUsage, $usage);
		$this->prevUsage = $usage;
	}

	/**
69
	 * Logs query and updates database usage stats.
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
	 */
	public function logDatabaseQuery($query, $usedtime, $gotrows, $gotcolumns)
	{
		$this->databaseUsage['clock'] += $usedtime;

		if (strlen($this->databaseQueryLog) < 1048576) { // don't keep track of big tests
			$rowcolstring = '';
			if (is_numeric($gotrows))
				$rowcolstring .= ' - ' . $gotrows . ($gotrows == 1 ? ' row' : ' rows');
			if (is_numeric($gotcolumns))
				$rowcolstring .= ' - ' . $gotcolumns . ($gotcolumns == 1 ? ' column' : ' columns');

			$this->databaseQueryLog .= $query . "\n\n" . sprintf('%.2f ms', $usedtime*1000) . $rowcolstring . "\n\n";
		}

		$this->databaseUsage['queries']++;
86 87 88
	}

	/**
89
	 * Output an (ugly) block of HTML detailing all resource usage and database queries.
90 91 92
	 */
	public function output()
	{
93
		$totaldelta = $this->delta($this->startUsage, $this->getCurrent());
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
?>
		<style>
		.debug-table { border-collapse: collapse; box-sizing: border-box; width: 100%; margin: 20px auto; }
		.debug-table tr { background-color: #ccc; }
		.debug-table td { padding: 10px; }

		td.debug-cell-files { width: 30%; padding-right: 5px; }
		td.debug-cell-queries { width: 70%; padding-left: 5px; }

		textarea.debug-output { box-sizing: border-box; width: 100%; font: 12px monospace; color: #000; }
		</style>

		<table class="debug-table">
		<tbody>
			<tr>
				<td colspan="2"><?php
					echo $this->line('Total', $totaldelta, $totaldelta) . "<br>\n";
111
					foreach ($this->stages as $stage => $stagedelta)
112 113 114 115 116 117 118 119 120 121 122
						echo '<br>' . $this->line(ucfirst($stage), $stagedelta, $totaldelta) . "\n";
				?></td>
			</tr>
			<tr>
				<td class="debug-cell-files">
					<textarea class="debug-output" cols="40" rows="20"><?php
						foreach (get_included_files() as $file)
							echo qa_html(implode('/', array_slice(explode('/', $file), -3)))."\n";
					?></textarea>
				</td>
				<td class="debug-cell-queries">
123
					<textarea class="debug-output" cols="40" rows="20"><?=qa_html($this->databaseQueryLog)?></textarea>
124 125 126 127 128 129 130 131 132
				</td>
			</tr>
		</tbody>
		</table>
<?php
	}


	/**
133
	 * Return the difference between two resource usage arrays, as an array.
134 135 136 137 138 139 140 141 142 143 144 145
	 */
	private function delta($oldusage, $newusage)
	{
		$delta = array();

		foreach ($newusage as $key => $value)
			$delta[$key] = max(0, $value-@$oldusage[$key]);

		return $delta;
	}

	/**
146
	 * Return HTML to represent the resource $usage, showing appropriate proportions of $totalusage.
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
	 */
	private function line($stage, $usage, $totalusage)
	{
		return sprintf(
			"%s &ndash; <b>%.1fms</b> (%d%%) &ndash; PHP %.1fms (%d%%), MySQL %.1fms (%d%%), Other %.1fms (%d%%) &ndash; %d PHP %s, %d DB %s, %dk RAM (%d%%)",
			$stage,
			$usage['clock'] * 1000,
			$usage['clock'] * 100 / $totalusage['clock'],
			$usage['cpu'] * 1000,
			$usage['cpu'] * 100 / $totalusage['clock'],
			$usage['mysql'] * 1000,
			$usage['mysql'] * 100 / $totalusage['clock'],
			$usage['other'] * 1000,
			$usage['other'] * 100 / $totalusage['clock'],
			$usage['files'],
			$usage['files'] == 1 ? 'file' : 'files',
			$usage['queries'],
			$usage['queries'] == 1 ? 'query' : 'queries',
			$usage['ram'] / 1024,
			$usage['ram'] ? ($usage['ram'] * 100 / $totalusage['ram']) : 0
		);
	}

}