root/releases/elgg0.8rc2/lib/adodb/adodb-perf.inc.php

Revision 725, 30.5 kB (checked in by misja, 2 years ago)

Updated ADODB library.

  • Property svn:eol-style set to native
Line 
1 <?php
2 /*
3 V4.93 10 Oct 2006  (c) 2000-2006 John Lim (jlim#natsoft.com.my). All rights reserved.
4   Released under both BSD license and Lesser GPL library license.
5   Whenever there is any discrepancy between the two licenses,
6   the BSD license will take precedence. See License.txt.
7   Set tabs to 4 for best viewing.
8  
9   Latest version is available at http://adodb.sourceforge.net
10  
11   Library for basic performance monitoring and tuning.
12  
13   My apologies if you see code mixed with presentation. The presentation suits
14   my needs. If you want to separate code from presentation, be my guest. Patches
15   are welcome.
16  
17 */
18
19 if (!defined('ADODB_DIR')) include_once(dirname(__FILE__).'/adodb.inc.php');
20 include_once(ADODB_DIR.'/tohtml.inc.php');
21
22 define( 'ADODB_OPT_HIGH', 2);
23 define( 'ADODB_OPT_LOW', 1);
24
25 // returns in K the memory of current process, or 0 if not known
26 function adodb_getmem()
27 {
28     if (function_exists('memory_get_usage'))
29         return (integer) ((memory_get_usage()+512)/1024);
30     
31     $pid = getmypid();
32     
33     if ( strncmp(strtoupper(PHP_OS),'WIN',3)==0) {
34         $output = array();
35     
36         exec('tasklist /FI "PID eq ' . $pid. '" /FO LIST', $output);
37         return substr($output[5], strpos($output[5], ':') + 1);
38     }
39     
40     /* Hopefully UNIX */
41     exec("ps --pid $pid --no-headers -o%mem,size", $output);
42     if (sizeof($output) == 0) return 0;
43     
44     $memarr = explode(' ',$output[0]);
45     if (sizeof($memarr)>=2) return (integer) $memarr[1];
46     
47     return 0;
48 }
49
50 // avoids localization problems where , is used instead of .
51 function adodb_round($n,$prec)
52 {
53     return number_format($n, $prec, '.', '');
54 }
55
56 /* return microtime value as a float */
57 function adodb_microtime()
58 {
59     $t = microtime();
60     $t = explode(' ',$t);
61     return (float)$t[1]+ (float)$t[0];
62 }
63
64 /* sql code timing */
65 function& adodb_log_sql(&$connx,$sql,$inputarr)
66 {
67     $perf_table = adodb_perf::table();
68     $connx->fnExecute = false;
69     $t0 = microtime();
70     $rs =& $connx->Execute($sql,$inputarr);
71     $t1 = microtime();
72
73     if (!empty($connx->_logsql) && (empty($connx->_logsqlErrors) || !$rs)) {
74     global $ADODB_LOG_CONN;
75     
76         if (!empty($ADODB_LOG_CONN)) {
77             $conn = &$ADODB_LOG_CONN;
78             if ($conn->databaseType != $connx->databaseType)
79                 $prefix = '/*dbx='.$connx->databaseType .'*/ ';
80             else
81                 $prefix = '';
82         } else {
83             $conn =& $connx;
84             $prefix = '';
85         }
86         
87         $conn->_logsql = false; // disable logsql error simulation
88         $dbT = $conn->databaseType;
89         
90         $a0 = split(' ',$t0);
91         $a0 = (float)$a0[1]+(float)$a0[0];
92         
93         $a1 = split(' ',$t1);
94         $a1 = (float)$a1[1]+(float)$a1[0];
95         
96         $time = $a1 - $a0;
97     
98         if (!$rs) {
99             $errM = $connx->ErrorMsg();
100             $errN = $connx->ErrorNo();
101             $conn->lastInsID = 0;
102             $tracer = substr('ERROR: '.htmlspecialchars($errM),0,250);
103         } else {
104             $tracer = '';
105             $errM = '';
106             $errN = 0;
107             $dbg = $conn->debug;
108             $conn->debug = false;
109             if (!is_object($rs) || $rs->dataProvider == 'empty')
110                 $conn->_affected = $conn->affected_rows(true);
111             $conn->lastInsID = @$conn->Insert_ID();
112             $conn->debug = $dbg;
113         }
114         if (isset($_SERVER['HTTP_HOST'])) {
115             $tracer .= '<br>'.$_SERVER['HTTP_HOST'];
116             if (isset($_SERVER['PHP_SELF'])) $tracer .= $_SERVER['PHP_SELF'];
117         } else
118             if (isset($_SERVER['PHP_SELF'])) $tracer .= '<br>'.$_SERVER['PHP_SELF'];
119         //$tracer .= (string) adodb_backtrace(false);
120         
121         $tracer = (string) substr($tracer,0,500);
122         
123         if (is_array($inputarr)) {
124             if (is_array(reset($inputarr))) $params = 'Array sizeof='.sizeof($inputarr);
125             else {
126                 // Quote string parameters so we can see them in the
127                 // performance stats. This helps spot disabled indexes.
128                 $xar_params = $inputarr;
129                 foreach ($xar_params as $xar_param_key => $xar_param) {
130                     if (gettype($xar_param) == 'string')
131                     $xar_params[$xar_param_key] = '"' . $xar_param . '"';
132                 }
133                 $params = implode(', ', $xar_params);
134                 if (strlen($params) >= 3000) $params = substr($params, 0, 3000);
135             }
136         } else {
137             $params = '';
138         }
139         
140         if (is_array($sql)) $sql = $sql[0];
141         if ($prefix) $sql = $prefix.$sql;
142         $arr = array('b'=>strlen($sql).'.'.crc32($sql),
143                     'c'=>substr($sql,0,3900), 'd'=>$params,'e'=>$tracer,'f'=>adodb_round($time,6));
144         //var_dump($arr);
145         $saved = $conn->debug;
146         $conn->debug = 0;
147         
148         $d = $conn->sysTimeStamp;
149         if (empty($d)) $d = date("'Y-m-d H:i:s'");
150         if ($conn->dataProvider == 'oci8' && $dbT != 'oci8po') {
151             $isql = "insert into $perf_table values($d,:b,:c,:d,:e,:f)";
152         } else if ($dbT == 'odbc_mssql' || $dbT == 'informix' || strncmp($dbT,'odbtp',4)==0) {
153             $timer = $arr['f'];
154             if ($dbT == 'informix') $sql2 = substr($sql2,0,230);
155
156             $sql1 = $conn->qstr($arr['b']);
157             $sql2 = $conn->qstr($arr['c']);
158             $params = $conn->qstr($arr['d']);
159             $tracer = $conn->qstr($arr['e']);
160             
161             $isql = "insert into $perf_table (created,sql0,sql1,params,tracer,timer) values($d,$sql1,$sql2,$params,$tracer,$timer)";
162             if ($dbT == 'informix') $isql = str_replace(chr(10),' ',$isql);
163             $arr = false;
164         } else {
165             if ($dbT == 'db2') $arr['f'] = (float) $arr['f'];
166             $isql = "insert into $perf_table (created,sql0,sql1,params,tracer,timer) values( $d,?,?,?,?,?)";
167         }
168         $ok = $conn->Execute($isql,$arr);
169         $conn->debug = $saved;
170         
171         if ($ok) {
172             $conn->_logsql = true;
173         } else {
174             $err2 = $conn->ErrorMsg();
175             $conn->_logsql = true; // enable logsql error simulation
176             $perf =& NewPerfMonitor($conn);
177             if ($perf) {
178                 if ($perf->CreateLogTable()) $ok = $conn->Execute($isql,$arr);
179             } else {
180                 $ok = $conn->Execute("create table $perf_table (
181                 created varchar(50),
182                 sql0 varchar(250),
183                 sql1 varchar(4000),
184                 params varchar(3000),
185                 tracer varchar(500),
186                 timer decimal(16,6))");
187             }
188             if (!$ok) {
189                 ADOConnection::outp( "<p><b>LOGSQL Insert Failed</b>: $isql<br>$err2</p>");
190                 $conn->_logsql = false;
191             }
192         }
193         $connx->_errorMsg = $errM;
194         $connx->_errorCode = $errN;
195     }
196     $connx->fnExecute = 'adodb_log_sql';
197     return $rs;
198 }
199
200     
201 /*
202 The settings data structure is an associative array that database parameter per element.
203
204 Each database parameter element in the array is itself an array consisting of:
205
206 0: category code, used to group related db parameters
207 1: either
208     a. sql string to retrieve value, eg. "select value from v\$parameter where name='db_block_size'",
209     b. array holding sql string and field to look for, e.g. array('show variables','table_cache'),
210     c. a string prefixed by =, then a PHP method of the class is invoked,
211         e.g. to invoke $this->GetIndexValue(), set this array element to '=GetIndexValue',
212 2: description of the database parameter
213 */
214
215 class adodb_perf {
216     var $conn;
217     var $color = '#F0F0F0';
218     var $table = '<table border=1 bgcolor=white>';
219     var $titles = '<tr><td><b>Parameter</b></td><td><b>Value</b></td><td><b>Description</b></td></tr>';
220     var $warnRatio = 90;
221     var $tablesSQL = false;
222     var $cliFormat = "%32s => %s \r\n";
223     var $sql1 = 'sql1'// used for casting sql1 to text for mssql
224     var $explain = true;
225     var $helpurl = "<a href=http://phplens.com/adodb/reference.functions.fnexecute.and.fncacheexecute.properties.html#logsql>LogSQL help</a>";
226     var $createTableSQL = false;
227     var $maxLength = 2000;
228     
229     // Sets the tablename to be used           
230     function table($newtable = false)
231     {
232         static $_table;
233
234         if (!empty($newtable))  $_table = $newtable;
235         if (empty($_table)) $_table = 'adodb_logsql';
236         return $_table;
237     }
238
239     // returns array with info to calculate CPU Load
240     function _CPULoad()
241     {
242 /*
243
244 cpu  524152 2662 2515228 336057010
245 cpu0 264339 1408 1257951 168025827
246 cpu1 259813 1254 1257277 168031181
247 page 622307 25475680
248 swap 24 1891
249 intr 890153570 868093576 6 0 4 4 0 6 1 2 0 0 0 124 0 8098760 2 13961053 0 0 0 0 0 0 0 0 0 0 0 0 0 16 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
250 disk_io: (3,0):(3144904,54369,610378,3090535,50936192) (3,1):(3630212,54097,633016,3576115,50951320)
251 ctxt 66155838
252 btime 1062315585
253 processes 69293
254
255 */
256         // Algorithm is taken from
257         // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/example__obtaining_raw_performance_data.asp
258         if (strncmp(PHP_OS,'WIN',3)==0) {
259             if (PHP_VERSION == '5.0.0') return false;
260             if (PHP_VERSION == '5.0.1') return false;
261             if (PHP_VERSION == '5.0.2') return false;
262             if (PHP_VERSION == '5.0.3') return false;
263             if (PHP_VERSION == '4.3.10') return false; # see http://bugs.php.net/bug.php?id=31737
264             
265             @$c = new COM("WinMgmts:{impersonationLevel=impersonate}!Win32_PerfRawData_PerfOS_Processor.Name='_Total'");
266             if (!$c) return false;
267             
268             $info[0] = $c->PercentProcessorTime;
269             $info[1] = 0;
270             $info[2] = 0;
271             $info[3] = $c->TimeStamp_Sys100NS;
272             //print_r($info);
273             return $info;
274         }
275         
276         // Algorithm - Steve Blinch (BlitzAffe Online, http://www.blitzaffe.com)
277         $statfile = '/proc/stat';
278         if (!file_exists($statfile)) return false;
279         
280         $fd = fopen($statfile,"r");
281         if (!$fd) return false;
282         
283         $statinfo = explode("\n",fgets($fd, 1024));
284         fclose($fd);
285         foreach($statinfo as $line) {
286             $info = explode(" ",$line);
287             if($info[0]=="cpu") {
288                 array_shift($info);  // pop off "cpu"
289                 if(!$info[0]) array_shift($info); // pop off blank space (if any)
290                 return $info;
291             }
292         }
293         
294         return false;
295         
296     }
297     
298     /* NOT IMPLEMENTED */
299     function MemInfo()
300     {
301         /*
302
303         total:    used:    free:  shared: buffers:  cached:
304 Mem:  1055289344 917299200 137990144        0 165437440 599773184
305 Swap: 2146775040 11055104 2135719936
306 MemTotal:      1030556 kB
307 MemFree:        134756 kB
308 MemShared:           0 kB
309 Buffers:        161560 kB
310 Cached:         581384 kB
311 SwapCached:       4332 kB
312 Active:         494468 kB
313 Inact_dirty:    322856 kB
314 Inact_clean:     24256 kB
315 Inact_target:   168316 kB
316 HighTotal:      131064 kB
317 HighFree:         1024 kB
318 LowTotal:       899492 kB
319 LowFree:        133732 kB
320 SwapTotal:     2096460 kB
321 SwapFree:      2085664 kB
322 Committed_AS:   348732 kB
323         */
324     }
325     
326     
327     /*
328         Remember that this is client load, not db server load!
329     */
330     var $_lastLoad;
331     function CPULoad()
332     {
333         $info = $this->_CPULoad();
334         if (!$info) return false;
335             
336         if (empty($this->_lastLoad)) {
337             sleep(1);
338             $this->_lastLoad = $info;
339             $info = $this->_CPULoad();
340         }
341         
342         $last = $this->_lastLoad;
343         $this->_lastLoad = $info;
344         
345         $d_user = $info[0] - $last[0];
346         $d_nice = $info[1] - $last[1];
347         $d_system = $info[2] - $last[2];
348         $d_idle = $info[3] - $last[3];
349         
350         //printf("Delta - User: %f  Nice: %f  System: %f  Idle: %f<br>",$d_user,$d_nice,$d_system,$d_idle);
351
352         if (strncmp(PHP_OS,'WIN',3)==0) {
353             if ($d_idle < 1) $d_idle = 1;
354             return 100*(1-$d_user/$d_idle);
355         }else {
356             $total=$d_user+$d_nice+$d_system+$d_idle;
357             if ($total<1) $total=1;
358             return 100*($d_user+$d_nice+$d_system)/$total;
359         }
360     }
361     
362     function Tracer($sql)
363     {
364         $perf_table = adodb_perf::table();
365         $saveE = $this->conn->fnExecute;
366         $this->conn->fnExecute = false;
367         
368         global $ADODB_FETCH_MODE;
369         $save = $ADODB_FETCH_MODE;
370         $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
371         if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
372                 
373         $sqlq = $this->conn->qstr($sql);
374         $arr = $this->conn->GetArray(
375 "select count(*),tracer
376     from $perf_table where sql1=$sqlq
377     group by tracer
378     order by 1 desc");
379         $s = '';
380         if ($arr) {
381             $s .= '<h3>Scripts Affected</h3>';
382             foreach($arr as $k) {
383                 $s .= sprintf("%4d",$k[0]).' &nbsp; '.strip_tags($k[1]).'<br>';
384             }
385         }
386         
387         if (isset($savem)) $this->conn->SetFetchMode($savem);
388         $ADODB_CACHE_MODE = $save;
389         $this->conn->fnExecute = $saveE;
390         return $s;
391     }
392
393     /*
394         Explain Plan for $sql.
395         If only a snippet of the $sql is passed in, then $partial will hold the crc32 of the
396             actual sql.
397     */
398     function Explain($sql,$partial=false)
399     {   
400         return false;
401     }
402     
403     function InvalidSQL($numsql = 10)
404     {
405     
406         if (isset($_GET['sql'])) return;
407         $s = '<h3>Invalid SQL</h3>';
408         $saveE = $this->conn->fnExecute;
409         $this->conn->fnExecute = false;
410         $perf_table = adodb_perf::table();
411         $rs =& $this->conn->SelectLimit("select distinct count(*),sql1,tracer as error_msg from $perf_table where tracer like 'ERROR:%' group by sql1,tracer order by 1 desc",$numsql);//,$numsql);
412         $this->conn->fnExecute = $saveE;
413         if ($rs) {
414             $s .= rs2html($rs,false,false,false,false);
415         } else
416             return "<p>$this->helpurl. ".$this->conn->ErrorMsg()."</p>";
417         
418         return $s;
419     }
420
421     
422     /*
423         This script identifies the longest running SQL
424     */   
425     function _SuspiciousSQL($numsql = 10)
426     {
427         global $ADODB_FETCH_MODE;
428         
429             $perf_table = adodb_perf::table();
430             $saveE = $this->conn->fnExecute;
431             $this->conn->fnExecute = false;
432             
433             if (isset($_GET['exps']) && isset($_GET['sql'])) {
434                 $partial = !empty($_GET['part']);
435                 echo "<a name=explain></a>".$this->Explain($_GET['sql'],$partial)."\n";
436             }
437             
438             if (isset($_GET['sql'])) return;
439             $sql1 = $this->sql1;
440             
441             $save = $ADODB_FETCH_MODE;
442             $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
443             if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
444             //$this->conn->debug=1;
445             $rs =& $this->conn->SelectLimit(
446             "select avg(timer) as avg_timer,$sql1,count(*),max(timer) as max_timer,min(timer) as min_timer
447                 from $perf_table
448                 where {$this->conn->upperCase}({$this->conn->substr}(sql0,1,5)) not in ('DROP ','INSER','COMMI','CREAT')
449                 and (tracer is null or tracer not like 'ERROR:%')
450                 group by sql1
451                 order by 1 desc",$numsql);
452             if (isset($savem)) $this->conn->SetFetchMode($savem);
453             $ADODB_FETCH_MODE = $save;
454             $this->conn->fnExecute = $saveE;
455             
456             if (!$rs) return "<p>$this->helpurl. ".$this->conn->ErrorMsg()."</p>";
457             $s = "<h3>Suspicious SQL</h3>
458 <font size=1>The following SQL have high average execution times</font><br>
459 <table border=1 bgcolor=white><tr><td><b>Avg Time</b><td><b>Count</b><td><b>SQL</b><td><b>Max</b><td><b>Min</b></tr>\n";
460             $max = $this->maxLength;
461             while (!$rs->EOF) {
462                 $sql = $rs->fields[1];
463                 $raw = urlencode($sql);
464                 if (strlen($raw)>$max-100) {
465                     $sql2 = substr($sql,0,$max-500);
466                     $raw = urlencode($sql2).'&part='.crc32($sql);
467                 }
468                 $prefix = "<a target=sql".rand