Upgrade to Pro — share decks privately, control downloads, hide ads and more …

プロファイル結果の可視化三本勝負 in PHP

uzulla
November 06, 2020

プロファイル結果の可視化三本勝負 in PHP

speaker uzulla
in "Laravel 勉強会 in 札幌 vol.3"
https://larasap.connpass.com/event/193566/

ref:
https://github.com/uzulla/xhprof-flamegraphs

uzulla

November 06, 2020
Tweet

More Decks by uzulla

Other Decks in Programming

Transcript

  1. https://github.com/longxinH/xhprof 「今回はこのxhprofにするぜ(fbが諦めたので、forkが⾊々ある…)」 $ git clone https://github.com/longxinH/xhprof.git ./xhprof $ cd xhprof/extension/

    $ phpize $ ./configure $ make && sudo make install $ vi /path/to/php.ini # 先頭にでも追加 extension = xhprof.so $ mkdir -p xhprof/tmp $ chmod 777 xhprof/tmp
  2. vi index.php <?php /** * October - The PHP platform

    that gets back to basics. * * @package October * @author Alexey Bobkov, Samuel Georges */
  3. 突然タレを⼊れます <?php ini_set("xhprof.output_dir", __DIR__."/xhprof/tmp"); define("START_AT", microtime(true)); require_once (__DIR__ . '/xhprof/xhprof_lib/utils/xhprof_runs.php');

    xhprof_enable(XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY); register_shutdown_function(function () { $id = preg_replace('|/|u', " /", $_SERVER['REQUEST_URI']); $id = preg_replace('/[^a-zA-Z0-9 /]/u', "-", $id); $id .= "_" . sprintf("%d", (microtime(true) - START_AT)) . "ms_"; $runs = new XHProfRuns_Default(); $runs->save_run(xhprof_disable(), $id); }); /** 〜略
  4. 別シェルで xhprof ビューワーを⽴ち上げる $ php -d xhprof.output_dir=`pwd`/xhprof/tmp \ -S 127.0.0.1:3334

    \ -t xhprof/xhprof_html/ # cli でphp.ini のxhprof の出⼒先dir 指定と # ビルトインサーバーのIPaddr:port 指定と # ビューワーアプリのDocRoot 指定してます $ open http://127.0.0.1:3334
  5. ここまでのまとめ index.php をちょっといじるだけでプロファイラを⼊れられる なにもかもわからんアプリでもらくらく! ビューワーは xhprof/xhprof_html/ で php -S であげるのが便利

    リモートからつなぎたいなら、ポートフォワードすると良い なれると数分でここまで来ます 「使ったことがある」ではなく、「秒でいれられる」ようになりましょう
  6. さて、Call graphを⾒ていきましょう 雑な⾒⽅ 「⾚くてデカい奴がヤバい」 「⻩⾊い奴は気になる」 「⽩い所は⼀旦無視だが、いずれ倒すことになる」 数字の読み⽅ Inc → 「こいつから以下」がどれくらいかかっているか

    Excl → 「こいつ⾃体が」どれくらいかかっているか Incがデカイのをたどり、Exclがデカイのを探す ⽮印の太さ 時間のかかり具合(≒ 処理の進む⽅向)
  7. 雑なPreload⽤コード $ vi preload.php <?php # preload.php こいつをphp.ini に書く ini_set("error_reporting",

    '-1'); ini_set("display_errors",'1'); ini_set("log_errors",'1'); include "php_list.php"; foreach ($list as $file) { opcache_compile_file($file); }
  8. 雑にPreloadするphpファイルのリストをつくる $ echo "<?php $list=[];" > php_list.php $ find `pwd`

    |grep -e "\.php$" | php -R 'echo "\$list[] = \"$argn\";".PHP_EOL;' >> php_list.php <?php $list=[]; # php_list.php # 「『とにかく、`.php` ファイル全部いれてみた』などと供述しており」 $list[] = "/tmp/sample/php_list.php"; $list[] = "/tmp/sample/server.php"; $list[] = "/tmp/sample/bootstrap/autoload.php"; $list[] = "/tmp/sample/bootstrap/app.php"; $list[] = "/tmp/sample/index.php"; // めっちゃ⻑いので省略
  9. 起動速度(?) $ wc -l php_list.php 4834 php_list.php # 雑に追加した結果のファイル数 $

    time php -c phpop.ini -r "echo 1;" ( ハチャメチャなWarning) 1 real 0m0.916s user 0m0.621s sys 0m0.274s $ time php -r "echo 1;" 1 real 0m0.123s user 0m0.057s sys 0m0.057s 0.916 - 0.123 = 0.793(sec) # 絞り込まないPreload は結構時間かかる。とはいえ「実⾏」ではないのでこんなもの
  10. # ⼯夫例 $id = preg_replace('|/|u', " /", $_SERVER['REQUEST_URI']); $id =

    preg_replace('/[^a-zA-Z0-9 /]/u', "-", $id); $id .= "_" . sprintf("%d", ((microtime(true) - START_AT))*1000) . "ms_"; $runs = new XHProfRuns_Default(); $runs->save_run(xhprof_disable(), $id); やり⽅は⾊々あるが、「ファイル名なので」 / とか ? とか & とかがあるとおかしくな る、それを避ける必要はある 例えば、 / は全⾓ / にするとか(他も同様に…) まあビューワーを改造しちゃってもいいかもね ここらへん、⾊々⼯夫すると便利。 ログインIDいれたりとか… が!⼿間かけてもダルいので、あきらめが肝⼼。
  11. $ php -i |grep xdebug xdebug xdebug support => enabled

    ( 省略) $ mkdir xdebug_prof/ zend_extension="xdebug.so" # ini_set では設定できんのよなあ、他の⽅法はあるがぐぐってください xdebug.profiler_enable=1 xdebug.profiler_output_dir=/tmp/sample/xdebug_prof $ php -c ./php.ini -S 127.0.0.1:3333 $ ls xdebug_prof/ cachegrind.out.7613 # この⽣成ファイルを指定する
  12. Flame graph Call Graph(例:xhprof),Call Tree(例:cachegrind)以外にFlame Graphという可視化 ⼀定サイクルでスタックトレースをサンプリングして、可視化する 「どのURLが重い」等ではなく、「どの処理がいつもいる・深い」を探す (スレッド処理系⽂化圏の概念) サンプルがタイマーで取られるので、ボロボロこぼれる

    「複数のリクエストをまたいで」ビジュアライズしないと意味が薄い PHPだと不⼈気(パッとまともに動くものがない気がする) xhprofで⽣成されるデータを加⼯して、⽣成するツールはある xhprof_enable ではなく、 xhprof_sample_enable でログを取る サンプリング間隔は xhprof.sampling_interval で変更可能(初期0.1秒)
  13. 以下、Flame graphの作り⽅ # index.php にいれるサンプルコード # コールグラフ作ったときのそれとコードが違う、共存不可能 ini_set("xhprof.output_dir", __DIR__ .

    "/xhprof/tmp"); ini_set("xhprof.sampling_interval", "100"); // デフォルト100000 require_once(__DIR__ . '/xhprof/xhprof_lib/utils/xhprof_runs.php'); xhprof_sample_enable(); register_shutdown_function(function () { $runs = new XHProfRuns_Default(); $runs->save_run(xhprof_disable(), (string)microtime(true)); }); $ rm xhprof/tmp/* # 過去データは必ず⼀度消す $ ab -n 1000 http://localhost:3333 # … などで、⼀定時間負荷をかけつづける。⼤量にファイルが⽣成される。 # 時間をかければかけるほど、精度が上がる。はず。⼀般的には。 # しかし重い!
  14. # ツールをDL する、FlameGraph ⽣成ツール $ git clone [email protected]:brendangregg/FlameGraph.git # 変換ツール…

    の本家(?)がうごかんので、ちょっといじったものです $ git clone [email protected]:uzulla/xhprof-flamegraphs.git # xhprof 結果dir を指定し、⼤量のxhprof ファイルを処理する $ xhprof-flamegraphs/xhprof-sample-to-flamegraph-stacks xhprof/tmp \ | FlameGraph/flamegraph.pl > flamegraph.svg # ⽣成されたsvg をブラウザで開くと、クリック操作できます $ open flamegraph.svg