diff --git a/css/app.css b/css/app.css index cf036cf7c1b0f62017a634910aadc84fe6638231..a96b461978fbad4ab7117a66dd315d95f8969ad4 100644 --- a/css/app.css +++ b/css/app.css @@ -4967,7 +4967,11 @@ table.hover tr:nth-of-type(even):hover { .fa-percent:before { content: ""; } +code { + font-size: 0.8em; } + div.sourceCode { + line-height: 1; border: 1px #cacaca solid; background-color: #f7f7f7; margin-bottom: 0.625rem; @@ -4986,7 +4990,12 @@ table.sourceCode { table.sourceCode tbody { border: none; } -table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode, table.sourceCode pre { +table.sourceCode, +tr.sourceCode, +td.lineNumbers, +td.sourceCode, +table.sourceCode +pre { margin: 0; padding: 0; border: 0; @@ -4998,7 +5007,8 @@ td.lineNumbers { text-align: right; color: #AAAAAA; padding-right: 5px; - padding-left: 5px; } + padding-left: 5px; + line-height: 1.14; } td.sourceCode { padding-left: 0.625rem; } diff --git a/images/posts/xhprof.png b/images/posts/xhprof.png new file mode 100644 index 0000000000000000000000000000000000000000..2c91f0986c15a87d999e35c6df49f736b0d7b4b7 Binary files /dev/null and b/images/posts/xhprof.png differ diff --git a/posts/2016-02-10-laravel-xhprof.md b/posts/2016-02-10-laravel-xhprof.md new file mode 100644 index 0000000000000000000000000000000000000000..af42590d692159af8d6e314c8eb8a2e04eb6ae75 --- /dev/null +++ b/posts/2016-02-10-laravel-xhprof.md @@ -0,0 +1,137 @@ +--- +title: Profiling Laravel requests with XHProf +date: Web Feb 10 19:00:00 EDT 2016 +author: Eduardo Trujillo +uuid: a78def34-69ac-4830-b97e-106b7c855e71 +--- + +I've recently been dealing with slow endpoints on a PHP application written +using Laravel. Normally, I would use XDebug, which is the go-to PHP debugging +tool, capture performance information of the affected code paths, and load the +results into a tool like PHPStorm, which has a built-in UI for analyzing the +captures. This seemed a bit tedious to me due to having to manually open each +file, so I decided to go and look for other options, and that led me to XHProf, +which is a tool specific for profiling built by Facebook. It looked promising +so I decided to give it a try. + +Like XDebug, XHProf needs its own extension to work. Luckily, the extension is +built-in on HHVM, so I didn't had to compile anything _(well, besides HHVM +itself)_ to get started. + +After capturing a couple of runs, I was able to identify the bottlenecks on the +endpoints, thanks to the callgraphs, which are rendered by the XHProf UI and +point out in red the functions that consumed most of the time during the +request or session. + +The UI is not very visually appealing, but it definitely gets the job done. I +was able to quickly lookup runs which definitely takes less time than loading +individual XDebug captures into PHPStorm. + +Additionally, being able to enable the profiling programatically reduces the +amount of noise when you want to focus on a specific section of your code. + +Below, I include a quick How-To on using XHProf within Laravel applications: + +## Getting started + +Install the XHProf package using Composer: + +> NOTE: There are many XHProf packages out there. Some are maintained, others +are abandoned. The most up-to-date seems to be `phacility/xhprof`, however it +does not include a `composer.json` file. `lox/xhprof` is a fork of that same +repository, but it also includes a `composer.json` file. + +```bash +composer require lox/xhprof +``` + +Then add a middleware class to your project that enables profiling while a +request is being handled. Its also possible to enable XHProf earlier in the +application lifecycle, if you are interested in profiling the framework +booting process. + +Here's an example of said middleware: + +```php +namespace App\Http\Middleware; + +use Closure; +use Illuminate\Http\Request; +use XHProfRuns_Default; + +/** + * Class XhprofMiddleware. + * + * XHProf is a useful profiling tool built by Facebook. This middleware allows + * developers to profile specific requests by appending `xhprof=true` to any + * query. + * + * Results will be stored on `/tmp` and can be visualized using the XHProf UI. + * + * @author Eduardo Trujillo <ed@chromabits.com> + * @package App\Http\Middleware + */ +class XhprofMiddleware +{ + /** + * Handle an incoming request. + * + * @param Request $request + * @param Closure $next + * + * @return mixed + */ + public function handle($request, Closure $next) + { + // We will only profile requests if the proper flag is set on the query + // of the request. You may further customize this to be disabled on + // production releases of your application. + if ($request->query->get('xhprof') !== 'true') { + return $next($request); + } + + xhprof_enable(); + + $result = $next($request); + + $xhprofData = xhprof_disable(); + $xhprofRuns = new XHProfRuns_Default('/tmp'); + + $runId = $xhprofRuns->save_run($xhprofData, 'xhprof_laravel'); + + // We will attach the XHProf run ID as part of the response header. + // This is a lot better than modifying the actual response body. + $result->headers->set('X-Xhprof-Run-Id', $runId); + + return $result; + } +} +``` + +Then, load the middleware into the application by adding it to your +`Kernel.php` file. + +## Capturing runs and visualizing + +The middleware above is setup to enable profiling whenever `xhprof=true` is +set on the request query. This means that setting this flag on any request to +your application should trigger profiing for that request. + +All runs will be stored on `/tmp` by default. To help with looking up results, +the middleware will also add a HTTP header to the response with the ID of the +run. + + + +Results can be inspected by hand, however it is generally more useful to use +the built-in "UI" which is capable of generating callgraphs and showing the +timing of all functions called. + +With HHVM, a web server for the UI can be launched from the root of a project: + +```bash +hhvm -m server -d hhvm.server.type=proxygen \ + -d hhvm.server.source_root=$(pwd)/vendor/lox/xhprof/xhprof_html \ + -d hhvm.server.port=8001 \ + -d hhvm.server.default_document=index.php +``` diff --git a/scss/_code.scss b/scss/_code.scss index 3f0445b3383d3ec798615195f51ba8b02b9a9200..b0ef18b9484fcc7da4073f5bd50f716571fee58d 100644 --- a/scss/_code.scss +++ b/scss/_code.scss @@ -1,4 +1,9 @@ +code { + font-size: 0.8em; +} + div.sourceCode { + line-height: 1; border: 1px $medium-gray solid; background-color: scale-color($medium-gray, $lightness: 85%); margin-bottom: rem-calc(10); @@ -24,9 +29,29 @@ table.sourceCode { border: none; } } -table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode, table.sourceCode pre - { margin: 0; padding: 0; border: 0; vertical-align: baseline; border: none; } -td.lineNumbers { border-right: 1px solid #AAAAAA; text-align: right; color: #AAAAAA; padding-right: 5px; padding-left: 5px; } + +table.sourceCode, +tr.sourceCode, +td.lineNumbers, +td.sourceCode, +table.sourceCode +pre { + margin: 0; + padding: 0; + border: 0; + vertical-align: baseline; + border: none; +} + +td.lineNumbers { + border-right: 1px solid #AAAAAA; + text-align: right; + color: #AAAAAA; + padding-right: 5px; + padding-left: 5px; + line-height: 1.14; +} + td.sourceCode { padding-left: rem-calc(10); } .sourceCode span.kw { color: #007020; font-weight: bold; } .sourceCode span.dt { color: #902000; } diff --git a/site.hs b/site.hs index 6b02b91f0edeab284bdd356262793b59ff5863ce..90390c1935a8e3fef074727e28784d223a2f428e 100644 --- a/site.hs +++ b/site.hs @@ -59,6 +59,10 @@ main = hakyllWith hakyllConf $ do match "images/*" $ do route idRoute compile copyFileCompiler + + match "images/posts/*" $ do + route idRoute + compile copyFileCompiler match "css/app.css" $ do route $ setExtension "css"