mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 14:52:41 +01:00
Very rough cut of DarkConsole + XHProf
This commit is contained in:
parent
246cba2bf0
commit
759eec3a77
52 changed files with 2728 additions and 60 deletions
177
externals/xhprof/LICENSE
vendored
Normal file
177
externals/xhprof/LICENSE
vendored
Normal file
|
@ -0,0 +1,177 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
866
externals/xhprof/xhprof_lib.php
vendored
Normal file
866
externals/xhprof/xhprof_lib.php
vendored
Normal file
|
@ -0,0 +1,866 @@
|
|||
<?php
|
||||
// Copyright (c) 2009 Facebook
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
//
|
||||
// This file contains various XHProf library (utility) functions.
|
||||
// Do not add any display specific code here.
|
||||
//
|
||||
|
||||
function xhprof_error($message) {
|
||||
error_log($message);
|
||||
}
|
||||
|
||||
/*
|
||||
* The list of possible metrics collected as part of XHProf that
|
||||
* require inclusive/exclusive handling while reporting.
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_get_possible_metrics() {
|
||||
static $possible_metrics =
|
||||
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 $possible_metrics;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the list of metrics present in $xhprof_data as an array.
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_get_metrics($xhprof_data) {
|
||||
|
||||
// get list of valid metrics
|
||||
$possible_metrics = xhprof_get_possible_metrics();
|
||||
|
||||
// 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) {
|
||||
if (isset($xhprof_data["main()"][$metric])) {
|
||||
$metrics[] = $metric;
|
||||
}
|
||||
}
|
||||
|
||||
return $metrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a parent/child function name encoded as
|
||||
* "a==>b" and returns array("a", "b").
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_parse_parent_child($parent_child) {
|
||||
$ret = explode("==>", $parent_child);
|
||||
|
||||
// Return if both parent and child are set
|
||||
if (isset($ret[1])) {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
return array(null, $ret[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given parent & child function name, composes the key
|
||||
* in the format present in the raw data.
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_build_parent_child_key($parent, $child) {
|
||||
if ($parent) {
|
||||
return $parent . "==>" . $child;
|
||||
} else {
|
||||
return $child;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if XHProf raw data appears to be valid and not corrupted.
|
||||
*
|
||||
* @param int $run_id Run id of run to be pruned.
|
||||
* [Used only for reporting errors.]
|
||||
* @param array $raw_data XHProf raw data to be pruned
|
||||
* & validated.
|
||||
*
|
||||
* @return bool true on success, false on failure
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_valid_run($run_id, $raw_data) {
|
||||
|
||||
$main_info = $raw_data["main()"];
|
||||
if (empty($main_info)) {
|
||||
xhprof_error("XHProf: main() missing in raw data for Run ID: $run_id");
|
||||
return false;
|
||||
}
|
||||
|
||||
// raw data should contain either wall time or samples information...
|
||||
if (isset($main_info["wt"])) {
|
||||
$metric = "wt";
|
||||
} else if (isset($main_info["samples"])) {
|
||||
$metric = "samples";
|
||||
} else {
|
||||
xhprof_error("XHProf: Wall Time information missing from Run ID: $run_id");
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($raw_data as $info) {
|
||||
$val = $info[$metric];
|
||||
|
||||
// basic sanity checks...
|
||||
if ($val < 0) {
|
||||
xhprof_error("XHProf: $metric should not be negative: Run ID $run_id"
|
||||
. serialize($info));
|
||||
return false;
|
||||
}
|
||||
if ($val > (86400000000)) {
|
||||
xhprof_error("XHProf: $metric > 1 day found in Run ID: $run_id "
|
||||
. serialize($info));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a trimmed version of the XHProf raw data. Note that the raw
|
||||
* data contains one entry for each unique parent/child function
|
||||
* combination.The trimmed version of raw data will only contain
|
||||
* entries where either the parent or child function is in the list
|
||||
* of $functions_to_keep.
|
||||
*
|
||||
* Note: Function main() is also always kept so that overall totals
|
||||
* can still be obtained from the trimmed version.
|
||||
*
|
||||
* @param array XHProf raw data
|
||||
* @param array array of function names
|
||||
*
|
||||
* @return array Trimmed XHProf Report
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_trim_run($raw_data, $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_raw_data = array();
|
||||
foreach ($raw_data as $parent_child => $info) {
|
||||
list($parent, $child) = xhprof_parse_parent_child($parent_child);
|
||||
|
||||
if (isset($function_map[$parent]) || isset($function_map[$child])) {
|
||||
$new_raw_data[$parent_child] = $info;
|
||||
}
|
||||
}
|
||||
|
||||
return $new_raw_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes raw XHProf data that was aggregated over "$num_runs" number
|
||||
* of runs averages/nomalizes the data. Essentially the various metrics
|
||||
* collected are divided by $num_runs.
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_normalize_metrics($raw_data, $num_runs) {
|
||||
|
||||
if (empty($raw_data) || ($num_runs == 0)) {
|
||||
return $raw_data;
|
||||
}
|
||||
|
||||
$raw_data_total = array();
|
||||
|
||||
if (isset($raw_data["==>main()"]) && isset($raw_data["main()"])) {
|
||||
xhprof_error("XHProf Error: both ==>main() and main() set in raw data...");
|
||||
}
|
||||
|
||||
foreach ($raw_data as $parent_child => $info) {
|
||||
foreach ($info as $metric => $value) {
|
||||
$raw_data_total[$parent_child][$metric] = ($value / $num_runs);
|
||||
}
|
||||
}
|
||||
|
||||
return $raw_data_total;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get raw data corresponding to specified array of runs
|
||||
* aggregated by certain weightage.
|
||||
*
|
||||
* Suppose you have run:5 corresponding to page1.php,
|
||||
* run:6 corresponding to page2.php,
|
||||
* and run:7 corresponding to page3.php
|
||||
*
|
||||
* and you want to accumulate these runs in a 2:4:1 ratio. You
|
||||
* can do so by calling:
|
||||
*
|
||||
* xhprof_aggregate_runs(array(5, 6, 7), array(2, 4, 1));
|
||||
*
|
||||
* The above will return raw data for the runs aggregated
|
||||
* in 2:4:1 ratio.
|
||||
*
|
||||
* @param object $xhprof_runs_impl An object that implements
|
||||
* the iXHProfRuns interface
|
||||
* @param array $runs run ids of the XHProf runs..
|
||||
* @param array $wts integral (ideally) weights for $runs
|
||||
* @param string $source source to fetch raw data for run from
|
||||
* @param bool $use_script_name If true, a fake edge from main() to
|
||||
* to __script::<scriptname> is introduced
|
||||
* in the raw data so that after aggregations
|
||||
* the script name is still preserved.
|
||||
*
|
||||
* @return array Return aggregated raw data
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_aggregate_runs($xhprof_runs_impl, $runs,
|
||||
$wts, $source="phprof",
|
||||
$use_script_name=false) {
|
||||
|
||||
$raw_data_total = null;
|
||||
$raw_data = null;
|
||||
$metrics = array();
|
||||
|
||||
$run_count = count($runs);
|
||||
$wts_count = count($wts);
|
||||
|
||||
if (($run_count == 0) ||
|
||||
(($wts_count > 0) && ($run_count != $wts_count))) {
|
||||
return array('description' => 'Invalid input..',
|
||||
'raw' => null);
|
||||
}
|
||||
|
||||
$bad_runs = array();
|
||||
foreach($runs as $idx => $run_id) {
|
||||
|
||||
$raw_data = $xhprof_runs_impl->get_run($run_id, $source, $description);
|
||||
|
||||
// use the first run to derive what metrics to aggregate on.
|
||||
if ($idx == 0) {
|
||||
foreach ($raw_data["main()"] as $metric => $val) {
|
||||
if ($metric != "pmu") {
|
||||
// for now, just to keep data size small, skip "peak" memory usage
|
||||
// data while aggregating.
|
||||
// The "regular" memory usage data will still be tracked.
|
||||
if (isset($val)) {
|
||||
$metrics[] = $metric;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!xhprof_valid_run($run_id, $raw_data)) {
|
||||
$bad_runs[] = $run_id;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($use_script_name) {
|
||||
$page = $description;
|
||||
|
||||
// create a fake function '__script::$page', and have and edge from
|
||||
// main() to '__script::$page'. We will also need edges to transfer
|
||||
// all edges originating from main() to now originate from
|
||||
// '__script::$page' to all function called from main().
|
||||
//
|
||||
// We also weight main() ever so slightly higher so that
|
||||
// it shows up above the new entry in reports sorted by
|
||||
// inclusive metrics or call counts.
|
||||
if ($page) {
|
||||
foreach($raw_data["main()"] as $metric => $val) {
|
||||
$fake_edge[$metric] = $val;
|
||||
$new_main[$metric] = $val + 0.00001;
|
||||
}
|
||||
$raw_data["main()"] = $new_main;
|
||||
$raw_data[xhprof_build_parent_child_key("main()",
|
||||
"__script::$page")]
|
||||
= $fake_edge;
|
||||
} else {
|
||||
$use_script_name = false;
|
||||
}
|
||||
}
|
||||
|
||||
// if no weights specified, use 1 as the default weightage..
|
||||
$wt = ($wts_count == 0) ? 1 : $wts[$idx];
|
||||
|
||||
// aggregate $raw_data into $raw_data_total with appropriate weight ($wt)
|
||||
foreach ($raw_data as $parent_child => $info) {
|
||||
if ($use_script_name) {
|
||||
// if this is an old edge originating from main(), it now
|
||||
// needs to be from '__script::$page'
|
||||
if (substr($parent_child, 0, 9) == "main()==>") {
|
||||
$child =substr($parent_child, 9);
|
||||
// ignore the newly added edge from main()
|
||||
if (substr($child, 0, 10) != "__script::") {
|
||||
$parent_child = xhprof_build_parent_child_key("__script::$page",
|
||||
$child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($raw_data_total[$parent_child])) {
|
||||
foreach ($metrics as $metric) {
|
||||
$raw_data_total[$parent_child][$metric] = ($wt * $info[$metric]);
|
||||
}
|
||||
} else {
|
||||
foreach ($metrics as $metric) {
|
||||
$raw_data_total[$parent_child][$metric] += ($wt * $info[$metric]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$runs_string = implode(",", $runs);
|
||||
|
||||
if (isset($wts)) {
|
||||
$wts_string = "in the ratio (" . implode(":", $wts) . ")";
|
||||
$normalization_count = array_sum($wts);
|
||||
} else {
|
||||
$wts_string = "";
|
||||
$normalization_count = $run_count;
|
||||
}
|
||||
|
||||
$run_count = $run_count - count($bad_runs);
|
||||
|
||||
$data['description'] = "Aggregated Report for $run_count runs: ".
|
||||
"$runs_string $wts_string\n";
|
||||
$data['raw'] = xhprof_normalize_metrics($raw_data_total,
|
||||
$normalization_count);
|
||||
$data['bad_runs'] = $bad_runs;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Analyze hierarchical raw data, and compute per-function (flat)
|
||||
* inclusive and exclusive metrics.
|
||||
*
|
||||
* Also, store overall totals in the 2nd argument.
|
||||
*
|
||||
* @param array $raw_data XHProf format raw profiler data.
|
||||
* @param array &$overall_totals OUT argument for returning
|
||||
* overall totals for various
|
||||
* metrics.
|
||||
* @return array Returns a map from function name to its
|
||||
* call count and inclusive & exclusive metrics
|
||||
* (such as wall time, etc.).
|
||||
*
|
||||
* @author Kannan Muthukkaruppan
|
||||
*/
|
||||
function xhprof_compute_flat_info($raw_data, &$overall_totals) {
|
||||
|
||||
global $display_calls;
|
||||
|
||||
$metrics = xhprof_get_metrics($raw_data);
|
||||
|
||||
$overall_totals = array( "ct" => 0,
|
||||
"wt" => 0,
|
||||
"ut" => 0,
|
||||
"st" => 0,
|
||||
"cpu" => 0,
|
||||
"mu" => 0,
|
||||
"pmu" => 0,
|
||||
"samples" => 0
|
||||
);
|
||||
|
||||
// compute inclusive times for each function
|
||||
$symbol_tab = xhprof_compute_inclusive_times($raw_data);
|
||||
|
||||
/* total metric value is the metric value for "main()" */
|
||||
foreach ($metrics as $metric) {
|
||||
$overall_totals[$metric] = $symbol_tab["main()"][$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];
|
||||
}
|
||||
if ($display_calls) {
|
||||
/* keep track of total number of calls */
|
||||
$overall_totals["ct"] += $info["ct"];
|
||||
}
|
||||
}
|
||||
|
||||
/* adjust exclusive times by deducting inclusive time of children */
|
||||
foreach ($raw_data as $parent_child => $info) {
|
||||
list($parent, $child) = xhprof_parse_parent_child($parent_child);
|
||||
|
||||
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] -= $info[$metric];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $symbol_tab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hierarchical diff:
|
||||
* Compute and return difference of two call graphs: Run2 - Run1.
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_compute_diff($xhprof_data1, $xhprof_data2) {
|
||||
global $display_calls;
|
||||
|
||||
// use the second run to decide what metrics we will do the diff on
|
||||
$metrics = xhprof_get_metrics($xhprof_data2);
|
||||
|
||||
$xhprof_delta = $xhprof_data2;
|
||||
|
||||
foreach ($xhprof_data1 as $parent_child => $info) {
|
||||
|
||||
if (!isset($xhprof_delta[$parent_child])) {
|
||||
|
||||
// this pc combination was not present in run1;
|
||||
// initialize all values to zero.
|
||||
if ($display_calls) {
|
||||
$xhprof_delta[$parent_child] = array("ct" => 0);
|
||||
} else {
|
||||
$xhprof_delta[$parent_child] = array();
|
||||
}
|
||||
foreach ($metrics as $metric) {
|
||||
$xhprof_delta[$parent_child][$metric] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ($display_calls) {
|
||||
$xhprof_delta[$parent_child]["ct"] -= $info["ct"];
|
||||
}
|
||||
|
||||
foreach ($metrics as $metric) {
|
||||
$xhprof_delta[$parent_child][$metric] -= $info[$metric];
|
||||
}
|
||||
}
|
||||
|
||||
return $xhprof_delta;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute inclusive metrics for function. This code was factored out
|
||||
* of xhprof_compute_flat_info().
|
||||
*
|
||||
* The raw data contains inclusive metrics of a function for each
|
||||
* unique parent function it is called from. The total inclusive metrics
|
||||
* for a function is therefore the sum of inclusive metrics for the
|
||||
* function across all parents.
|
||||
*
|
||||
* @return array Returns a map of function name to total (across all parents)
|
||||
* inclusive metrics for the function.
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_compute_inclusive_times($raw_data) {
|
||||
global $display_calls;
|
||||
|
||||
$metrics = xhprof_get_metrics($raw_data);
|
||||
|
||||
$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 ($raw_data as $parent_child => $info) {
|
||||
|
||||
list($parent, $child) = xhprof_parse_parent_child($parent_child);
|
||||
|
||||
if ($parent == $child) {
|
||||
/*
|
||||
* XHProf PHP extension should never trigger this situation any more.
|
||||
* Recursion is handled in the XHProf PHP extension by giving nested
|
||||
* calls a unique recursion-depth appended name (for example, foo@1).
|
||||
*/
|
||||
xhprof_error("Error in Raw Data: parent & child are both: $parent");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($symbol_tab[$child])) {
|
||||
|
||||
if ($display_calls) {
|
||||
$symbol_tab[$child] = array("ct" => $info["ct"]);
|
||||
} else {
|
||||
$symbol_tab[$child] = array();
|
||||
}
|
||||
foreach ($metrics as $metric) {
|
||||
$symbol_tab[$child][$metric] = $info[$metric];
|
||||
}
|
||||
} else {
|
||||
if ($display_calls) {
|
||||
/* increment call count for this child */
|
||||
$symbol_tab[$child]["ct"] += $info["ct"];
|
||||
}
|
||||
|
||||
/* update inclusive times/metric for this child */
|
||||
foreach ($metrics as $metric) {
|
||||
$symbol_tab[$child][$metric] += $info[$metric];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $symbol_tab;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Prunes XHProf raw data:
|
||||
*
|
||||
* Any node whose inclusive walltime accounts for less than $prune_percent
|
||||
* of total walltime is pruned. [It is possible that a child function isn't
|
||||
* pruned, but one or more of its parents get pruned. In such cases, when
|
||||
* viewing the child function's hierarchical information, the cost due to
|
||||
* the pruned parent(s) will be attributed to a special function/symbol
|
||||
* "__pruned__()".]
|
||||
*
|
||||
* @param array $raw_data XHProf raw data to be pruned & validated.
|
||||
* @param double $prune_percent Any edges that account for less than
|
||||
* $prune_percent of time will be pruned
|
||||
* from the raw data.
|
||||
*
|
||||
* @return array Returns the pruned raw data.
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_prune_run($raw_data, $prune_percent) {
|
||||
|
||||
$main_info = $raw_data["main()"];
|
||||
if (empty($main_info)) {
|
||||
xhprof_error("XHProf: main() missing in raw data");
|
||||
return false;
|
||||
}
|
||||
|
||||
// raw data should contain either wall time or samples information...
|
||||
if (isset($main_info["wt"])) {
|
||||
$prune_metric = "wt";
|
||||
} else if (isset($main_info["samples"])) {
|
||||
$prune_metric = "samples";
|
||||
} else {
|
||||
xhprof_error("XHProf: for main() we must have either wt "
|
||||
."or samples attribute set");
|
||||
return false;
|
||||
}
|
||||
|
||||
// determine the metrics present in the raw data..
|
||||
$metrics = array();
|
||||
foreach ($main_info as $metric => $val) {
|
||||
if (isset($val)) {
|
||||
$metrics[] = $metric;
|
||||
}
|
||||
}
|
||||
|
||||
$prune_threshold = (($main_info[$prune_metric] * $prune_percent) / 100.0);
|
||||
|
||||
init_metrics($raw_data, null, null, false);
|
||||
$flat_info = xhprof_compute_inclusive_times($raw_data);
|
||||
|
||||
foreach ($raw_data as $parent_child => $info) {
|
||||
|
||||
list($parent, $child) = xhprof_parse_parent_child($parent_child);
|
||||
|
||||
// is this child's overall total from all parents less than threshold?
|
||||
if ($flat_info[$child][$prune_metric] < $prune_threshold) {
|
||||
unset($raw_data[$parent_child]); // prune the edge
|
||||
} else if ($parent &&
|
||||
($parent != "__pruned__()") &&
|
||||
($flat_info[$parent][$prune_metric] < $prune_threshold)) {
|
||||
|
||||
// Parent's overall inclusive metric is less than a threshold.
|
||||
// All edges to the parent node will get nuked, and this child will
|
||||
// be a dangling child.
|
||||
// So instead change its parent to be a special function __pruned__().
|
||||
$pruned_edge = xhprof_build_parent_child_key("__pruned__()", $child);
|
||||
|
||||
if (isset($raw_data[$pruned_edge])) {
|
||||
foreach ($metrics as $metric) {
|
||||
$raw_data[$pruned_edge][$metric]+=$raw_data[$parent_child][$metric];
|
||||
}
|
||||
} else {
|
||||
$raw_data[$pruned_edge] = $raw_data[$parent_child];
|
||||
}
|
||||
|
||||
unset($raw_data[$parent_child]); // prune the edge
|
||||
}
|
||||
}
|
||||
|
||||
return $raw_data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set one key in an array and return the array
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_array_set($arr, $k, $v) {
|
||||
$arr[$k] = $v;
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes/unsets one key in an array and return the array
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_array_unset($arr, $k) {
|
||||
unset($arr[$k]);
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type definitions for URL params
|
||||
*/
|
||||
define('XHPROF_STRING_PARAM', 1);
|
||||
define('XHPROF_UINT_PARAM', 2);
|
||||
define('XHPROF_FLOAT_PARAM', 3);
|
||||
define('XHPROF_BOOL_PARAM', 4);
|
||||
|
||||
|
||||
/**
|
||||
* Internal helper function used by various
|
||||
* xhprof_get_param* flavors for various
|
||||
* types of parameters.
|
||||
*
|
||||
* @param string name of the URL query string param
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_get_param_helper($param) {
|
||||
$val = null;
|
||||
if (isset($_GET[$param]))
|
||||
$val = $_GET[$param];
|
||||
else if (isset($_POST[$param])) {
|
||||
$val = $_POST[$param];
|
||||
}
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts value for string param $param from query
|
||||
* string. If param is not specified, return the
|
||||
* $default value.
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_get_string_param($param, $default = '') {
|
||||
$val = xhprof_get_param_helper($param);
|
||||
|
||||
if ($val === null)
|
||||
return $default;
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts value for unsigned integer param $param from
|
||||
* query string. If param is not specified, return the
|
||||
* $default value.
|
||||
*
|
||||
* If value is not a valid unsigned integer, logs error
|
||||
* and returns null.
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_get_uint_param($param, $default = 0) {
|
||||
$val = xhprof_get_param_helper($param);
|
||||
|
||||
if ($val === null)
|
||||
$val = $default;
|
||||
|
||||
// trim leading/trailing whitespace
|
||||
$val = trim($val);
|
||||
|
||||
// if it only contains digits, then ok..
|
||||
if (ctype_digit($val)) {
|
||||
return $val;
|
||||
}
|
||||
|
||||
xhprof_error("$param is $val. It must be an unsigned integer.");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extracts value for a float param $param from
|
||||
* query string. If param is not specified, return
|
||||
* the $default value.
|
||||
*
|
||||
* If value is not a valid unsigned integer, logs error
|
||||
* and returns null.
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_get_float_param($param, $default = 0) {
|
||||
$val = xhprof_get_param_helper($param);
|
||||
|
||||
if ($val === null)
|
||||
$val = $default;
|
||||
|
||||
// trim leading/trailing whitespace
|
||||
$val = trim($val);
|
||||
|
||||
// TBD: confirm the value is indeed a float.
|
||||
if (true) // for now..
|
||||
return (float)$val;
|
||||
|
||||
xhprof_error("$param is $val. It must be a float.");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts value for a boolean param $param from
|
||||
* query string. If param is not specified, return
|
||||
* the $default value.
|
||||
*
|
||||
* If value is not a valid unsigned integer, logs error
|
||||
* and returns null.
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_get_bool_param($param, $default = false) {
|
||||
$val = xhprof_get_param_helper($param);
|
||||
|
||||
if ($val === null)
|
||||
$val = $default;
|
||||
|
||||
// trim leading/trailing whitespace
|
||||
$val = trim($val);
|
||||
|
||||
switch (strtolower($val)) {
|
||||
case '0':
|
||||
case '1':
|
||||
$val = (bool)$val;
|
||||
break;
|
||||
case 'true':
|
||||
case 'on':
|
||||
case 'yes':
|
||||
$val = true;
|
||||
break;
|
||||
case 'false':
|
||||
case 'off':
|
||||
case 'no':
|
||||
$val = false;
|
||||
break;
|
||||
default:
|
||||
xhprof_error("$param is $val. It must be a valid boolean string.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return $val;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize params from URL query string. The function
|
||||
* creates globals variables for each of the params
|
||||
* and if the URL query string doesn't specify a particular
|
||||
* param initializes them with the corresponding default
|
||||
* value specified in the input.
|
||||
*
|
||||
* @params array $params An array whose keys are the names
|
||||
* of URL params who value needs to
|
||||
* be retrieved from the URL query
|
||||
* string. PHP globals are created
|
||||
* with these names. The value is
|
||||
* itself an array with 2-elems (the
|
||||
* param type, and its default value).
|
||||
* If a param is not specified in the
|
||||
* query string the default value is
|
||||
* used.
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_param_init($params) {
|
||||
/* Create variables specified in $params keys, init defaults */
|
||||
foreach ($params as $k => $v) {
|
||||
switch ($v[0]) {
|
||||
case XHPROF_STRING_PARAM:
|
||||
$p = xhprof_get_string_param($k, $v[1]);
|
||||
break;
|
||||
case XHPROF_UINT_PARAM:
|
||||
$p = xhprof_get_uint_param($k, $v[1]);
|
||||
break;
|
||||
case XHPROF_FLOAT_PARAM:
|
||||
$p = xhprof_get_float_param($k, $v[1]);
|
||||
break;
|
||||
case XHPROF_BOOL_PARAM:
|
||||
$p = xhprof_get_bool_param($k, $v[1]);
|
||||
break;
|
||||
default:
|
||||
xhprof_error("Invalid param type passed to xhprof_param_init: "
|
||||
. $v[0]);
|
||||
exit();
|
||||
}
|
||||
|
||||
// create a global variable using the parameter name.
|
||||
$GLOBALS[$k] = $p;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given a partial query string $q return matching function names in
|
||||
* specified XHProf run. This is used for the type ahead function
|
||||
* selector.
|
||||
*
|
||||
* @author Kannan
|
||||
*/
|
||||
function xhprof_get_matching_functions($q, $xhprof_data) {
|
||||
|
||||
$matches = array();
|
||||
|
||||
foreach ($xhprof_data as $parent_child => $info) {
|
||||
list($parent, $child) = xhprof_parse_parent_child($parent_child);
|
||||
if (stripos($parent, $q) !== false) {
|
||||
$matches[$parent] = 1;
|
||||
}
|
||||
if (stripos($child, $q) !== false) {
|
||||
$matches[$child] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$res = array_keys($matches);
|
||||
|
||||
// sort it so the answers are in some reliable order...
|
||||
asort($res);
|
||||
|
||||
return ($res);
|
||||
}
|
||||
|
|
@ -7,6 +7,15 @@
|
|||
*/
|
||||
|
||||
celerity_register_resource_map(array(
|
||||
'aphront-dark-console-css' =>
|
||||
array(
|
||||
'uri' => '/res/ac3fc983/rsrc/css/aphront/dark-console.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
),
|
||||
'disk' => '/rsrc/css/aphront/dark-console.css',
|
||||
),
|
||||
'aphront-dialog-view-css' =>
|
||||
array(
|
||||
'uri' => '/res/a05107ae/rsrc/css/aphront/dialog-view.css',
|
||||
|
@ -54,7 +63,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'aphront-table-view-css' =>
|
||||
array(
|
||||
'uri' => '/res/52b0191f/rsrc/css/aphront/table-view.css',
|
||||
'uri' => '/res/de3a1e4c/rsrc/css/aphront/table-view.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -215,6 +224,15 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/css/core/syntax.css',
|
||||
),
|
||||
'javelin-behavior-dark-console' =>
|
||||
array(
|
||||
'uri' => '/res/453503f4/rsrc/js/application/core/behavior-dark-console.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
),
|
||||
'disk' => '/rsrc/js/application/core/behavior-dark-console.js',
|
||||
),
|
||||
'javelin-behavior-aphront-basic-tokenizer' =>
|
||||
array(
|
||||
'uri' => '/res/8317d761/rsrc/js/application/core/behavior-tokenizer.js',
|
||||
|
@ -237,7 +255,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'javelin-behavior-differential-edit-inline-comments' =>
|
||||
array(
|
||||
'uri' => '/res/be5ed33e/rsrc/js/application/differential/behavior-edit-inline-comments.js',
|
||||
'uri' => '/res/f5b54891/rsrc/js/application/differential/behavior-edit-inline-comments.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -340,7 +358,7 @@ celerity_register_resource_map(array(
|
|||
), array (
|
||||
'packages' =>
|
||||
array (
|
||||
'4bb7e37f' =>
|
||||
'c5efa388' =>
|
||||
array (
|
||||
'name' => 'core.pkg.css',
|
||||
'symbols' =>
|
||||
|
@ -357,7 +375,7 @@ celerity_register_resource_map(array(
|
|||
9 => 'aphront-typeahead-control-css',
|
||||
10 => 'phabricator-directory-css',
|
||||
),
|
||||
'uri' => '/res/pkg/4bb7e37f/core.pkg.css',
|
||||
'uri' => '/res/pkg/c5efa388/core.pkg.css',
|
||||
'type' => 'css',
|
||||
),
|
||||
'f399aad7' =>
|
||||
|
@ -377,17 +395,17 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'reverse' =>
|
||||
array (
|
||||
'phabricator-core-css' => '4bb7e37f',
|
||||
'phabricator-core-buttons-css' => '4bb7e37f',
|
||||
'phabricator-standard-page-view' => '4bb7e37f',
|
||||
'aphront-dialog-view-css' => '4bb7e37f',
|
||||
'aphront-form-view-css' => '4bb7e37f',
|
||||
'aphront-panel-view-css' => '4bb7e37f',
|
||||
'aphront-side-nav-view-css' => '4bb7e37f',
|
||||
'aphront-table-view-css' => '4bb7e37f',
|
||||
'aphront-tokenizer-control-css' => '4bb7e37f',
|
||||
'aphront-typeahead-control-css' => '4bb7e37f',
|
||||
'phabricator-directory-css' => '4bb7e37f',
|
||||
'phabricator-core-css' => 'c5efa388',
|
||||
'phabricator-core-buttons-css' => 'c5efa388',
|
||||
'phabricator-standard-page-view' => 'c5efa388',
|
||||
'aphront-dialog-view-css' => 'c5efa388',
|
||||
'aphront-form-view-css' => 'c5efa388',
|
||||
'aphront-panel-view-css' => 'c5efa388',
|
||||
'aphront-side-nav-view-css' => 'c5efa388',
|
||||
'aphront-table-view-css' => 'c5efa388',
|
||||
'aphront-tokenizer-control-css' => 'c5efa388',
|
||||
'aphront-typeahead-control-css' => 'c5efa388',
|
||||
'phabricator-directory-css' => 'c5efa388',
|
||||
'differential-core-view-css' => 'f399aad7',
|
||||
'differential-changeset-view-css' => 'f399aad7',
|
||||
'differential-revision-detail-css' => 'f399aad7',
|
||||
|
|
|
@ -68,6 +68,15 @@ phutil_register_library_map(array(
|
|||
'ConduitAPI_file_upload_Method' => 'applications/conduit/method/file/upload',
|
||||
'ConduitAPI_user_find_Method' => 'applications/conduit/method/user/find',
|
||||
'ConduitException' => 'applications/conduit/protocol/exception',
|
||||
'DarkConsole' => 'aphront/console/api',
|
||||
'DarkConsoleController' => 'aphront/console/controller',
|
||||
'DarkConsoleCore' => 'aphront/console/core',
|
||||
'DarkConsoleErrorLogPlugin' => 'aphront/console/plugin/errorlog',
|
||||
'DarkConsolePlugin' => 'aphront/console/plugin/base',
|
||||
'DarkConsoleRequestPlugin' => 'aphront/console/plugin/request',
|
||||
'DarkConsoleServicesPlugin' => 'aphront/console/plugin/services',
|
||||
'DarkConsoleXHProfPlugin' => 'aphront/console/plugin/xhprof',
|
||||
'DarkConsoleXHProfPluginAPI' => 'aphront/console/plugin/xhprof/api',
|
||||
'DifferentialAction' => 'applications/differential/constants/action',
|
||||
'DifferentialAddCommentView' => 'applications/differential/view/addcomment',
|
||||
'DifferentialCCWelcomeMail' => 'applications/differential/mail/ccwelcome',
|
||||
|
@ -181,6 +190,10 @@ phutil_register_library_map(array(
|
|||
'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/base',
|
||||
'PhabricatorUser' => 'applications/people/storage/user',
|
||||
'PhabricatorUserDAO' => 'applications/people/storage/base',
|
||||
'PhabricatorXHProfController' => 'applications/xhprof/controller/base',
|
||||
'PhabricatorXHProfProfileController' => 'applications/xhprof/controller/profile',
|
||||
'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/symbol',
|
||||
'PhabricatorXHProfProfileTopLevelView' => 'applications/xhprof/view/toplevel',
|
||||
),
|
||||
'function' =>
|
||||
array(
|
||||
|
@ -249,6 +262,11 @@ phutil_register_library_map(array(
|
|||
'ConduitAPI_differential_setdiffproperty_Method' => 'ConduitAPIMethod',
|
||||
'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod',
|
||||
'ConduitAPI_user_find_Method' => 'ConduitAPIMethod',
|
||||
'DarkConsoleController' => 'AliteController',
|
||||
'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin',
|
||||
'DarkConsoleRequestPlugin' => 'DarkConsolePlugin',
|
||||
'DarkConsoleServicesPlugin' => 'DarkConsolePlugin',
|
||||
'DarkConsoleXHProfPlugin' => 'DarkConsolePlugin',
|
||||
'DifferentialAddCommentView' => 'AphrontView',
|
||||
'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail',
|
||||
'DifferentialChangeset' => 'DifferentialDAO',
|
||||
|
@ -343,6 +361,10 @@ phutil_register_library_map(array(
|
|||
'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController',
|
||||
'PhabricatorUser' => 'PhabricatorUserDAO',
|
||||
'PhabricatorUserDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorXHProfController' => 'PhabricatorController',
|
||||
'PhabricatorXHProfProfileController' => 'PhabricatorXHProfController',
|
||||
'PhabricatorXHProfProfileSymbolView' => 'AphrontView',
|
||||
'PhabricatorXHProfProfileTopLevelView' => 'AphrontView',
|
||||
),
|
||||
'requires_interface' =>
|
||||
array(
|
||||
|
|
|
@ -24,6 +24,7 @@ abstract class AphrontApplicationConfiguration {
|
|||
private $request;
|
||||
private $host;
|
||||
private $path;
|
||||
private $console;
|
||||
|
||||
abstract public function getApplicationName();
|
||||
abstract public function getURIMap();
|
||||
|
@ -39,6 +40,10 @@ abstract class AphrontApplicationConfiguration {
|
|||
return $this->request;
|
||||
}
|
||||
|
||||
final public function getConsole() {
|
||||
return $this->console;
|
||||
}
|
||||
|
||||
final public function buildController() {
|
||||
$map = $this->getURIMap();
|
||||
$mapper = new AphrontURIMapper($map);
|
||||
|
@ -74,4 +79,8 @@ abstract class AphrontApplicationConfiguration {
|
|||
return $this->path;
|
||||
}
|
||||
|
||||
final public function willBuildRequest() {
|
||||
$this->console = new DarkConsoleCore();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'aphront/console/core');
|
||||
phutil_require_module('phabricator', 'aphront/mapper');
|
||||
|
||||
phutil_require_module('phutil', 'symbols');
|
||||
|
|
21
src/aphront/console/api/DarkConsole.php
Executable file
21
src/aphront/console/api/DarkConsole.php
Executable file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class DarkConsole {
|
||||
|
||||
}
|
10
src/aphront/console/api/__init__.php
Normal file
10
src/aphront/console/api/__init__.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
phutil_require_source('DarkConsole.php');
|
100
src/aphront/console/controller/DarkConsoleController.php
Executable file
100
src/aphront/console/controller/DarkConsoleController.php
Executable file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class DarkConsoleController extends AliteController {
|
||||
|
||||
protected $op;
|
||||
protected $data;
|
||||
|
||||
public function __construct(AliteRequest $request, array $params) {
|
||||
parent::__construct($request);
|
||||
|
||||
validate_parameter_list(
|
||||
$params,
|
||||
array(
|
||||
),
|
||||
$ops = array(
|
||||
'tab' => true,
|
||||
'toggle' => true,
|
||||
'visible' => true,
|
||||
'plugin' => true,
|
||||
|
||||
'etc' => true,
|
||||
));
|
||||
|
||||
foreach (array_keys($ops) as $op) {
|
||||
if (isset($params[$op])) {
|
||||
$this->op = $op;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->data = $params;
|
||||
}
|
||||
|
||||
public function getShortControllerName() {
|
||||
return 'DarkConsole';
|
||||
}
|
||||
|
||||
public function shouldPreflush() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function process() {
|
||||
$request = $this->getRequest();
|
||||
|
||||
if (!$this->op) {
|
||||
$this->op = 'toggle';
|
||||
}
|
||||
|
||||
$coredata = $request->getCoreData();
|
||||
$console = $coredata->getConsole();
|
||||
|
||||
if ($request->isAsync()) {
|
||||
$return = null;
|
||||
} else {
|
||||
$return = '/';
|
||||
}
|
||||
|
||||
|
||||
switch ($this->op) {
|
||||
case 'toggle':
|
||||
$enabled = $coredata->didToggleDarkConsole();
|
||||
if ($enabled) {
|
||||
if (!$console) {
|
||||
$console = new DarkConsoleCore($coredata);
|
||||
}
|
||||
$console->setConsoleSetting(
|
||||
DarkConsoleCore::SETTING_VISIBLE,
|
||||
true);
|
||||
}
|
||||
break;
|
||||
case 'tab':
|
||||
$console->setConsoleSetting(
|
||||
DarkConsoleCore::SETTING_TAB,
|
||||
$request->getStr('tab'));
|
||||
break;
|
||||
case 'visible':
|
||||
$console->setConsoleSetting(
|
||||
DarkConsoleCore::SETTING_VISIBLE,
|
||||
!$console->getConsoleSetting(DarkConsoleCore::SETTING_VISIBLE));
|
||||
break;
|
||||
}
|
||||
// return <alite:redirect uri={$return} />;
|
||||
}
|
||||
|
||||
}
|
12
src/aphront/console/controller/__init__.php
Normal file
12
src/aphront/console/controller/__init__.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'aphront/console/core');
|
||||
|
||||
|
||||
phutil_require_source('DarkConsoleController.php');
|
186
src/aphront/console/core/DarkConsoleCore.php
Executable file
186
src/aphront/console/core/DarkConsoleCore.php
Executable file
|
@ -0,0 +1,186 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class DarkConsoleCore {
|
||||
|
||||
const PLUGIN_ERRORLOG = 'ErrorLog';
|
||||
const PLUGIN_SERVICES = 'Services';
|
||||
const PLUGIN_XHPROF = 'XHProf';
|
||||
const PLUGIN_REQUEST = 'Request';
|
||||
|
||||
public static function getPlugins() {
|
||||
return array(
|
||||
self::PLUGIN_ERRORLOG,
|
||||
self::PLUGIN_REQUEST,
|
||||
self::PLUGIN_SERVICES,
|
||||
self::PLUGIN_XHPROF,
|
||||
);
|
||||
}
|
||||
|
||||
private $plugins = array();
|
||||
private $settings;
|
||||
private $coredata;
|
||||
|
||||
public function setConsoleSetting($key, $value) {
|
||||
/*
|
||||
$guard = new WriteOnHttpGet();
|
||||
$okay = user_set_pref(
|
||||
$this->getCoreData()->getViewerContext()->getUserID(),
|
||||
self::APPLICATION_ID,
|
||||
$key,
|
||||
$value);
|
||||
$guard->release();
|
||||
if (!$okay) {
|
||||
throw new Exception('Failed to set preference setting.');
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public function getConsoleSetting($key) {
|
||||
// $viewer_id = $this->getCoreData()->getViewerContext()->getUserID();
|
||||
// return idx(idx($this->settings[$viewer_id], $key), 'value');
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getPlugin($plugin_name) {
|
||||
return idx($this->plugins, $plugin_name);
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
|
||||
/*
|
||||
$this->settings = users_multiget_prefs_info(
|
||||
array($coredata->getViewerContext()->getUserID()),
|
||||
self::APPLICATION_ID);
|
||||
|
||||
$disabled = $this->getConsoleSetting(self::SETTING_PLUGINS);
|
||||
$disabled = array_flip(explode(',', $disabled));
|
||||
*/
|
||||
foreach (self::getPlugins() as $plugin_name) {
|
||||
$plugin = self::newPlugin($plugin_name);
|
||||
if ($plugin->isPermanent() || !isset($disabled[$plugin_name])) {
|
||||
if ($plugin->shouldStartup()) {
|
||||
$plugin->didStartup();
|
||||
$plugin->setConsoleCore($this);
|
||||
$this->plugins[$plugin_name] = $plugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function newPlugin($plugin) {
|
||||
$class = 'DarkConsole'.$plugin.'Plugin';
|
||||
PhutilSymbolLoader::loadClass($class);
|
||||
return newv($class, array());
|
||||
}
|
||||
|
||||
public function getEnabledPlugins() {
|
||||
return $this->plugins;
|
||||
}
|
||||
|
||||
public function render(AphrontRequest $request) {
|
||||
|
||||
$plugins = $this->getEnabledPlugins();
|
||||
|
||||
foreach ($plugins as $plugin) {
|
||||
$plugin->setRequest($request);
|
||||
$plugin->willShutdown();
|
||||
}
|
||||
|
||||
foreach ($plugins as $plugin) {
|
||||
$plugin->didShutdown();
|
||||
}
|
||||
|
||||
foreach ($plugins as $plugin) {
|
||||
$plugin->setData($plugin->generateData());
|
||||
}
|
||||
|
||||
$selected = 'XHProf';//true;//$this->getConsoleSetting(DarkConsoleCore::SETTING_TAB);
|
||||
$visible = true;//$this->getConsoleSetting(DarkConsoleCore::SETTING_VISIBLE);
|
||||
|
||||
if (!isset($plugins[$selected])) {
|
||||
$selected = key($plugins);
|
||||
}
|
||||
|
||||
$tabs = array();
|
||||
foreach ($plugins as $key => $plugin) {
|
||||
$tabs[$key] = array(
|
||||
'name' => $plugin->getName(),
|
||||
'panel' => $plugin->render(),
|
||||
);
|
||||
}
|
||||
|
||||
$tabs_markup = array();
|
||||
$panel_markup = array();
|
||||
foreach ($tabs as $key => $data) {
|
||||
$is_selected = ($key == $selected);
|
||||
if ($is_selected) {
|
||||
$style = null;
|
||||
$tabclass = 'dark-console-tab-selected';
|
||||
} else {
|
||||
$style = 'display: none;';
|
||||
$tabclass = null;
|
||||
}
|
||||
|
||||
$tabs_markup[] = javelin_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'class' => "dark-console-tab {$tabclass}",
|
||||
'sigil' => 'dark-console-tab',
|
||||
'meta' => array(
|
||||
'key' => $key,
|
||||
),
|
||||
),
|
||||
(string)$data['name']);
|
||||
|
||||
$panel_markup[] = javelin_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'dark-console-panel',
|
||||
'style' => $style,
|
||||
'sigil' => 'dark-console-panel',
|
||||
'meta' => array(
|
||||
'key' => $key,
|
||||
),
|
||||
),
|
||||
(string)$data['panel']);
|
||||
}
|
||||
|
||||
$console = javelin_render_tag(
|
||||
'table',
|
||||
array(
|
||||
'class' => 'dark-console',
|
||||
'sigil' => 'dark-console',
|
||||
'meta' => array(
|
||||
'visible' => true,
|
||||
),
|
||||
'style' => '',
|
||||
),
|
||||
'<tr>'.
|
||||
'<th class="dark-console-tabs">'.
|
||||
implode("\n", $tabs_markup).
|
||||
'</th>'.
|
||||
'<td>'.implode("\n", $panel_markup).'</td>'.
|
||||
'</tr>');
|
||||
|
||||
Javelin::initBehavior('dark-console');
|
||||
|
||||
return "\n\n\n\n".$console."\n\n\n\n";
|
||||
}
|
||||
}
|
||||
|
16
src/aphront/console/core/__init__.php
Normal file
16
src/aphront/console/core/__init__.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||
|
||||
phutil_require_module('phutil', 'symbols');
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
||||
phutil_require_source('DarkConsoleCore.php');
|
88
src/aphront/console/plugin/base/DarkConsolePlugin.php
Executable file
88
src/aphront/console/plugin/base/DarkConsolePlugin.php
Executable file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
abstract class DarkConsolePlugin {
|
||||
|
||||
private $data;
|
||||
private $request;
|
||||
private $core;
|
||||
|
||||
abstract public function getName();
|
||||
abstract public function getDescription();
|
||||
abstract public function render();
|
||||
|
||||
public function __construct() {
|
||||
|
||||
}
|
||||
|
||||
public function setConsoleCore(DarkConsoleCore $core) {
|
||||
$this->core = $core;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getConsoleCore() {
|
||||
return $this->core;
|
||||
}
|
||||
|
||||
public function generateData() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function setData($data) {
|
||||
$this->data = $data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function setRequest($request) {
|
||||
$this->request = $request;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRequest() {
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
public function isPermanent() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function shouldStartup() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function didStartup() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function willShutdown() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function didShutdown() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
10
src/aphront/console/plugin/base/__init__.php
Normal file
10
src/aphront/console/plugin/base/__init__.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
phutil_require_source('DarkConsolePlugin.php');
|
129
src/aphront/console/plugin/errorlog/DarkConsoleErrorLogPlugin.php
Executable file
129
src/aphront/console/plugin/errorlog/DarkConsoleErrorLogPlugin.php
Executable file
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class DarkConsoleErrorLogPlugin extends DarkConsolePlugin {
|
||||
|
||||
public function getName() {
|
||||
$count = count($this->getData());
|
||||
|
||||
/*
|
||||
if ($count) {
|
||||
return
|
||||
<x:frag>
|
||||
<span style="color: #ff0000;">•</span> Error Log ({$count})
|
||||
</x:frag>;
|
||||
}
|
||||
|
||||
*/
|
||||
return 'Error Log';
|
||||
}
|
||||
|
||||
|
||||
public function getDescription() {
|
||||
return 'Shows errors and warnings.';
|
||||
}
|
||||
|
||||
|
||||
public function generateData() {
|
||||
/*
|
||||
$stub = tabconsole();
|
||||
if (!$stub) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$errors = $stub->getErrors();
|
||||
|
||||
$data = array();
|
||||
foreach ($errors as $error) {
|
||||
if (is_array($error)) {
|
||||
list($err, $trace) = $error;
|
||||
$trace = implode("\n", $trace);
|
||||
} else {
|
||||
$err = $error->getMessage();
|
||||
$trace = $error->getTraceAsString();
|
||||
}
|
||||
$data[] = array(
|
||||
'error' => $err,
|
||||
'trace' => $trace,
|
||||
);
|
||||
}
|
||||
return $data;
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
public function render() {
|
||||
|
||||
return '!!';
|
||||
/*
|
||||
$data = $this->getData();
|
||||
if (!$data) {
|
||||
return
|
||||
<x:frag>
|
||||
<div class="mu">No errors.</div>
|
||||
</x:frag>;
|
||||
}
|
||||
|
||||
$markup = <table class="LConsoleErrors" />;
|
||||
$alt = false;
|
||||
foreach ($data as $error) {
|
||||
$row = <tr class={$alt ? 'alt' : null} />;
|
||||
|
||||
$text = $error['error'];
|
||||
$text = preg_replace('/\(in .* on line \d+\)$/', '', trim($text));
|
||||
|
||||
$trace = $error['trace'];
|
||||
$trace = explode("\n", $trace);
|
||||
if (!$trace) {
|
||||
$trace = array('unknown@0@unknown');
|
||||
}
|
||||
|
||||
foreach ($trace as $idx => $traceline) {
|
||||
list($file, $line, $where) = array_merge(
|
||||
explode('@', $traceline),
|
||||
array('?', '?', '?'));
|
||||
if ($where == 'DarkConsole->addError' ||
|
||||
$where == 'debug_rlog') {
|
||||
unset($trace[$idx]);
|
||||
}
|
||||
}
|
||||
|
||||
$row->appendChild(<th rowspan={count($trace)}>{$text}</th>);
|
||||
|
||||
foreach ($trace as $traceline) {
|
||||
list($file, $line, $where) = array_merge(
|
||||
explode('@', $traceline),
|
||||
array('?', '?', '?'));
|
||||
$row->appendChild(<td>{$file}:{$line}</td>);
|
||||
$row->appendChild(<td>{$where}()</td>);
|
||||
$markup->appendChild($row);
|
||||
$row = <tr class={$alt ? 'alt' : null} />;
|
||||
}
|
||||
|
||||
$alt = !$alt;
|
||||
}
|
||||
|
||||
return
|
||||
<x:frag>
|
||||
<h1>Errors</h1>
|
||||
<div class="LConsoleErrors">{$markup}</div>
|
||||
</x:frag>;
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
12
src/aphront/console/plugin/errorlog/__init__.php
Normal file
12
src/aphront/console/plugin/errorlog/__init__.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'aphront/console/plugin/base');
|
||||
|
||||
|
||||
phutil_require_source('DarkConsoleErrorLogPlugin.php');
|
72
src/aphront/console/plugin/request/DarkConsoleRequestPlugin.php
Executable file
72
src/aphront/console/plugin/request/DarkConsoleRequestPlugin.php
Executable file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class DarkConsoleRequestPlugin extends DarkConsolePlugin {
|
||||
|
||||
public function getName() {
|
||||
return 'Request';
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return 'Information about $_REQUEST and $_SERVER.';
|
||||
}
|
||||
|
||||
public function generateData() {
|
||||
return array(
|
||||
'Request' => $_REQUEST,
|
||||
'Server' => $_SERVER,
|
||||
);
|
||||
}
|
||||
|
||||
public function render() {
|
||||
|
||||
$data = $this->getData();
|
||||
|
||||
$sections = array(
|
||||
'Basics' => array(
|
||||
'Host' => $data['Server']['SERVER_ADDR'],
|
||||
'Hostname' => gethostbyaddr($data['Server']['SERVER_ADDR']),
|
||||
),
|
||||
);
|
||||
|
||||
$sections = array_merge($sections, $data);
|
||||
|
||||
/*
|
||||
$out = <x:frag />;
|
||||
foreach ($sections as $header => $map) {
|
||||
$list = <table class="LConsoleRequestDict" />;
|
||||
foreach ($map as $key => $value) {
|
||||
if (!is_scalar($value)) {
|
||||
$value = fb_json_encode($value);
|
||||
}
|
||||
$value = <text wrap="80">{$value}</text>;
|
||||
$list->appendChild(
|
||||
<tr><th>{$key}</th><td>{$value}</td></tr>);
|
||||
}
|
||||
$out->appendChild(
|
||||
<x:frag>
|
||||
<h1>{$header}</h1>
|
||||
{$list}
|
||||
</x:frag>);
|
||||
}
|
||||
|
||||
return $out;
|
||||
*/
|
||||
return "REQUEST";
|
||||
}
|
||||
}
|
12
src/aphront/console/plugin/request/__init__.php
Normal file
12
src/aphront/console/plugin/request/__init__.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'aphront/console/plugin/base');
|
||||
|
||||
|
||||
phutil_require_source('DarkConsoleRequestPlugin.php');
|
39
src/aphront/console/plugin/services/DarkConsoleServicesPlugin.php
Executable file
39
src/aphront/console/plugin/services/DarkConsoleServicesPlugin.php
Executable file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class DarkConsoleServicesPlugin extends DarkConsolePlugin {
|
||||
|
||||
protected $observations;
|
||||
|
||||
public function getName() {
|
||||
return 'Services';
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return 'Information about services.';
|
||||
}
|
||||
|
||||
public function willShutdown() {
|
||||
// $this->observations = cacheobserver();
|
||||
}
|
||||
|
||||
public function render() {
|
||||
return '!';
|
||||
}
|
||||
}
|
||||
|
12
src/aphront/console/plugin/services/__init__.php
Normal file
12
src/aphront/console/plugin/services/__init__.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'aphront/console/plugin/base');
|
||||
|
||||
|
||||
phutil_require_source('DarkConsoleServicesPlugin.php');
|
112
src/aphront/console/plugin/xhprof/DarkConsoleXHProfPlugin.php
Executable file
112
src/aphront/console/plugin/xhprof/DarkConsoleXHProfPlugin.php
Executable file
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class DarkConsoleXHProfPlugin extends DarkConsolePlugin {
|
||||
|
||||
protected $xhprofID;
|
||||
protected $xhprofData;
|
||||
|
||||
public function getName() {
|
||||
$run = $this->getData();
|
||||
|
||||
if ($run) {
|
||||
return '<span style="color: #ff00ff;">•</span> XHProf';
|
||||
}
|
||||
|
||||
return 'XHProf';
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return 'Provides detailed PHP profiling information through XHProf.';
|
||||
}
|
||||
|
||||
public function generateData() {
|
||||
return $this->xhprofID;
|
||||
}
|
||||
|
||||
public function getXHProfRunID() {
|
||||
return $this->xhprofID;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
if (!DarkConsoleXHProfPluginAPI::isProfilerAvailable()) {
|
||||
return
|
||||
'<p>The "xhprof" PHP extension is not available. Install xhprof '.
|
||||
'to enable the XHProf plugin.';
|
||||
}
|
||||
|
||||
return '...';
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
|
||||
public function render() {
|
||||
$run = $this->getData();
|
||||
|
||||
if ($run) {
|
||||
$uri = 'http://www.intern.facebook.com/intern/phprof/?run='.$run;
|
||||
return
|
||||
<x:frag>
|
||||
<h1>XHProf Results</h1>
|
||||
<div class="XHProfPlugin">
|
||||
<a href={$uri} target="_blank" class="XHProfPlugin">Permalink</a>
|
||||
<iframe src={$uri} width="100%" height="600" />
|
||||
</div>
|
||||
</x:frag>;
|
||||
}
|
||||
|
||||
$uri = URI::getRequestURI();
|
||||
return
|
||||
<x:frag>
|
||||
<h1>XHProf</h1>
|
||||
<form action={$uri} method="get" class="EnableFeature">
|
||||
<fieldset>
|
||||
<legend>Enable Profiling</legend>
|
||||
<p>Profiling was not enabled for this request. Click the button
|
||||
below to rerun the request with profiling enabled.</p>
|
||||
<button type="submit" name="_profile_" value="all"
|
||||
style="margin: 2px 1em; width: 75%;">
|
||||
Profile Page (With Includes)
|
||||
</button>
|
||||
<button type="submit" name="_profile_" value="exec"
|
||||
style="margin: 2px 1em; width: 75%;">
|
||||
Profile Page (No Includes)
|
||||
</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
</x:frag>;
|
||||
}
|
||||
|
||||
public function willShutdown() {
|
||||
if (empty($_REQUEST['_profile_']) ||
|
||||
$_REQUEST['_profile_'] != 'complete') {
|
||||
require_module('profiling/phprof/bootstrap');
|
||||
$this->xhprofData = FB_HotProfiler::stop_hotprofiler();
|
||||
}
|
||||
}
|
||||
|
||||
public function didShutdown() {
|
||||
if ($this->xhprofData) {
|
||||
require_module_lazy('profiling/phprof');
|
||||
$this->xhprofID = phprof_save_run($this->xhprofData);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
13
src/aphront/console/plugin/xhprof/__init__.php
Normal file
13
src/aphront/console/plugin/xhprof/__init__.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'aphront/console/plugin/base');
|
||||
phutil_require_module('phabricator', 'aphront/console/plugin/xhprof/api');
|
||||
|
||||
|
||||
phutil_require_source('DarkConsoleXHProfPlugin.php');
|
84
src/aphront/console/plugin/xhprof/api/DarkConsoleXHProfPluginAPI.php
Executable file
84
src/aphront/console/plugin/xhprof/api/DarkConsoleXHProfPluginAPI.php
Executable file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class DarkConsoleXHProfPluginAPI {
|
||||
|
||||
private static $profilerStarted;
|
||||
|
||||
public static function isProfilerAvailable() {
|
||||
return extension_loaded('xhprof');
|
||||
}
|
||||
|
||||
public static function includeXHProfLib() {
|
||||
// TODO: this is incredibly stupid, but we may not have Phutil metamodule
|
||||
// stuff loaded yet so we can't just phutil_get_library_root() our way
|
||||
// to victory.
|
||||
$root = __FILE__;
|
||||
for ($ii = 0; $ii < 7; $ii++) {
|
||||
$root = dirname($root);
|
||||
}
|
||||
|
||||
require_once $root.'/externals/xhprof/xhprof_lib.php';
|
||||
}
|
||||
|
||||
public static function hookProfiler($section) {
|
||||
if (empty($_REQUEST['__profile__'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($section != $_REQUEST['__profile__']) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self::isProfilerAvailable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self::$profilerStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::startProfiler();
|
||||
self::$profilerStarted = true;
|
||||
}
|
||||
|
||||
public static function startProfiler() {
|
||||
self::includeXHProfLib();
|
||||
xhprof_enable();
|
||||
}
|
||||
|
||||
public static function stopProfiler() {
|
||||
if (self::$profilerStarted) {
|
||||
$data = xhprof_disable();
|
||||
$data = serialize($data);
|
||||
$file_class = 'PhabricatorFile';
|
||||
PhutilSymbolLoader::loadClass($file_class);
|
||||
$file = call_user_func(
|
||||
array($file_class, 'newFromFileData'),
|
||||
$data,
|
||||
array(
|
||||
'mime-type' => 'application/xhprof',
|
||||
'name' => 'profile.xhprof',
|
||||
));
|
||||
return $file->getPHID();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
12
src/aphront/console/plugin/xhprof/api/__init__.php
Normal file
12
src/aphront/console/plugin/xhprof/api/__init__.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phutil', 'symbols');
|
||||
|
||||
|
||||
phutil_require_source('DarkConsoleXHProfPluginAPI.php');
|
|
@ -125,12 +125,17 @@ class AphrontDefaultApplicationConfiguration
|
|||
'$' => 'PhabricatorFacebookAuthController',
|
||||
'diagnose/$' => 'PhabricatorFacebookAuthDiagnosticsController',
|
||||
),
|
||||
|
||||
'/xhprof/' => array(
|
||||
'profile/(?<phid>[^/]+)/$' => 'PhabricatorXHProfProfileController',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function buildRequest() {
|
||||
$request = new AphrontRequest($this->getHost(), $this->getPath());
|
||||
$request->setRequestData($_GET + $_POST);
|
||||
$request->setApplicationConfiguration($this);
|
||||
return $request;
|
||||
}
|
||||
|
||||
|
@ -146,6 +151,7 @@ class AphrontDefaultApplicationConfiguration
|
|||
'</div>';
|
||||
|
||||
$view = new PhabricatorStandardPageView();
|
||||
$view->setRequest($this->getRequest());
|
||||
$view->appendChild($content);
|
||||
|
||||
$response = new AphrontWebpageResponse();
|
||||
|
@ -181,6 +187,7 @@ class AphrontDefaultApplicationConfiguration
|
|||
|
||||
$view = new PhabricatorStandardPageView();
|
||||
$view->setTitle('404 Not Found');
|
||||
$view->setRequest($this->getRequest());
|
||||
$view->appendChild($failure);
|
||||
|
||||
$response = new AphrontWebpageResponse();
|
||||
|
|
|
@ -30,21 +30,23 @@ class AphrontRequest {
|
|||
private $requestData;
|
||||
private $user;
|
||||
private $env;
|
||||
|
||||
final public function setEnvConfig(array $conf) {
|
||||
$this->env = $conf;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getEnvConfig($key, $default = null) {
|
||||
return idx($this->env, $key, $default);
|
||||
}
|
||||
private $applicationConfiguration;
|
||||
|
||||
final public function __construct($host, $path) {
|
||||
$this->host = $host;
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
final public function setApplicationConfiguration(
|
||||
$application_configuration) {
|
||||
$this->applicationConfiguration = $application_configuration;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getApplicationConfiguration() {
|
||||
return $this->applicationConfiguration;
|
||||
}
|
||||
|
||||
final public function setRequestData(array $request_data) {
|
||||
$this->requestData = $request_data;
|
||||
return $this;
|
||||
|
|
|
@ -10,8 +10,11 @@ phutil_require_module('phabricator', 'aphront/response/404');
|
|||
phutil_require_module('phabricator', 'aphront/response/ajax');
|
||||
phutil_require_module('phabricator', 'applications/differential/controller/base');
|
||||
phutil_require_module('phabricator', 'applications/differential/parser/changeset');
|
||||
phutil_require_module('phabricator', 'applications/differential/parser/markup');
|
||||
phutil_require_module('phabricator', 'applications/differential/storage/changeset');
|
||||
phutil_require_module('phabricator', 'applications/differential/storage/inlinecomment');
|
||||
phutil_require_module('phabricator', 'applications/differential/view/changesetdetailview');
|
||||
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
||||
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
|
|
@ -9,8 +9,10 @@
|
|||
phutil_require_module('phabricator', 'aphront/response/ajax');
|
||||
phutil_require_module('phabricator', 'aphront/response/dialog');
|
||||
phutil_require_module('phabricator', 'applications/differential/controller/base');
|
||||
phutil_require_module('phabricator', 'applications/differential/parser/markup');
|
||||
phutil_require_module('phabricator', 'applications/differential/storage/inlinecomment');
|
||||
phutil_require_module('phabricator', 'applications/differential/view/inlinecomment');
|
||||
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||
phutil_require_module('phabricator', 'view/dialog');
|
||||
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
|
|
@ -12,6 +12,7 @@ phutil_require_module('arcanist', 'parser/diff');
|
|||
phutil_require_module('phabricator', 'applications/differential/constants/changetype');
|
||||
phutil_require_module('phabricator', 'applications/differential/storage/changeset');
|
||||
phutil_require_module('phabricator', 'applications/differential/storage/diff');
|
||||
phutil_require_module('phabricator', 'applications/differential/view/inlinecomment');
|
||||
phutil_require_module('phabricator', 'applications/files/uri');
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||
phutil_require_module('phabricator', 'storage/queryfx');
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||
phutil_require_module('phabricator', 'view/base');
|
||||
|
||||
|
||||
|
|
|
@ -104,6 +104,9 @@ class PhabricatorFile extends PhabricatorFileDAO {
|
|||
$file->setStorageFormat(self::STORAGE_FORMAT_RAW);
|
||||
$file->setStorageHandle($blob->getID());
|
||||
|
||||
if (isset($params['mime-type'])) {
|
||||
$file->setMimeType($params['mime-type']);
|
||||
} else {
|
||||
try {
|
||||
$tmp = new TempFile();
|
||||
Filesystem::writeFile($tmp, $data);
|
||||
|
@ -112,6 +115,7 @@ class PhabricatorFile extends PhabricatorFileDAO {
|
|||
} catch (Exception $ex) {
|
||||
// Be robust here since we don't really care that much about mime types.
|
||||
}
|
||||
}
|
||||
|
||||
$file->save();
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
abstract class PhabricatorXHProfController extends PhabricatorController {
|
||||
|
||||
public function buildStandardPageResponse($view, array $data) {
|
||||
$page = $this->buildStandardPageView();
|
||||
|
||||
$page->setApplicationName('XHProf');
|
||||
$page->setBaseURI('/xhprof/');
|
||||
$page->setTitle(idx($data, 'title'));
|
||||
$page->setGlyph("\xE2\x98\x84");
|
||||
$page->appendChild($view);
|
||||
|
||||
$response = new AphrontWebpageResponse();
|
||||
return $response->setContent($page->render());
|
||||
}
|
||||
|
||||
}
|
15
src/applications/xhprof/controller/base/__init__.php
Normal file
15
src/applications/xhprof/controller/base/__init__.php
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'aphront/response/webpage');
|
||||
phutil_require_module('phabricator', 'applications/base/controller/base');
|
||||
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
||||
phutil_require_source('PhabricatorXHProfController.php');
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class PhabricatorXHProfProfileController
|
||||
extends PhabricatorXHProfController {
|
||||
|
||||
private $phid;
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->phid = $data['phid'];
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
|
||||
$file = id(new PhabricatorFile())->loadOneWhere(
|
||||
'phid = %s',
|
||||
$this->phid);
|
||||
|
||||
if (!$file) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$data = $file->loadFileData();
|
||||
$data = unserialize($data);
|
||||
if (!$data) {
|
||||
throw new Exception("Failed to unserialize XHProf profile!");
|
||||
}
|
||||
|
||||
$request = $this->getRequest();
|
||||
$symbol = $request->getStr('symbol');
|
||||
|
||||
if ($symbol) {
|
||||
$view = new PhabricatorXHProfProfileSymbolView();
|
||||
$view->setProfileData($data);
|
||||
$view->setSymbol($symbol);
|
||||
} else {
|
||||
$view = new PhabricatorXHProfProfileTopLevelView();
|
||||
$view->setProfileData($data);
|
||||
$view->setLimit(100);
|
||||
}
|
||||
|
||||
return $this->buildStandardPageResponse(
|
||||
$view,
|
||||
array(
|
||||
'title' => 'Profile',
|
||||
));
|
||||
}
|
||||
}
|
18
src/applications/xhprof/controller/profile/__init__.php
Normal file
18
src/applications/xhprof/controller/profile/__init__.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'aphront/response/404');
|
||||
phutil_require_module('phabricator', 'applications/files/storage/file');
|
||||
phutil_require_module('phabricator', 'applications/xhprof/controller/base');
|
||||
phutil_require_module('phabricator', 'applications/xhprof/view/symbol');
|
||||
phutil_require_module('phabricator', 'applications/xhprof/view/toplevel');
|
||||
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
||||
phutil_require_source('PhabricatorXHProfProfileController.php');
|
|
@ -0,0 +1,163 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class PhabricatorXHProfProfileSymbolView extends AphrontView {
|
||||
|
||||
private $profileData;
|
||||
private $baseURI;
|
||||
private $symbol;
|
||||
|
||||
public function setProfileData(array $data) {
|
||||
$this->profileData = $data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setBaseURI($uri) {
|
||||
$this->baseURI = $uri;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setSymbol($symbol) {
|
||||
$this->symbol = $symbol;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
DarkConsoleXHProfPluginAPI::includeXHProfLib();
|
||||
|
||||
$data = $this->profileData;
|
||||
|
||||
$GLOBALS['display_calls'] = true;
|
||||
$totals = array();
|
||||
$flat = xhprof_compute_flat_info($data, $totals);
|
||||
unset($GLOBALS['display_calls']);
|
||||
|
||||
$symbol = $this->symbol;
|
||||
|
||||
$children = array();
|
||||
$parents = array();
|
||||
foreach ($this->profileData as $key => $counters) {
|
||||
list($parent, $child) = explode('==>', $key, 2);
|
||||
if ($parent == $symbol) {
|
||||
$children[$key] = $child;
|
||||
} else if ($child == $symbol) {
|
||||
$parents[$key] = $parent;
|
||||
}
|
||||
}
|
||||
|
||||
$base_uri = $this->baseURI;
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array(
|
||||
'Metrics for this Call',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
);
|
||||
$rows[] = array(
|
||||
phutil_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $base_uri.'?symbol='.$symbol,
|
||||
),
|
||||
phutil_escape_html($symbol)),
|
||||
$flat[$symbol]['ct'],
|
||||
$flat[$symbol]['wt'],
|
||||
'',
|
||||
$flat[$symbol]['excl_wt'],
|
||||
'',
|
||||
);
|
||||
|
||||
$rows[] = array(
|
||||
'Parent Calls',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
);
|
||||
foreach ($parents as $key => $name) {
|
||||
$rows[] = array(
|
||||
phutil_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $base_uri.'?symbol='.$name,
|
||||
),
|
||||
phutil_escape_html($name)),
|
||||
$data[$key]['ct'],
|
||||
$data[$key]['wt'],
|
||||
'',
|
||||
$data[$key]['wt'],
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
$rows[] = array(
|
||||
'Child Calls',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
);
|
||||
foreach ($children as $key => $name) {
|
||||
$rows[] = array(
|
||||
phutil_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $base_uri.'?symbol='.$name,
|
||||
),
|
||||
phutil_escape_html($name)),
|
||||
$data[$key]['ct'],
|
||||
$data[$key]['wt'],
|
||||
'',
|
||||
$data[$key]['wt'],
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
||||
$table = new AphrontTableView($rows);
|
||||
$table->setHeaders(
|
||||
array(
|
||||
'Symbol',
|
||||
'Count',
|
||||
'Incl Wall Time',
|
||||
'%',
|
||||
'Excl Wall Time',
|
||||
'%',
|
||||
));
|
||||
$table->setColumnClasses(
|
||||
array(
|
||||
'wide pri',
|
||||
'n',
|
||||
'n',
|
||||
'n',
|
||||
'n',
|
||||
'n',
|
||||
));
|
||||
|
||||
$panel = new AphrontPanelView();
|
||||
$panel->setHeader('XHProf Profile');
|
||||
$panel->appendChild($table);
|
||||
|
||||
return $panel->render();
|
||||
}
|
||||
}
|
17
src/applications/xhprof/view/symbol/__init__.php
Normal file
17
src/applications/xhprof/view/symbol/__init__.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'aphront/console/plugin/xhprof/api');
|
||||
phutil_require_module('phabricator', 'view/base');
|
||||
phutil_require_module('phabricator', 'view/control/table');
|
||||
phutil_require_module('phabricator', 'view/layout/panel');
|
||||
|
||||
phutil_require_module('phutil', 'markup');
|
||||
|
||||
|
||||
phutil_require_source('PhabricatorXHProfProfileSymbolView.php');
|
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class PhabricatorXHProfProfileTopLevelView extends AphrontView {
|
||||
|
||||
private $profileData;
|
||||
private $limit;
|
||||
private $baseURI;
|
||||
|
||||
public function setProfileData(array $data) {
|
||||
$this->profileData = $data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setLimit($limit) {
|
||||
$this->limit = $limit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setBaseURI($uri) {
|
||||
$this->baseURI = $uri;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
DarkConsoleXHProfPluginAPI::includeXHProfLib();
|
||||
|
||||
$GLOBALS['display_calls'] = true;
|
||||
$totals = array();
|
||||
$flat = xhprof_compute_flat_info($this->profileData, $totals);
|
||||
unset($GLOBALS['display_calls']);
|
||||
|
||||
$aggregated = array();
|
||||
foreach ($flat as $call => $counters) {
|
||||
$agg_call = reset(explode('@', $call, 2));
|
||||
if (empty($aggregated[$agg_call])) {
|
||||
$aggregated[$agg_call] = $counters;
|
||||
} else {
|
||||
foreach ($aggregated[$agg_call] as $key => $val) {
|
||||
if ($key != 'wt') {
|
||||
$aggregated[$agg_call][$key] += $counters[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$flat = $aggregated;
|
||||
|
||||
$flat = isort($flat, 'wt');
|
||||
$flat = array_reverse($flat);
|
||||
|
||||
$rows = array();
|
||||
$rows[] = array(
|
||||
'Total',
|
||||
number_format($totals['ct']),
|
||||
number_format($totals['wt']).' us',
|
||||
'100.0%',
|
||||
number_format($totals['wt']).' us',
|
||||
'100.0%',
|
||||
);
|
||||
|
||||
if ($this->limit) {
|
||||
$flat = array_slice($flat, 0, $this->limit);
|
||||
}
|
||||
|
||||
$base_uri = $this->baseURI;
|
||||
|
||||
foreach ($flat as $call => $counters) {
|
||||
$rows[] = array(
|
||||
phutil_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $base_uri.'?symbol='.$call,
|
||||
),
|
||||
phutil_escape_html($call)),
|
||||
number_format($counters['ct']),
|
||||
number_format($counters['wt']).' us',
|
||||
sprintf('%.1f%%', 100 * $counters['wt'] / $totals['wt']),
|
||||
number_format($counters['excl_wt']).' us',
|
||||
sprintf('%.1f%%', 100 * $counters['excl_wt'] / $totals['wt']),
|
||||
);
|
||||
}
|
||||
|
||||
$table = new AphrontTableView($rows);
|
||||
$table->setHeaders(
|
||||
array(
|
||||
'Symbol',
|
||||
'Count',
|
||||
'Incl Wall Time',
|
||||
'%',
|
||||
'Excl Wall Time',
|
||||
'%',
|
||||
));
|
||||
$table->setColumnClasses(
|
||||
array(
|
||||
'wide pri',
|
||||
'n',
|
||||
'n',
|
||||
'n',
|
||||
'n',
|
||||
'n',
|
||||
));
|
||||
|
||||
$panel = new AphrontPanelView();
|
||||
$panel->setHeader('XHProf Profile');
|
||||
$panel->appendChild($table);
|
||||
|
||||
return $panel->render();
|
||||
}
|
||||
}
|
18
src/applications/xhprof/view/toplevel/__init__.php
Normal file
18
src/applications/xhprof/view/toplevel/__init__.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'aphront/console/plugin/xhprof/api');
|
||||
phutil_require_module('phabricator', 'view/base');
|
||||
phutil_require_module('phabricator', 'view/control/table');
|
||||
phutil_require_module('phabricator', 'view/layout/panel');
|
||||
|
||||
phutil_require_module('phutil', 'markup');
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
||||
phutil_require_source('PhabricatorXHProfProfileTopLevelView.php');
|
|
@ -45,6 +45,10 @@ class AphrontPageView extends AphrontView {
|
|||
return;
|
||||
}
|
||||
|
||||
protected function willSendResponse($response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
|
||||
$this->willRenderPage();
|
||||
|
@ -54,7 +58,7 @@ class AphrontPageView extends AphrontView {
|
|||
$body = $this->getBody();
|
||||
$tail = $this->getTail();
|
||||
|
||||
return <<<EOHTML
|
||||
$response = <<<EOHTML
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
@ -68,6 +72,10 @@ class AphrontPageView extends AphrontView {
|
|||
</html>
|
||||
|
||||
EOHTML;
|
||||
|
||||
$response = $this->willSendResponse($response);
|
||||
return $response;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -65,6 +65,14 @@ class PhabricatorStandardPageView extends AphrontPageView {
|
|||
|
||||
|
||||
protected function willRenderPage() {
|
||||
|
||||
if (!$this->getRequest()) {
|
||||
throw new Exception(
|
||||
"You must set the Request to render a PhabricatorStandardPageView.");
|
||||
}
|
||||
|
||||
$console = $this->getRequest()->getApplicationConfiguration()->getConsole();
|
||||
|
||||
require_celerity_resource('phabricator-core-css');
|
||||
require_celerity_resource('phabricator-core-buttons-css');
|
||||
require_celerity_resource('phabricator-standard-page-view');
|
||||
|
@ -72,6 +80,15 @@ class PhabricatorStandardPageView extends AphrontPageView {
|
|||
require_celerity_resource('javelin-lib-dev');
|
||||
require_celerity_resource('javelin-workflow-dev');
|
||||
|
||||
if ($console) {
|
||||
require_celerity_resource('aphront-dark-console-css');
|
||||
Javelin::initBehavior(
|
||||
'dark-console',
|
||||
array(
|
||||
'uri' => '/~/',
|
||||
));
|
||||
}
|
||||
|
||||
$this->bodyContent = $this->renderChildren();
|
||||
}
|
||||
|
||||
|
@ -94,7 +111,19 @@ class PhabricatorStandardPageView extends AphrontPageView {
|
|||
return $this->glyph;
|
||||
}
|
||||
|
||||
protected function willSendResponse($response) {
|
||||
$console = $this->getRequest()->getApplicationConfiguration()->getConsole();
|
||||
if ($console) {
|
||||
$response = str_replace(
|
||||
'<darkconsole />',
|
||||
$console->render($this->getRequest()),
|
||||
$response);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected function getBody() {
|
||||
$console = $this->getRequest()->getApplicationConfiguration()->getConsole();
|
||||
|
||||
$tabs = array();
|
||||
foreach ($this->tabs as $name => $tab) {
|
||||
|
@ -142,6 +171,7 @@ class PhabricatorStandardPageView extends AphrontPageView {
|
|||
}
|
||||
|
||||
return
|
||||
($console ? '<darkconsole />' : null).
|
||||
'<div class="phabricator-standard-page">'.
|
||||
'<div class="phabricator-standard-header">'.
|
||||
'<div class="phabricator-login-details">'.
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
|
||||
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
||||
phutil_require_module('phabricator', 'view/page/base');
|
||||
|
||||
phutil_require_module('phutil', 'markup');
|
||||
|
|
|
@ -34,6 +34,9 @@ setup_aphront_basics();
|
|||
phutil_require_module('phabricator', 'infrastructure/env');
|
||||
PhabricatorEnv::setEnvConfig($conf);
|
||||
|
||||
phutil_require_module('phabricator', 'aphront/console/plugin/xhprof/api');
|
||||
DarkConsoleXHProfPluginAPI::hookProfiler('all');
|
||||
|
||||
$host = $_SERVER['HTTP_HOST'];
|
||||
$path = $_REQUEST['__path__'];
|
||||
|
||||
|
@ -51,6 +54,7 @@ switch ($host) {
|
|||
|
||||
$application->setHost($host);
|
||||
$application->setPath($path);
|
||||
$application->willBuildRequest();
|
||||
$request = $application->buildRequest();
|
||||
$application->setRequest($request);
|
||||
list($controller, $uri_data) = $application->buildController();
|
||||
|
@ -83,6 +87,19 @@ foreach ($headers as $header) {
|
|||
list($header, $value) = $header;
|
||||
header("{$header}: {$value}");
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['__profile__']) &&
|
||||
($_REQUEST['__profile__'] == 'all')) {
|
||||
$profile = DarkConsoleXHProfPluginAPI::stopProfiler();
|
||||
$profile = print_r($profile, true);
|
||||
if (strpos($response_string, '<body>') !== false) {
|
||||
$response_string = str_replace(
|
||||
'<body>',
|
||||
'<body>'.$profile,
|
||||
$response_string);
|
||||
}
|
||||
}
|
||||
|
||||
echo $response_string;
|
||||
|
||||
|
||||
|
|
46
webroot/rsrc/css/aphront/dark-console.css
Normal file
46
webroot/rsrc/css/aphront/dark-console.css
Normal file
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* @provides aphront-dark-console-css
|
||||
*/
|
||||
|
||||
.dark-console {
|
||||
background: #555555;
|
||||
color: #eeeeee;
|
||||
width: 100%;
|
||||
font-family: "Verdana";
|
||||
font-size: 11px;
|
||||
border-bottom: 2px solid #000000;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.dark-console-tabs {
|
||||
width: 180px;
|
||||
background: #222222;
|
||||
border-right: 1px solid #888888;
|
||||
padding: 2.5em 0em;
|
||||
}
|
||||
|
||||
|
||||
a.dark-console-tab {
|
||||
padding: .75em 12px;
|
||||
text-align: right;
|
||||
background: #444444;
|
||||
position: relative;
|
||||
border: 1px solid #666666;
|
||||
border-width: 1px 0;
|
||||
border-right-color: #888888;
|
||||
margin-bottom: 2px;
|
||||
display: block;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
a.dark-console-tab-selected {
|
||||
margin-right: -1px;
|
||||
padding-right: 13px;
|
||||
background: #555555;
|
||||
border-color: #888888;
|
||||
border-right-color: #555555;
|
||||
color: #eeeeee;
|
||||
}
|
||||
|
||||
|
|
@ -64,6 +64,12 @@
|
|||
font-size: 10px;
|
||||
}
|
||||
|
||||
.aphront-table-view td.n {
|
||||
font-family: "Monaco", monospace;
|
||||
font-size: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.aphront-table-view tr.no-data td {
|
||||
padding: 1em;
|
||||
text-align: center;
|
||||
|
|
55
webroot/rsrc/js/application/core/behavior-dark-console.js
Normal file
55
webroot/rsrc/js/application/core/behavior-dark-console.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* @provides javelin-behavior-dark-console
|
||||
*/
|
||||
|
||||
JX.behavior('dark-console', function() {
|
||||
JX.Stratcom.listen(
|
||||
'click',
|
||||
['dark-console', 'dark-console-tab'],
|
||||
function(e) {
|
||||
var console = e.getNode('dark-console');
|
||||
var tabs = JX.DOM.scry(console, 'a', 'dark-console-tab');
|
||||
var panels = JX.DOM.scry(console, 'div', 'dark-console-panel');
|
||||
var target = e.getTarget();
|
||||
for (var ii = 0; ii < tabs.length; ii++) {
|
||||
JX.DOM.alterClass(
|
||||
tabs[ii],
|
||||
'dark-console-tab-selected',
|
||||
tabs[ii] == target);
|
||||
(tabs[ii] != target ? JX.DOM.hide : JX.DOM.show)(panels[ii]);
|
||||
}
|
||||
/*
|
||||
new JX.Request(e.getNodeData('dark-console').uri, JX.bag)
|
||||
.setData({tab: e.getNodeData('dark-console-tab').key})
|
||||
.send();
|
||||
*/
|
||||
});
|
||||
|
||||
JX.Stratcom.listen(
|
||||
'keypress',
|
||||
null,
|
||||
function(e) {
|
||||
var raw = e.getRawEvent();
|
||||
if ((String.fromCharCode(raw.charCode).charAt(0) == '`') &&
|
||||
!raw.shiftKey &&
|
||||
!raw.metaKey) {
|
||||
|
||||
if (JX.Stratcom.pass()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var console = JX.DOM.find(document.body, 'table', 'dark-console');
|
||||
var data = JX.Stratcom.getData(console);
|
||||
data.visible = !data.visible;
|
||||
if (data.visible) {
|
||||
JX.DOM.show(console);
|
||||
} else {
|
||||
JX.DOM.hide(console);
|
||||
}
|
||||
|
||||
// new JX.Request(data.uri, JX.bag)
|
||||
// .setData({visible: data.visible})
|
||||
// .send();
|
||||
}
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue