Issues (4069)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

SugarXHprof/xhprof_lib/utils/xhprof_lib.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3
//  Copyright (c) 2009 Facebook
4
//
5
//  Licensed under the Apache License, Version 2.0 (the "License");
6
//  you may not use this file except in compliance with the License.
7
//  You may obtain a copy of the License at
8
//
9
//      http://www.apache.org/licenses/LICENSE-2.0
10
//
11
//  Unless required by applicable law or agreed to in writing, software
12
//  distributed under the License is distributed on an "AS IS" BASIS,
13
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
//  See the License for the specific language governing permissions and
15
//  limitations under the License.
16
//
17
18
//
19
// This file contains various XHProf library (utility) functions.
20
// Do not add any display specific code here.
21
//
22
23
function xhprof_error($message) {
24
  error_log($message);
25
}
26
27
/*
28
 * The list of possible metrics collected as part of XHProf that
29
 * require inclusive/exclusive handling while reporting.
30
 *
31
 * @author Kannan
32
 */
33
function xhprof_get_possible_metrics() {
34
 static $possible_metrics =
35
   array("wt" => array("Wall", "microsecs", "walltime"),
36
         "ut" => array("User", "microsecs", "user cpu time"),
37
         "st" => array("Sys", "microsecs", "system cpu time"),
38
         "cpu" => array("Cpu", "microsecs", "cpu time"),
39
         "mu" => array("MUse", "bytes", "memory usage"),
40
         "pmu" => array("PMUse", "bytes", "peak memory usage"),
41
         "samples" => array("Samples", "samples", "cpu time"));
42
 return $possible_metrics;
43
}
44
45
/**
46
 * Initialize the metrics we'll display based on the information
47
 * in the raw data.
48
 *
49
 * @author Kannan
50
 */
51
function init_metrics($xhprof_data, $rep_symbol, $sort, $diff_report = false) {
52
  global $stats;
53
  global $pc_stats;
54
  global $metrics;
55
  global $diff_mode;
56
  global $sortable_columns;
57
  global $sort_col;
58
  global $display_calls;
59
60
  $diff_mode = $diff_report;
61
62
  if (!empty($sort)) {
63
    if (array_key_exists($sort, $sortable_columns)) {
64
      $sort_col = $sort;
65
    } else {
66
      print("Invalid Sort Key $sort specified in URL");
67
    }
68
  }
69
70
  // For C++ profiler runs, walltime attribute isn't present.
71
  // In that case, use "samples" as the default sort column.
72
  if (!isset($xhprof_data["main()"]["wt"])) {
73
74
    if ($sort_col == "wt") {
75
      $sort_col = "samples";
76
    }
77
78
    // C++ profiler data doesn't have call counts.
79
    // ideally we should check to see if "ct" metric
80
    // is present for "main()". But currently "ct"
81
    // metric is artificially set to 1. So, relying
82
    // on absence of "wt" metric instead.
83
    $display_calls = false;
84
  } else {
85
    $display_calls = true;
86
  }
87
88
  // parent/child report doesn't support exclusive times yet.
89
  // So, change sort hyperlinks to closest fit.
90
  if (!empty($rep_symbol)) {
91
    $sort_col = str_replace("excl_", "", $sort_col);
92
  }
93
94
  if ($display_calls) {
95
    $stats = array("fn", "ct", "Calls%");
96
  } else {
97
    $stats = array("fn");
98
  }
99
100
  $pc_stats = $stats;
101
102
  $possible_metrics = xhprof_get_possible_metrics($xhprof_data);
0 ignored issues
show
The call to xhprof_get_possible_metrics() has too many arguments starting with $xhprof_data.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
103
  foreach ($possible_metrics as $metric => $desc) {
104
    if (isset($xhprof_data["main()"][$metric])) {
105
      $metrics[] = $metric;
106
      // flat (top-level reports): we can compute
107
      // exclusive metrics reports as well.
108
      $stats[] = $metric;
109
      $stats[] = "I" . $desc[0] . "%";
110
      $stats[] = "excl_" . $metric;
111
      $stats[] = "E" . $desc[0] . "%";
112
113
      // parent/child report for a function: we can
114
      // only breakdown inclusive times correctly.
115
      $pc_stats[] = $metric;
116
      $pc_stats[] = "I" . $desc[0] . "%";
117
    }
118
  }
119
}
120
121
/*
122
 * Get the list of metrics present in $xhprof_data as an array.
123
 *
124
 * @author Kannan
125
 */
126
function xhprof_get_metrics($xhprof_data) {
127
128
  // get list of valid metrics
129
  $possible_metrics = xhprof_get_possible_metrics();
130
131
  // return those that are present in the raw data.
132
  // We'll just look at the root of the subtree for this.
133
  $metrics = array();
134
  foreach ($possible_metrics as $metric => $desc) {
135
    if (isset($xhprof_data["main()"][$metric])) {
136
      $metrics[] = $metric;
137
    }
138
  }
139
140
  return $metrics;
141
}
142
143
/**
144
 * Takes a parent/child function name encoded as
145
 * "a==>b" and returns array("a", "b").
146
 *
147
 * @author Kannan
148
 */
149
function xhprof_parse_parent_child($parent_child) {
150
  $ret = explode("==>", $parent_child);
151
152
  // Return if both parent and child are set
153
  if (isset($ret[1])) {
154
    return $ret;
155
  }
156
157
  return array(null, $ret[0]);
158
}
159
160
/**
161
 * Given parent & child function name, composes the key
162
 * in the format present in the raw data.
163
 *
164
 * @author Kannan
165
 */
166
function xhprof_build_parent_child_key($parent, $child) {
167
  if ($parent) {
168
    return $parent . "==>" . $child;
169
  } else {
170
    return $child;
171
  }
172
}
173
174
175
/**
176
 * Checks if XHProf raw data appears to be valid and not corrupted.
177
 *
178
 *  @param   int    $run_id        Run id of run to be pruned.
179
 *                                 [Used only for reporting errors.]
180
 *  @param   array  $raw_data      XHProf raw data to be pruned
181
 *                                 & validated.
182
 *
183
 *  @return  bool   true on success, false on failure
184
 *
185
 *  @author Kannan
186
 */
187
function xhprof_valid_run($run_id, $raw_data) {
188
189
  $main_info = $raw_data["main()"];
190
  if (empty($main_info)) {
191
    xhprof_error("XHProf: main() missing in raw data for Run ID: $run_id");
192
    return false;
193
  }
194
195
  // raw data should contain either wall time or samples information...
196
  if (isset($main_info["wt"])) {
197
    $metric = "wt";
198
  } else if (isset($main_info["samples"])) {
199
    $metric = "samples";
200
  } else {
201
    xhprof_error("XHProf: Wall Time information missing from Run ID: $run_id");
202
    return false;
203
  }
204
205
  foreach ($raw_data as $info) {
206
    $val = $info[$metric];
207
208
    // basic sanity checks...
209
    if ($val < 0) {
210
      xhprof_error("XHProf: $metric should not be negative: Run ID $run_id"
211
                   . serialize($info));
212
      return false;
213
    }
214
    if ($val > (86400000000)) {
215
      xhprof_error("XHProf: $metric > 1 day found in Run ID: $run_id "
216
                   . serialize($info));
217
      return false;
218
    }
219
  }
220
  return true;
221
}
222
223
224
/**
225
 * Return a trimmed version of the XHProf raw data. Note that the raw
226
 * data contains one entry for each unique parent/child function
227
 * combination.The trimmed version of raw data will only contain
228
 * entries where either the parent or child function is in the list
229
 * of $functions_to_keep.
230
 *
231
 * Note: Function main() is also always kept so that overall totals
232
 * can still be obtained from the trimmed version.
233
 *
234
 * @param  array  XHProf raw data
235
 * @param  array  array of function names
236
 *
237
 * @return array  Trimmed XHProf Report
238
 *
239
 * @author Kannan
240
 */
241
function xhprof_trim_run($raw_data, $functions_to_keep) {
242
243
  // convert list of functions to a hash with function as the key
244
  $function_map = array_fill_keys($functions_to_keep, 1);
245
246
  // always keep main() as well so that overall totals can still
247
  // be computed if need be.
248
  $function_map['main()'] = 1;
249
250
  $new_raw_data = array();
251
  foreach ($raw_data as $parent_child => $info) {
252
    list($parent, $child) = xhprof_parse_parent_child($parent_child);
253
254
    if (isset($function_map[$parent]) || isset($function_map[$child])) {
255
      $new_raw_data[$parent_child] = $info;
256
    }
257
  }
258
259
  return $new_raw_data;
260
}
261
262
/**
263
 * Takes raw XHProf data that was aggregated over "$num_runs" number
264
 * of runs averages/nomalizes the data. Essentially the various metrics
265
 * collected are divided by $num_runs.
266
 *
267
 * @author Kannan
268
 */
269
function xhprof_normalize_metrics($raw_data, $num_runs) {
270
271
  if (empty($raw_data) || ($num_runs == 0)) {
272
    return $raw_data;
273
  }
274
275
  $raw_data_total = array();
276
277
  if (isset($raw_data["==>main()"]) && isset($raw_data["main()"])) {
278
    xhprof_error("XHProf Error: both ==>main() and main() set in raw data...");
279
  }
280
281
  foreach ($raw_data as $parent_child => $info) {
282
    foreach ($info as $metric => $value) {
283
      $raw_data_total[$parent_child][$metric] = ($value / $num_runs);
284
    }
285
  }
286
287
  return $raw_data_total;
288
}
289
290
291
/**
292
 * Get raw data corresponding to specified array of runs
293
 * aggregated by certain weightage.
294
 *
295
 * Suppose you have run:5 corresponding to page1.php,
296
 *                  run:6 corresponding to page2.php,
297
 *             and  run:7 corresponding to page3.php
298
 *
299
 * and you want to accumulate these runs in a 2:4:1 ratio. You
300
 * can do so by calling:
301
 *
302
 *     xhprof_aggregate_runs(array(5, 6, 7), array(2, 4, 1));
303
 *
304
 * The above will return raw data for the runs aggregated
305
 * in 2:4:1 ratio.
306
 *
307
 *  @param object  $xhprof_runs_impl  An object that implements
308
 *                                    the iXHProfRuns interface
309
 *  @param  array  $runs            run ids of the XHProf runs..
310
 *  @param  array  $wts             integral (ideally) weights for $runs
311
 *  @param  string $source          source to fetch raw data for run from
312
 *  @param  bool   $use_script_name If true, a fake edge from main() to
313
 *                                  to __script::<scriptname> is introduced
314
 *                                  in the raw data so that after aggregations
315
 *                                  the script name is still preserved.
316
 *
317
 *  @return array  Return aggregated raw data
318
 *
319
 *  @author Kannan
320
 */
321
function xhprof_aggregate_runs($xhprof_runs_impl, $runs,
322
                               $wts, $source="phprof",
323
                               $use_script_name=false) {
324
325
  $raw_data_total = null;
326
  $raw_data       = null;
327
  $metrics        = array();
328
329
  $run_count = count($runs);
330
  $wts_count = count($wts);
331
332
  if (($run_count == 0) ||
333
      (($wts_count > 0) && ($run_count != $wts_count))) {
334
    return array('description' => 'Invalid input..',
335
                 'raw'  => null);
336
  }
337
338
  $bad_runs = array();
339
  foreach ($runs as $idx => $run_id) {
340
341
    $raw_data = $xhprof_runs_impl->get_run($run_id, $source, $description);
342
343
    // use the first run to derive what metrics to aggregate on.
344
    if ($idx == 0) {
345
      foreach ($raw_data["main()"] as $metric => $val) {
346
        if ($metric != "pmu") {
347
          // for now, just to keep data size small, skip "peak" memory usage
348
          // data while aggregating.
349
          // The "regular" memory usage data will still be tracked.
350
          if (isset($val)) {
351
            $metrics[] = $metric;
352
          }
353
        }
354
      }
355
    }
356
357
    if (!xhprof_valid_run($run_id, $raw_data)) {
358
      $bad_runs[] = $run_id;
359
      continue;
360
    }
361
362
    if ($use_script_name) {
363
      $page = $description;
364
365
      // create a fake function '__script::$page', and have and edge from
366
      // main() to '__script::$page'. We will also need edges to transfer
367
      // all edges originating from main() to now originate from
368
      // '__script::$page' to all function called from main().
369
      //
370
      // We also weight main() ever so slightly higher so that
371
      // it shows up above the new entry in reports sorted by
372
      // inclusive metrics or call counts.
373
      if ($page) {
374
        foreach ($raw_data["main()"] as $metric => $val) {
375
          $fake_edge[$metric] = $val;
376
          $new_main[$metric]  = $val + 0.00001;
377
        }
378
        $raw_data["main()"] = $new_main;
379
        $raw_data[xhprof_build_parent_child_key("main()",
380
                                                "__script::$page")]
381
          = $fake_edge;
382
      } else {
383
        $use_script_name = false;
384
      }
385
    }
386
387
    // if no weights specified, use 1 as the default weightage..
388
    $wt = ($wts_count == 0) ? 1 : $wts[$idx];
389
390
    // aggregate $raw_data into $raw_data_total with appropriate weight ($wt)
391
    foreach ($raw_data as $parent_child => $info) {
392
      if ($use_script_name) {
393
        // if this is an old edge originating from main(), it now
394
        // needs to be from '__script::$page'
395
        if (substr($parent_child, 0, 9) == "main()==>") {
396
          $child = substr($parent_child, 9);
397
          // ignore the newly added edge from main()
398
          if (substr($child, 0, 10) != "__script::") {
399
            $parent_child = xhprof_build_parent_child_key("__script::$page",
400
                                                          $child);
401
          }
402
        }
403
      }
404
405
      if (!isset($raw_data_total[$parent_child])) {
406
        foreach ($metrics as $metric) {
407
          $raw_data_total[$parent_child][$metric] = ($wt * $info[$metric]);
408
        }
409
      } else {
410
        foreach ($metrics as $metric) {
411
          $raw_data_total[$parent_child][$metric] += ($wt * $info[$metric]);
412
        }
413
      }
414
    }
415
  }
416
417
  $runs_string = implode(",", $runs);
418
419
  if (isset($wts)) {
420
    $wts_string  = "in the ratio (" . implode(":", $wts) . ")";
421
    $normalization_count = array_sum($wts);
422
  } else {
423
    $wts_string = "";
424
    $normalization_count = $run_count;
425
  }
426
427
  $run_count = $run_count - count($bad_runs);
428
429
  $data['description'] = "Aggregated Report for $run_count runs: ".
430
                         "$runs_string $wts_string\n";
431
  $data['raw'] = xhprof_normalize_metrics($raw_data_total,
432
                                          $normalization_count);
433
  $data['bad_runs'] = $bad_runs;
434
435
  return $data;
436
}
437
438
439
/**
440
 * Analyze hierarchical raw data, and compute per-function (flat)
441
 * inclusive and exclusive metrics.
442
 *
443
 * Also, store overall totals in the 2nd argument.
444
 *
445
 * @param  array $raw_data          XHProf format raw profiler data.
446
 * @param  array &$overall_totals   OUT argument for returning
447
 *                                  overall totals for various
448
 *                                  metrics.
449
 * @return array Returns a map from function name to its
450
 *               call count and inclusive & exclusive metrics
451
 *               (such as wall time, etc.).
452
 *
453
 * @author Kannan Muthukkaruppan
454
 */
455
function xhprof_compute_flat_info($raw_data, &$overall_totals) {
456
457
  global $display_calls;
458
459
  $metrics = xhprof_get_metrics($raw_data);
460
461
  $overall_totals = array("ct" => 0,
462
                           "wt" => 0,
463
                           "ut" => 0,
464
                           "st" => 0,
465
                           "cpu" => 0,
466
                           "mu" => 0,
467
                           "pmu" => 0,
468
                           "samples" => 0
469
                           );
470
471
  // compute inclusive times for each function
472
  $symbol_tab = xhprof_compute_inclusive_times($raw_data);
473
474
  /* total metric value is the metric value for "main()" */
475
  foreach ($metrics as $metric) {
476
    $overall_totals[$metric] = $symbol_tab["main()"][$metric];
477
  }
478
479
  /*
480
   * initialize exclusive (self) metric value to inclusive metric value
481
   * to start with.
482
   * In the same pass, also add up the total number of function calls.
483
   */
484
  foreach ($symbol_tab as $symbol => $info) {
0 ignored issues
show
The expression $symbol_tab of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
485
    foreach ($metrics as $metric) {
486
      $symbol_tab[$symbol]["excl_" . $metric] = $symbol_tab[$symbol][$metric];
487
    }
488
    if ($display_calls) {
489
      /* keep track of total number of calls */
490
      $overall_totals["ct"] += $info["ct"];
491
    }
492
  }
493
494
  /* adjust exclusive times by deducting inclusive time of children */
495
  foreach ($raw_data as $parent_child => $info) {
496
    list($parent, $child) = xhprof_parse_parent_child($parent_child);
0 ignored issues
show
The assignment to $child is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
497
498
    if ($parent) {
499
      foreach ($metrics as $metric) {
500
        // make sure the parent exists hasn't been pruned.
501
        if (isset($symbol_tab[$parent])) {
502
          $symbol_tab[$parent]["excl_" . $metric] -= $info[$metric];
503
        }
504
      }
505
    }
506
  }
507
508
  return $symbol_tab;
509
}
510
511
/**
512
 * Hierarchical diff:
513
 * Compute and return difference of two call graphs: Run2 - Run1.
514
 *
515
 * @author Kannan
516
 */
517
function xhprof_compute_diff($xhprof_data1, $xhprof_data2) {
518
  global $display_calls;
519
520
  // use the second run to decide what metrics we will do the diff on
521
  $metrics = xhprof_get_metrics($xhprof_data2);
522
523
  $xhprof_delta = $xhprof_data2;
524
525
  foreach ($xhprof_data1 as $parent_child => $info) {
526
527
    if (!isset($xhprof_delta[$parent_child])) {
528
529
      // this pc combination was not present in run1;
530
      // initialize all values to zero.
531
      if ($display_calls) {
532
        $xhprof_delta[$parent_child] = array("ct" => 0);
533
      } else {
534
        $xhprof_delta[$parent_child] = array();
535
      }
536
      foreach ($metrics as $metric) {
537
        $xhprof_delta[$parent_child][$metric] = 0;
538
      }
539
    }
540
541
    if ($display_calls) {
542
      $xhprof_delta[$parent_child]["ct"] -= $info["ct"];
543
    }
544
545
    foreach ($metrics as $metric) {
546
      $xhprof_delta[$parent_child][$metric] -= $info[$metric];
547
    }
548
  }
549
550
  return $xhprof_delta;
551
}
552
553
554
/**
555
 * Compute inclusive metrics for function. This code was factored out
556
 * of xhprof_compute_flat_info().
557
 *
558
 * The raw data contains inclusive metrics of a function for each
559
 * unique parent function it is called from. The total inclusive metrics
560
 * for a function is therefore the sum of inclusive metrics for the
561
 * function across all parents.
562
 *
563
 * @return array  Returns a map of function name to total (across all parents)
564
 *                inclusive metrics for the function.
565
 *
566
 * @author Kannan
567
 */
568
function xhprof_compute_inclusive_times($raw_data) {
569
  global $display_calls;
570
571
  $metrics = xhprof_get_metrics($raw_data);
572
573
  $symbol_tab = array();
574
575
  /*
576
   * First compute inclusive time for each function and total
577
   * call count for each function across all parents the
578
   * function is called from.
579
   */
580
  foreach ($raw_data as $parent_child => $info) {
581
582
    list($parent, $child) = xhprof_parse_parent_child($parent_child);
583
584
    if ($parent == $child) {
585
      /*
586
       * XHProf PHP extension should never trigger this situation any more.
587
       * Recursion is handled in the XHProf PHP extension by giving nested
588
       * calls a unique recursion-depth appended name (for example, foo@1).
589
       */
590
      xhprof_error("Error in Raw Data: parent & child are both: $parent");
591
      return;
592
    }
593
594
    if (!isset($symbol_tab[$child])) {
595
596
      if ($display_calls) {
597
        $symbol_tab[$child] = array("ct" => $info["ct"]);
598
      } else {
599
        $symbol_tab[$child] = array();
600
      }
601
      foreach ($metrics as $metric) {
602
        $symbol_tab[$child][$metric] = $info[$metric];
603
      }
604
    } else {
605
      if ($display_calls) {
606
        /* increment call count for this child */
607
        $symbol_tab[$child]["ct"] += $info["ct"];
608
      }
609
610
      /* update inclusive times/metric for this child  */
611
      foreach ($metrics as $metric) {
612
        $symbol_tab[$child][$metric] += $info[$metric];
613
      }
614
    }
615
  }
616
617
  return $symbol_tab;
618
}
619
620
621
/*
622
 * Prunes XHProf raw data:
623
 *
624
 * Any node whose inclusive walltime accounts for less than $prune_percent
625
 * of total walltime is pruned. [It is possible that a child function isn't
626
 * pruned, but one or more of its parents get pruned. In such cases, when
627
 * viewing the child function's hierarchical information, the cost due to
628
 * the pruned parent(s) will be attributed to a special function/symbol
629
 * "__pruned__()".]
630
 *
631
 *  @param   array  $raw_data      XHProf raw data to be pruned & validated.
632
 *  @param   double $prune_percent Any edges that account for less than
633
 *                                 $prune_percent of time will be pruned
634
 *                                 from the raw data.
635
 *
636
 *  @return  array  Returns the pruned raw data.
637
 *
638
 *  @author Kannan
639
 */
640
function xhprof_prune_run($raw_data, $prune_percent) {
641
642
  $main_info = $raw_data["main()"];
643
  if (empty($main_info)) {
644
    xhprof_error("XHProf: main() missing in raw data");
645
    return false;
646
  }
647
648
  // raw data should contain either wall time or samples information...
649
  if (isset($main_info["wt"])) {
650
    $prune_metric = "wt";
651
  } else if (isset($main_info["samples"])) {
652
    $prune_metric = "samples";
653
  } else {
654
    xhprof_error("XHProf: for main() we must have either wt "
655
                 ."or samples attribute set");
656
    return false;
657
  }
658
659
  // determine the metrics present in the raw data..
660
  $metrics = array();
661
  foreach ($main_info as $metric => $val) {
662
    if (isset($val)) {
663
      $metrics[] = $metric;
664
    }
665
  }
666
667
  $prune_threshold = (($main_info[$prune_metric] * $prune_percent) / 100.0);
668
669
  init_metrics($raw_data, null, null, false);
670
  $flat_info = xhprof_compute_inclusive_times($raw_data);
671
672
  foreach ($raw_data as $parent_child => $info) {
673
674
    list($parent, $child) = xhprof_parse_parent_child($parent_child);
675
676
    // is this child's overall total from all parents less than threshold?
677
    if ($flat_info[$child][$prune_metric] < $prune_threshold) {
678
      unset($raw_data[$parent_child]); // prune the edge
679
    } else if ($parent &&
680
               ($parent != "__pruned__()") &&
681
               ($flat_info[$parent][$prune_metric] < $prune_threshold)) {
682
683
      // Parent's overall inclusive metric is less than a threshold.
684
      // All edges to the parent node will get nuked, and this child will
685
      // be a dangling child.
686
      // So instead change its parent to be a special function __pruned__().
687
      $pruned_edge = xhprof_build_parent_child_key("__pruned__()", $child);
688
689
      if (isset($raw_data[$pruned_edge])) {
690
        foreach ($metrics as $metric) {
691
          $raw_data[$pruned_edge][$metric]+=$raw_data[$parent_child][$metric];
692
        }
693
      } else {
694
        $raw_data[$pruned_edge] = $raw_data[$parent_child];
695
      }
696
697
      unset($raw_data[$parent_child]); // prune the edge
698
    }
699
  }
700
701
  return $raw_data;
702
}
703
704
705
/**
706
 * Set one key in an array and return the array
707
 *
708
 * @author Kannan
709
 */
710
function xhprof_array_set($arr, $k, $v) {
711
  $arr[$k] = $v;
712
  return $arr;
713
}
714
715
/**
716
 * Removes/unsets one key in an array and return the array
717
 *
718
 * @author Kannan
719
 */
720
function xhprof_array_unset($arr, $k) {
721
  unset($arr[$k]);
722
  return $arr;
723
}
724
725
/**
726
 * Type definitions for URL params
727
 */
728
define('XHPROF_STRING_PARAM', 1);
729
define('XHPROF_UINT_PARAM',   2);
730
define('XHPROF_FLOAT_PARAM',  3);
731
define('XHPROF_BOOL_PARAM',   4);
732
733
734
/**
735
 * Internal helper function used by various
736
 * xhprof_get_param* flavors for various
737
 * types of parameters.
738
 *
739
 * @param string   name of the URL query string param
740
 *
741
 * @author Kannan
742
 */
743
function xhprof_get_param_helper($param) {
744
  $val = null;
745
  if (isset($_GET[$param]))
746
    $val = $_GET[$param];
747
  else if (isset($_POST[$param])) {
748
    $val = $_POST[$param];
749
  }
750
  return $val;
751
}
752
753
/**
754
 * Extracts value for string param $param from query
755
 * string. If param is not specified, return the
756
 * $default value.
757
 *
758
 * @author Kannan
759
 */
760
function xhprof_get_string_param($param, $default = '') {
761
  $val = xhprof_get_param_helper($param);
762
763
  if ($val === null)
764
    return $default;
765
766
  return $val;
767
}
768
769
/**
770
 * Extracts value for unsigned integer param $param from
771
 * query string. If param is not specified, return the
772
 * $default value.
773
 *
774
 * If value is not a valid unsigned integer, logs error
775
 * and returns null.
776
 *
777
 * @author Kannan
778
 */
779
function xhprof_get_uint_param($param, $default = 0) {
780
  $val = xhprof_get_param_helper($param);
781
782
  if ($val === null)
783
    $val = $default;
784
785
  // trim leading/trailing whitespace
786
  $val = trim($val);
787
788
  // if it only contains digits, then ok..
789
  if (ctype_digit($val)) {
790
    return $val;
791
  }
792
793
  xhprof_error("$param is $val. It must be an unsigned integer.");
794
  return null;
795
}
796
797
798
/**
799
 * Extracts value for a float param $param from
800
 * query string. If param is not specified, return
801
 * the $default value.
802
 *
803
 * If value is not a valid unsigned integer, logs error
804
 * and returns null.
805
 *
806
 * @author Kannan
807
 */
808
function xhprof_get_float_param($param, $default = 0) {
809
  $val = xhprof_get_param_helper($param);
810
811
  if ($val === null)
812
    $val = $default;
813
814
  // trim leading/trailing whitespace
815
  $val = trim($val);
816
817
  // TBD: confirm the value is indeed a float.
818
  if (true) // for now..
0 ignored issues
show
Avoid IF statements that are always true or false
Loading history...
819
    return (float)$val;
820
821
  xhprof_error("$param is $val. It must be a float.");
822
  return null;
823
}
824
825
/**
826
 * Extracts value for a boolean param $param from
827
 * query string. If param is not specified, return
828
 * the $default value.
829
 *
830
 * If value is not a valid unsigned integer, logs error
831
 * and returns null.
832
 *
833
 * @author Kannan
834
 */
835
function xhprof_get_bool_param($param, $default = false) {
836
  $val = xhprof_get_param_helper($param);
837
838
  if ($val === null)
839
    $val = $default;
840
841
  // trim leading/trailing whitespace
842
  $val = trim($val);
843
844
  switch (strtolower($val)) {
845
  case '0':
846
  case '1':
847
    $val = (bool)$val;
848
    break;
849
  case 'true':
850
  case 'on':
851
  case 'yes':
852
    $val = true;
853
    break;
854
  case 'false':
855
  case 'off':
856
  case 'no':
857
    $val = false;
858
    break;
859
  default:
860
    xhprof_error("$param is $val. It must be a valid boolean string.");
861
    return null;
862
  }
863
864
  return $val;
865
866
}
867
868
/**
869
 * Initialize params from URL query string. The function
870
 * creates globals variables for each of the params
871
 * and if the URL query string doesn't specify a particular
872
 * param initializes them with the corresponding default
873
 * value specified in the input.
874
 *
875
 * @params array $params An array whose keys are the names
876
 *                       of URL params who value needs to
877
 *                       be retrieved from the URL query
878
 *                       string. PHP globals are created
879
 *                       with these names. The value is
880
 *                       itself an array with 2-elems (the
881
 *                       param type, and its default value).
882
 *                       If a param is not specified in the
883
 *                       query string the default value is
884
 *                       used.
885
 * @author Kannan
886
 */
887
function xhprof_param_init($params) {
888
  /* Create variables specified in $params keys, init defaults */
889
  foreach ($params as $k => $v) {
890
    switch ($v[0]) {
891
    case XHPROF_STRING_PARAM:
892
      $p = xhprof_get_string_param($k, $v[1]);
893
      break;
894
    case XHPROF_UINT_PARAM:
895
      $p = xhprof_get_uint_param($k, $v[1]);
896
      break;
897
    case XHPROF_FLOAT_PARAM:
898
      $p = xhprof_get_float_param($k, $v[1]);
899
      break;
900
    case XHPROF_BOOL_PARAM:
901
      $p = xhprof_get_bool_param($k, $v[1]);
902
      break;
903
    default:
904
      xhprof_error("Invalid param type passed to xhprof_param_init: "
905
                   . $v[0]);
906
      exit();
907
    }
908
909
    // create a global variable using the parameter name.
910
    $GLOBALS[$k] = $p;
911
  }
912
}
913
914
915
/**
916
 * Given a partial query string $q return matching function names in
917
 * specified XHProf run. This is used for the type ahead function
918
 * selector.
919
 *
920
 * @author Kannan
921
 */
922
function xhprof_get_matching_functions($q, $xhprof_data) {
923
924
  $matches = array();
925
926
  foreach ($xhprof_data as $parent_child => $info) {
927
    list($parent, $child) = xhprof_parse_parent_child($parent_child);
928
    if (stripos($parent, $q) !== false) {
929
      $matches[$parent] = 1;
930
    }
931
    if (stripos($child, $q) !== false) {
932
      $matches[$child] = 1;
933
    }
934
  }
935
936
  $res = array_keys($matches);
937
938
  // sort it so the answers are in some reliable order...
939
  asort($res);
940
941
  return ($res);
942
}
943
944