qtools_profiler-8.x-1.x-dev/src/XHProfLib/Parser/BaseParser.php
src/XHProfLib/Parser/BaseParser.php
<?php
namespace Drupal\qtools_profiler\XHProfLib\Parser;
use Drupal\qtools_profiler\XHProfLib\Report\ReportConstants;
use Drupal\qtools_profiler\XHProfLib\Run;
use Drupal\qtools_profiler\XHProfLib\Symbol\Symbol;
/**
* Class BaseReport
*/
abstract class BaseParser implements ParserInterface {
protected $stats;
protected $pc_stats;
protected $metrics;
protected $diff_mode;
protected $sort_col;
protected $display_calls;
protected $sort;
protected $symbol;
private $overall_totals = array(
"ct" => 0,
"wt" => 0,
"ut" => 0,
"st" => 0,
"cpu" => 0,
"mu" => 0,
"pmu" => 0,
"samples" => 0
);
/**
* @var \Drupal\qtools_profiler\XHProfLib\Symbol\Symbol
*/
protected $mainSymbol;
/**
* @var \Drupal\qtools_profiler\XHProfLib\Run
*/
protected $run;
/**
* @param $run
* @param $sort
* @param $symbol
*/
public function __construct(Run $run, $sort, $symbol) {
$this->sort = $sort;
$this->run = $run;
$this->symbol = $symbol;
$this->diff_mode = FALSE;
$this->mainSymbol = $run->getMainSymbol();
$this->initMetrics($run->getSymbols(), NULL, $sort);
}
/**
* @param $symbols
* All profiled symbols.
* @param $symbol
* Set this to show the parent-child report.
* @param $sort
* Metric used to sort.
*/
protected function initMetrics($symbols, $symbol, $sort) {
if (!empty($sort)) {
if (array_key_exists($sort, ReportConstants::getSortableColumns())) {
$this->sort_col = $sort;
}
else {
print("Invalid Sort Key $sort specified in URL");
}
}
// For C++ profiler runs, walltime attribute isn't present.
// In that case, use "samples" as the default sort column.
$wt = $this->mainSymbol->getWt();
if (!isset($wt)) {
if ($this->sort_col == "wt") {
$this->sort_col = "samples";
}
// C++ profiler data doesn't have call counts.
// ideally we should check to see if "ct" metric
// is present for "main()". But currently "ct"
// metric is artificially set to 1. So, relying
// on absence of "wt" metric instead.
$this->display_calls = FALSE;
}
else {
$this->display_calls = TRUE;
}
// parent/child report doesn't support exclusive times yet.
// So, change sort hyperlinks to closest fit.
if (!empty($symbol)) {
$this->sort_col = str_replace("excl_", "", $this->sort_col);
}
if ($this->display_calls) {
$this->stats = array("fn", "ct", "Calls%");
}
else {
$this->stats = array("fn");
}
$this->pc_stats = $this->stats;
$possible_metrics = $this->getPossibleMetrics($symbols);
foreach ($possible_metrics as $metric => $desc) {
$mainMetric = $this->mainSymbol->getMetric($metric);
if (isset($mainMetric)) {
$metrics[] = $metric;
// flat (top-level reports): we can compute
// exclusive metrics reports as well.
$this->stats[] = $metric;
$this->stats[] = "I" . $desc[0] . "%";
$this->stats[] = "excl_" . $metric;
$this->stats[] = "E" . $desc[0] . "%";
// parent/child report for a function: we can
// only breakdown inclusive times correctly.
$this->pc_stats[] = $metric;
$this->pc_stats[] = "I" . $desc[0] . "%";
}
}
}
/**
* @return array
*/
public function getPossibleMetrics() {
return array(
"wt" => array("Wall", "microsecs", "walltime"),
"ut" => array("User", "microsecs", "user cpu time"),
"st" => array("Sys", "microsecs", "system cpu time"),
"cpu" => array("Cpu", "microsecs", "cpu time"),
"mu" => array("MUse", "bytes", "memory usage"),
"pmu" => array("PMUse", "bytes", "peak memory usage"),
"samples" => array("Samples", "samples", "cpu time")
);
}
/**
* @return array
*/
public function getMetrics() {
// get list of valid metrics
$possible_metrics = $this->getPossibleMetrics();
// return those that are present in the raw data.
// We'll just look at the root of the subtree for this.
$metrics = array();
foreach ($possible_metrics as $metric => $desc) {
$mainMetric = $this->mainSymbol->getMetric($metric);
if (isset($mainMetric)) {
$metrics[] = $metric;
}
}
return $metrics;
}
/**
* @param Symbol[] $symbols
*
* @return array
*/
protected function computeFlatInfo($symbols) {
$metrics = $this->getMetrics();
// Compute inclusive times for each function.
$symbol_tab = $this->computeInclusiveTimes($symbols);
// Total metric value is the metric value for "main()".
foreach ($metrics as $metric) {
$this->overall_totals[$metric] = $this->mainSymbol->getMetric($metric);
}
// Initialize exclusive (self) metric value to inclusive metric value to start with.
// In the same pass, also add up the total number of function calls.
foreach ($symbol_tab as $symbol => $info) {
foreach ($metrics as $metric) {
$symbol_tab[$symbol]["excl_" . $metric] = $symbol_tab[$symbol][$metric];
}
// Keep track of total number of calls.
$this->overall_totals["ct"] += $info["ct"];
}
// Adjust exclusive times by deducting inclusive time of children.
foreach ($symbols as $symbol) {
$parent = $symbol->getParent();
if ($parent) {
foreach ($metrics as $metric) {
// make sure the parent exists hasn't been pruned.
if (isset($symbol_tab[$parent])) {
$symbol_tab[$parent]["excl_" . $metric] -= $symbol->getMetric($metric);
}
}
}
}
return $symbol_tab;
}
/**
* @param Symbol[] $symbols
*
* @return array
*/
protected function computeInclusiveTimes($symbols) {
$metrics = $this->getMetrics();
$symbol_tab = array();
/*
* First compute inclusive time for each function and total
* call count for each function across all parents the
* function is called from.
*/
foreach ($symbols as $symbol) {
$child = $symbol->getChild();
if (!isset($symbol_tab[$child])) {
$symbol_tab[$child] = array("ct" => $symbol->getCt());
foreach ($metrics as $metric) {
$symbol_tab[$child][$metric] = $symbol->getMetric($metric);
}
}
else {
// increment call count for this child
$symbol_tab[$child]["ct"] += $symbol->getCt();
// update inclusive times/metric for this child
foreach ($metrics as $metric) {
$symbol_tab[$child][$metric] += $symbol->getMetric($metric);
}
}
}
return $symbol_tab;
}
/**
* @param Symbol[] $symbols
* @param $functions_to_keep
*
* @return array
*/
function trimRun($symbols, $functions_to_keep) {
// convert list of functions to a hash with function as the key
$function_map = array_fill_keys($functions_to_keep, 1);
// always keep main() as well so that overall totals can still
// be computed if need be.
$function_map['main()'] = 1;
$new_symbols = array();
foreach ($symbols as $symbol) {
$parent = $symbol->getParent();
$child = $symbol->getChild();
if (isset($function_map[$parent]) || isset($function_map[$child])) {
$new_symbols["{$parent}==>$child"] = $symbol;
}
}
return $new_symbols;
}
/**
* @param $arr
* @param $k
* @param $v
*
* @return mixed
*/
function arraySet($arr, $k, $v) {
$arr[$k] = $v;
return $arr;
}
/**
* @param $arr
* @param $k
*
* @return mixed
*/
function arrayUnset($arr, $k) {
unset($arr[$k]);
return $arr;
}
/**
* @return mixed
*/
public function getTotals() {
return $this->overall_totals;
}
/**
* @return mixed
*/
public function getDisplayCalls() {
return $this->display_calls;
}
}
