Logo Search packages:      
Sourcecode: mysql-connector-c++ version File versions

cpp_trace_analyzer.php

<?php
/*
   Copyright 2008-2009 Sun Microsystems, Inc.  All rights reserved.

   The MySQL Connector/C++ is licensed under the terms of the GPL
   <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
   MySQL Connectors. There are special exceptions to the terms and
   conditions of the GPL as it is applied to this software, see the
   FLOSS License Exception
   <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
*/

/**
* Script to filter Connector/C++ (and Connector/OpenOffice.org) debug traces
*
* NOTE: NO SUPPORT FOR THIS SCRIPT
* NOTE: FIX BUGS YOURSELF!
*
* MySQL Connector/C++ offers several debug traces, The internal debug trace is
* a call trace. It shows each and every function call and sometimes
* the function arguments and other related information.
*
* Example:
* |  INF: Tracing enabled
* <MySQL_Connection::setClientOption
* >MySQL_Prepared_Statement::setInt
* |  INF: this=0x69a2e0
* |  >MySQL_Prepared_Statement::checkClosed
* |  <MySQL_Prepared_Statement::checkClosed
* |  <MySQL_Prepared_Statement::setInt
*
*
*
* Obviously this trace  can easily get very, very long. It can show significantly
* more detail than you might want to find in the trace. Therefore you will find
* yourself often "grep'ing" through the trace to identify relevant information.
* This script aims to help you with the grep. I'm not a Unix-Shell expert,
* I've grown up with PHP - so its in PHP.
*
* Syntax:
*   php script.php
*     [-l trace_nesting_level]
*     [-s show_function]
*     [-r remove_function]
*     [-b read_file_backwards]
*     [-m max_number_of_lines_to_display]
*     [-stats]
*      trace_input_file
*
*
* The script shows three numbers at the beginning of each line of its output:
*
*  <input_lineno>/<nesting_level>/<output_lineno>
*
* <input_lineno>  - number of the line in the input file which causes the output
* <nesting_level> - see -t trace_nesting_level
* <output_lineno> - line number the line in the the generated output
*
*
* -t trace_nesting_level
*
* Show only information up to a function call depth of -t <n>.
* Nesting starts at level 1. If you apply -t 1 to the above example trace,
* the calls to MySQL_Prepared_Statement::checkClosed and
* MySQL_Prepared_Statement::setInt should not be shown, because they are
* on nesting level 2.
*
*
* -s show_function
*
* Show only classes and methods that apply to the pattern -s <pattern>.
* To show only method calls from the class X use -s X:: . To show
* only calls to method Y from class X, use -s X::Y. To show only
* all methods with the name Y from any class, use -s Y.
*
* Example based on the above trace:
*
* -s MySQL_Connection::
*    show only calls from the MySQL Connection class
*
* -s execute
*    show only calls of the method "execute" from any class
*
* -s MySQL_Prepared_Statement::setInt
*    show calls of method setInt from class MySQL_Prepared_statement
*
*
* -r remove_function
*
* Hide classes and methods that apply to pattern -r <pattern>.
* See -s show_function for a description of <pattern>.
*
*
* -b read_file_backwards
*
* Not functional.
*
*
* - m max_number_of_lines_to_display
*
* Limit output to <n> lines.
*
*
* -c
*
* Compress opening and closing function name into one line. Normally the trace
* will show two lines for every function call. The first line gets printed when
* entering a function. The second line gets printed when exiting. -c does
* "collapse" or "compress" to subsequent lines that show entering and leaving
* a function into one line. For example:
*
* > class::func()
* < class::func()
*
* Would be compressed into one line:
*
* = class::func()
*
* Compression happens only if no further lines are between entering and leaving
* a function.
*
*
* -v
*
* Print debug output.
*
*
* - stats
*
* Print call statistics. This can be useful to decide which
* classes and/or methods to exclude from the output using -r or -s. Statistics
* cover all calls - regardless if hidden from the output or not!
*
*
* @see http://dev.mysql.com/doc/refman/6.0/en/connector-cpp-debug-tracing.html
*/


$analyzer = new cpp_trace_analyzer();
if (!$analyzer->checkArgs($argc, $argv) ||
            !$analyzer->parseOptions($argc, $argv))
      die("Syntax error\n\n");

$analyzer->printLog();


class cpp_trace_analyzer {

      protected $tracefile = null;
      protected $level = 0;
      protected $show_functions = array();
      protected $exclude_functions = array();
      protected $verbose = false;
      protected $max_lines = 0;
      protected $read_from_tail = false;
      protected $collapse = false;

      protected $stats = array();
      protected $collect_stats = false;

      protected $fetch_buffer = array();
      protected $fetch_unfetched = 0;

      public function __construct() {
      }

      public function checkArgs($argc, $argv) {
            if ($argc < 2) {
                  $this->printUsage();
                  return false;
            }

            return true;
      }

      public function parseOptions($argc, $argv) {


            $this->tracefile = $argv[$argc - 1];
            if (!file_exists($this->tracefile) || !is_readable($this->tracefile)) {
                  $this->printUsage("Trace file missing");
                  return false;
            }
            unset($argv[$argc - 1]);
            // skip file name
            unset($argv[0]);
            while ($token = array_shift($argv)) {

                  switch ($token) {
                        case '-l':
                              $level = array_shift($argv);
                              if (is_null($level)) {
                                    $this->printUsage("-l passed without level");
                                    return false;
                              }
                              if ($level < 0) {
                                    $this->printUsage("-l passed with nevative level");
                                    return false;
                              }
                              $this->level = (int)$level;
                              break;

                        case '-s':
                              $function = array_shift($argv);
                              if (is_null($function)) {
                                    $this->printUsage("-s used without function name");
                                    return false;
                              }
                              $this->show_functions[$function] = $function;
                              break;

                        case '-r':
                              $function = array_shift($argv);
                              if (is_null($function)) {
                                    $this->printUsage("-r used without function name");
                                    return false;
                              }
                              $this->exclude_functions[$function] = $function;
                              break;

                        case '-m':
                              $max = array_shift($argv);
                              if (is_null($max)) {
                                    $this->printUsage("-m max lines to display used without limit");
                                    return false;
                              }
                              if ($max < 0) {
                                    $this->printUsage("-m passed with negative limit");
                                    return false;
                              }
                              $this->max_lines = $max;
                              break;

                        case '-c':
                              $this->collapse = true;
                              break;

                        case '-b':
                              $this->printUsage("-b is not supported yet");
                              return false;
                              $this->read_from_tail = true;
                              break;

                        case '-stats':
                              $this->collect_stats = true;
                              break;

                        case '-v';
                              $this->verbose = true;
                              break;
                  }
            }

            return true;
      }

      public function printLog() {

                  $fp = fopen($this->tracefile, 'r');
                  if (!$fp) {
                        $this->printUsage("Cannot open tracefile for reading");
                        return false;
                  }

                  $lineno = 0;
                  $displayed = 0;
                  $show_level = null;

                  while ($line = $this->fetchLine($fp)) {
                        $lineno++;
                        $function = trim($line);
                        $level = 1;
                        $exit = false;
                        do {
                              $left = substr(trim($function), 0, 1);
                              switch ($left) {
                                    case '|':
                                          // indentation
                                          $level++;
                                          break;
                                    case '<':
                                          // function exit
                                          $function = substr(trim($function), 1);
                                          $exit = true;
                                          break 2;

                                    case '>':
                                          // function enter
                                          $function = substr(trim($function), 1);
                                          if ($this->collapse) {
                                                // look ahead: is the next line the closing function call?
                                                $next = $this->fetchLine($fp);
                                                if (strstr($next, '<' . $function)) {
                                                            // Yes, it is..- lets collapse into one line
                                                            if ($this->verbose)
                                                                  printf("%07d - Collapsing\n%s/%s", $lineno, $line, $next);
                                                            $line = str_replace('>' . $function, '=' . $function, $line);
                                                            $lineno++;
                                                } else {
                                                      if ($this->verbose)
                                                                  printf("%07d - No collapse\n%s/%s\n", $lineno, $line, $next);
                                                      $this->unfetchLine();
                                                }
                                          }
                                          $level++;
                                          break 2;

                                    default:
                                          // function name or similar
                                          break 2;
                              }
                              $function = substr($function, 1);
                        } while ($function != '');

                        if ('' == $function) {
                              if ($this->verbose)
                                    printf("%07d - Skip unknown '%s'\n", $lineno, $line);
                              continue;
                        }

                        if (strstr($function, '::')) {
                              $pclass = '';
                              $pmethod = $function;
                              $len = strlen($function);
                              for ($i = 0; $i < $len; $i++) {
                                    $char = $function{$i};

                                    if (':' == $char && ($i < $len -1) && ':' == $function{$i + 1}) {
                                          break;
                                    }
                                    $pclass .= $char;
                              }
                              if ($pclass != '')
                                    $pmethod = substr($function, $i + 2);
                        }

                        if ($this->collect_stats && !$exit && $pmethod != '') {
                              if (!isset($this->stats[$pclass][$pmethod]))
                                    $this->stats[$pclass][$pmethod] = 1;
                              else
                                    $this->stats[$pclass][$pmethod]++;
                        }
                        if ($this->level > 0 && ($level > $this->level)) {
                              if ($this->verbose)
                                          printf("%07d - Skip - level %d > %d\n", $lineno, $level, $this->level);
                              continue;
                        }

                        if ($pclass != '' && $pclass != $class)
                              $class = $pclass;

                        if ($pmethod != '' && $pmethod != $method)
                              $method = $pmethod;

                        if (isset($this->exclude_functions[$class . '::'])) {
                              if ($this->verbose)
                                    printf("%07d - Skip - class %s because of -r %s::\n", $lineno, $class, $class);
                              continue;
                        }

                        if (isset($this->exclude_functions[$method])) {
                              if ($this->verbose)
                                    printf("%07d - Skip - method %s because of -r %s\n", $lineno, $method, $method);
                              continue;
                        }

                        if (isset($this->exclude_functions[$class . '::' . $method])) {
                              if ($this->verbose)
                                    printf("%07d - Skip - method %s::%s because of -r %s::%s\n", $lineno, $class, $method, $class, $method);
                              continue;
                        }

                        if (!empty($this->show_functions)) {
                              if (!isset($this->show_functions[$class . '::']) &&
                                          !isset($this->show_functions[$method]) &&
                                          !isset($this->show_functions[$class . '::' . $method]))
                                          {
                                    if ((!is_null($show_level) && $level < $show_level) || is_null($show_level)) {
                                          if ($this->verbose)
                                                printf("%07d/%03d - Skip - class %s or method %s not in positive show list, no -s %s:: and no -s %s\n", $lineno, $level, $class, $method, $class, $method);
                                          $show_level = null;
                                          continue;
                                    } else if ($show_level === $level) {
                                          // last one on the initial opening level?
                                          if (!$exit) {
                                                // something else, could be a new function, eat up - KLUDGE no proper way to detect if it should be skipped
                                                if ($this->verbose)
                                                      printf("%07d/%03d - Skip - class %s or method %s not in positive show list, no -s %s:: and no -s %s\n", $lineno, $level, $class, $method, $class, $method);
                                                $show_level = null;
                                                continue;
                                          } else {
                                                // <function - exiting function
                                                $show_level = null;
                                          }
                                    }
                              } else {
                                    if (is_null($show_level)) {
                                          $show_level = $level;
                                          if ($this->verbose)
                                                printf("%07d - Info - accepting level > %d\n", $lineno, $level);
                                    }
                              }
                        }

                        $displayed++;
                        printf("%07d/%02d/%07d\t%s", $lineno, $level, $displayed, $line);
                        if (($this->max_lines > 0) && ($displayed == $this->max_lines)) {
                              if ($this->verbose)
                                          printf("%07d - Skip - showed %d lines, limit of -m %d reached\n", $lineno, $displayed, $this->max_lines);
                              break;
                        }
                  }

                  fclose($fp);

                  if ($this->collect_stats)
                        $this->printStats();

                  return true;
      }


      public function printStats() {

            printf("\n");
            $total_calls = $fac = 0;
            foreach ($this->stats as $class => $methods) {
                  foreach ($methods as $method => $calls)
                        $total_calls += $calls;
            }
            $fac = 100 / $total_calls;

            $total_hidden = 0;
            $auto_hide = '';

            ksort($this->stats);
            foreach ($this->stats as $class => $methods) {

                  if (!empty($this->show_functions)) {
                        if (isset($this->show_functions[$class . '::']))
                              $comment = sprintf("(shown because of -s %s::)", $class);
                        else
                              $comment = "(hidden)";
                  } else if (!empty($this->exclude_functions)) {
                        if (isset($this->exclude_functions[$class . '::']))
                              $comment = sprintf("(hidden because of -r %s::)", $class);
                        else
                              $comment = '(shown)';
                  }

                  if ($class)
                        printf("Class: %s %s\n\n", $class, $comment);
                  else
                        printf("No class\n\n");

                  arsort($methods, SORT_NUMERIC);

                  foreach ($methods as $method => $calls) {
                        $hidden = false;
                        $comment = '';
                        if (!empty($this->show_functions)) {
                              if (isset($this->show_functions[$method])) {
                                    $comment = sprintf("(shown because of -s %s)", $method);
                              } else if (isset($this->show_functions[$class . '::' . $method])) {
                                    $comment = sprintf("(shown because of -s %s::%s)", $class, $method);
                              } else if (isset($this->show_functions[$class . '::'])) {
                                    $comment = sprintf("(shown because of -s %s::)", $class);
                              } else {
                                    $total_hidden += $calls;
                                    $hidden = true;
                                    $comment = "(hidden)";
                              }
                        } else if (!empty($this->exclude_functions)) {
                              if (isset($this->exclude_functions[$method])) {
                                    $total_hidden += $calls;
                                    $comment = sprintf("(hidden because of -r %s)", $method);
                              } else if (isset($this->exclude_functions[$class . '::' . $method])) {
                                    $total_hidden += $calls;
                                    $hidden = true;
                                    $comment = sprintf("(hidden because of -r %s::%s)", $class, $method);
                              } else if (isset($this->exclude_functions[$class . '::'])) {
                                    $total_hidden += $calls;
                                    $hidden = true;
                                    $comment = sprintf("(hidden because of -r %s::)", $class);
                              }
                        }

                        if ($comment == '') {
                              if ($hidden) {
                                    $comment = '(hidden)';
                              } else if ($calls * $fac >= 2) {
                                    $comment = sprintf('(shown, use "-r %s::%s" to hide)', $class, $method);
                                    $auto_hide .= sprintf("-r %s::%s ", $class, $method);
                              } else {
                                    $comment = "(shown)";
                              }
                        }

                        printf("  %-40s %-7d (= %5s%%) - %s\n", $method, $calls,
                              sprintf("%2.2f", $calls * $fac), $comment);

                  }
                  printf("\n");
            }

            printf("NOTE: %2.2f%% of all calls hidden.\n", $total_hidden * $fac);
            printf("\n");
            printf("If you want to hide all functions which are invoked in more than 2%% of all cases, use:\n");
            printf("%s\n", $auto_hide);
            printf("\n");
      }

      protected function fetchLine($fp, $bytes = 16384) {
            if ($this->unfetched > 0) {
                  $line = $this->fetch_buffer[count($this->fetch_buffer) - $this->unfetched];
                  $this->unfetched--;
                  return $line;
            }

            if (!$this->read_from_tail)
                  $line =  fgets($fp, $bytes);

            $this->fetch_buffer[] = $line;
            if (count($this->fetch_buffer) > 10) {
                  // remove oldest
                  array_shift($this->fetch_buffer);
            }

            return $line;
      }

      protected function unfetchLine() {
            if ($this->unfetched == count($this->fetch_buffer))
                  return false;

            $this->unfetched++;
            return $this->fetch_buffer[count($this->fetch_buffer) - $this->unfetched - 1];
      }

      protected function printUsage($msg = NULL) {
            print "\n";
            print "Usage:\n";
            print " script.php [-l trace_nesting_level] [-s show_function] [-r remove_function] [-b read_file_backwards] [-m max_number_of_lines_to_display] [-stats] trace\n";
            print "\n";
            if (!is_null($msg))
                  printf(" %s\n\n", $msg);
      }

}
?>

Generated by  Doxygen 1.6.0   Back to index