The article will discuss the possible use of xdebug by system administrators of web servers. It may seem that the administrator does not have to diagnose and debug the code, since this is the work of the programmer. This is true. But, how, in case of a problem, convince the programmer that his code is not optimal (if this is the case) and needs to be reworked, if the programmer says all the time: “We are fine - repair the server”? Imagine that this is a programmer with whom it is undesirable to argue. For example, our very expensive and outraged customer.
When administering web servers, engineers often encounter problems that ordinary users of the site call “slow working” or “slowing down”. Of course, this is a very important issue that can have serious financial consequences for the site owner. It is with such a wording that “slow working” is usually addressed by users and / or site owners to the administrator of the web server, and this wording is enough to start diagnosing everything.
Interestingly, the causes of such problems may be hidden almost anywhere. And there can be several of these reasons, and the problems themselves can be difficult to reproduce. What the user ultimately classifies as “brakes” might be at the lower level:
- Delays in the network access channel to the site
- Server hardware problems
- Insufficient server resources
- Non-optimal operating system
- Non-optimal software
- Problems with access to third-party resources, access to which is implemented synchronously
- Not optimally written site code
')
The task of the administrator is to identify and, if possible, eliminate these problems. If they, of course, are in his area of ​​responsibility. And the last and possibly penultimate items are not included in his area of ​​responsibility. But we must also show that the problem lies precisely in these points.
It is about the latter case, we will talk in our article. Practice shows that this case accounts for about 90% of all problems associated with the slow work of the site. Suboptimal code, poorly written SQL queries, improper use of locks - all this can lead to a slowdown of the site. And if, up to a certain point, powerful iron could digest bad code, then with the onset of a certain “x” point, it simply ceases to cope with the load. It is possible to increase the capacity further, but, first, they will one day run out, and second, these are extra equipment costs. Therefore, first of all, the solution of the problem should be started with the analysis of the site code.
It is worth mentioning that there are cases when the acquisition of new hardware is more cost-effective than parsing and editing a large amount of code.
The main task of the administrator, oddly enough - the administration of the server. He is not a developer and has no moral right to touch the code, even if it does not work well. Therefore, in order to conduct the most detailed diagnostics of this code and identify bottlenecks leading to the slow work of the site, it is necessary to use tools that do not require editing the code.
Yes, I forgot, it is about developing in PHP! This is what most web programmers use and like. Although, at the expense of "love" question, rather controversial.
Xdebug
Xdebug is a
PHP extension written by one of the
PHP developers and intended to collect and analyze debug information in php code. It is important to note that this is an open-source project.
We will consider
xdebug through the prism of the system administrator, affecting only the functionality that does not require editing the site code, and does not slow down the server.
Obviously, any profiling or tracing introduces additional delays during code execution. Therefore, the diagnosis should not affect the performance of the site.
Install xdebug
When using Centos / RHEL / Fedora, the easiest way to install xdebug will be to install from the EPEL repository:
$ yum install php-pecl-xdebugInstalling using pecl:
$ pecl install xdebugYou can also install xdebug from the source code by downloading it at
http://xdebug.org/download.php :
$ tar xvzf xdebug-2.2.1.tgz$ cd xdebug-2.2.1$ phpize$ ./configure --enable-xdebug$ make && make installXdebug setup
The basic setting will be to simply connect the newly installed extension to the
php.ini file . In this file you need to check the presence of the line:
zend_extension = /path/to/xdebug.so
xdebug.default_enable = 0
xdebug.overload_var_dump = 0
If the line was not added to
php.ini or to one of the
included files, for example
/etc/php.d/xdebug.ini , then you need to do it manually. Then restart the web server.
From this point on, the site developers have at their disposal new xdebug features that can theoretically be used. With the first two directives, we specifically disable the function of the extended display of the function call stack when errors occur and the redefinition of the standard function
var_dump () . Despite the fact that this is a great feature that helps the development, it changes (albeit slightly) the behavior of the site code. We cannot go for it.
We start the analysis: Trace of function calls
The most important and useful method for diagnosing a site and identifying problem areas is the call tracing of functions. When entering any page of the site that we have chosen, the collection of statistics on the operation of functions will be launched, namely:
- The start and end time of the code
- The order of performance of functions
- Execution time of each function
- Memory consumption of each function
This invaluable information will help to reliably determine the "brake" section or parts of the code. In addition, it will be possible to determine the amount of memory consumed by a particular function.
Before starting the trace, we will define some variables that will help us control the collection of information and collect more of the necessary data during the execution of the code.
xdebug.collect_params indicates how much detail you need to collect information about the arguments of functions. 0 - minimum, no information is collected. 1 - information is collected on the number and type of arguments. 3 - information about the meaning of the arguments. 4 - complete information: type, name, argument value at the moment of transfer. The more information we want to get, the longer the trace will be performed.
xdebug.show_mem_delta determines whether or not to reflect the difference in memory consumption in the final report when each function is called.
xdebug.trace_enable_trigger enables or disables the ability to run trace on demand.
xdebug.auto_trace enables or disables automatic start of tracing every time the site pages are accessed.
xdebug.collect_assignments includes or does not include in the report information on the assignment of values ​​to variables.
xdebug.collect_includes includes or does not include information about connected files in the report.
xdebug.collect_return includes or does not include in the report information about the values ​​returned by the functions.
xdebug.trace_output_dir specifies the directory in which reports will be dumped.
xdebug.trace_output_name generates the file name for the report.
The remaining variables are associated with the visual display of the report, and we are not interested. The standard form looks quite acceptable for analysis.
The essence of the method will be to start the trace only at the moment when the administrator wants it. Turning on tracing on an ongoing basis is unacceptable because it leads to increased resource consumption during code execution, and also creates a lot of unnecessary reports. In order to indicate to the server that the administrator wants to start tracing the functions at the time the request is executed, he needs to pass the
XDEBUG_TRACE parameter in a GET or POST request, or set a cookie with this name. The latter method seems to be the most preferable, since a POST request is not always possible, and it is almost never necessary. And when using GET, there may be problems related to the fact that the address bar is often processed on the server using mod_rewrite or aliases before calling the PHP code. Therefore, our variable may simply not reach the addressee.
We will collect as much information as possible about the passed variables, the connected files and the difference in memory consumed between function calls. Add reports will be in
/ var / tmp . The remaining settings will be the default. As a result, we add the following lines in
php.ini :
xdebug.trace_enable_trigger = 1
xdebug.auto_trace = 0
xdebug.collect_params = 4
xdebug.show_mem_delta = 1
xdebug.trace_output_dir = / var / tmp
And restart the web server. For analysis, take the following code:
<?php require "config.inc"; require "class/db.php"; for ($i = 1; $i < 6; $i++) { show_num($i); } $v = array(); alloc_array(1024); $db = DB::Get("mysql", HOST, USER, PASS, NAME); $db->connect(); echo "finished"; function show_num($i) { ($i % 2) ? show_odd($i) : show_even($i); } function show_odd($i) { echo "odd: $i<br>"; } function show_even($i) { echo "even: $i<br>"; sleep(1); } function alloc_array($size) { global $v; for ($i = 0; $i < $size; $i++) { $v[] = $i; } } ?>
We will not go into its details now, although it is not difficult to guess that the code is of no value for use in cases other than test cases. Imagine that the user tried to open in the browser the page for which this code is responsible. The page was loaded for more than ten seconds, which is very long! Our goal is to understand why. Since the tracing of the code has already been configured accordingly, we only need to tell the server to enable it for our future request and to make this very request. As mentioned earlier, to do this, you must set the XDEBUG_TRACE variable in a GET or POST request, or pass it using a cookie. We chose the last option - the transfer using cookies.
Typically, variable cookies for a specific domain are set by the server in the client’s browser using the
Set-Cookie HTTP header or set already on the client’s side using JavaScript.
It’s rather difficult to get the header we need from the server: we don’t touch the site code, and making changes to the server settings requires it to be reset, which is not acceptable for periodic (non-one-time) operations. The most correct option is to set a cookie for our domain on your (client) side. We cannot force the server to give us the necessary javascript script to set the cookie. All for the same reasons as in the case of the Set-Cookie header. Therefore, we will set cookies in the browser on our own. In Google Chrome, you can do this by typing the script directly into the address bar:
javascript: document.cookie = "XDEBUG_TRACE = 1"
The execution context of this script should correspond to our site. That is, you must first open the site page, and then enter the command.
Having set the variable, we make a request to the page and wait for the end of the request. Next, on the server, look for the corresponding trace file in the
/ var / tmp directory . Let's look at the results:
TRACE START [2012-09-25 11:19:54]
0.0005 645152 +645152 -> {main} () /var/www/test.php call
0.0007 649296 +4144 -> require (/var/www/config.inc) /var/www/test.php:4
0.0007 649504 +208 -> define ('HOST', '10 .1.1.1 ') /var/www/config.incUE
0.0008 649536 +32 -> define ('NAME', 'db') /var/www/config.inc:4
0.0008 649568 +32 -> define ('USER', 'u0') /var/www/config.incimin
0.0008 649600 +32 -> define ('PASS', 'ps') /var/www/config.inc:6
0.0012 695728 +46128 -> require (/var/www/class/db.php) /var/www/test.phpiny
0.0013 694736-992 -> show_num ($ i = 1) /var/www/test.php:8
0.0013 694736 +0 -> show_odd ($ i = 1) /var/www/test.php:21
0.0013 694864 +128 -> show_num ($ i = 2) /var/www/test.php:8
0.0013 694864 +0 -> show_even ($ i = 2) /var/www/test.php:21
0.0014 694960 +96 -> sleep (1) /var/www/test.php:30
1.0033 694864 -96 -> show_num ($ i = 3) /var/www/test.php:8
1.0034 694864 +0 -> show_odd ($ i = 3) /var/www/test.php:21
1.0034 694864 +0 -> show_num ($ i = 4) /var/www/test.php:8
1.0034 694864 +0 -> show_even ($ i = 4) /var/www/test.php:21
1.0035 694960 +96 -> sleep (1) /var/www/test.php:30
2.0047 694864 -96 -> show_num ($ i = 5) /var/www/test.php:8
2.0048 694864 +0 -> show_odd ($ i = 5) /var/www/test.php:21
2.0048 695224 +360 -> alloc_array ($ size = 1024) /var/www/test.php:13
2.0057 843024 +147800 -> DB :: Get ($ type = 'mysql', $ host = '10 .1.1.1 ', $ user =' u0 ', $ pass =' ​​ps', $ db = 'db') / var /www/test.php:15
2.0057 843664 +640 -> absDB -> __ construct ($ host = '10 .1.1.1 ', $ user =' u0 ', $ pass =' ​​ps', $ db = 'db') / var / www / class / db. php: 10
2.0058 843664 +0 -> DB -> __ construct ($ host = '10 .1.1.1 ', $ user =' u0 ', $ pass =' ​​ps', $ db = 'db') / var / www / class / db. php: 36
2.0058 844000 +336 -> DB-> build () /var/www/class/db.php:19
2.0058 844016 +16 -> absDB-> connect () /var/www/test.php:16
2.0058 844368 +352 -> mysqli_connect ('10 .1.1.1 ',' u0 ',' ps', 'db') /var/www/class/db.php:47
11.0164 8432
TRACE END [2012-09-25 11:20:05]
The first and last lines of the report display the start and end times of the request, respectively. From this information it follows that the code was executed for 11 seconds. The report also displays the order of calls to all functions in the code, taking into account their nesting. The first column indicates the total execution time of the code in seconds at the time of the function call, the second column shows the memory consumption in bytes also at the time of the function call. The third column is the difference in memory consumption caused by the PREVIOUS function. The remaining columns show the name of the function, the file name and the line number in which it was called.
Let's try to understand what causes the slow work of the code. First of all, the long work of the
mysqli_connect () function, which takes almost 9 seconds, is striking. Obviously, there are problems with access to a remote server. Note that this function is called through several layers of abstraction and classes. For many frameworks, this is common. In addition, delays occur in the
sleep () function, which is called in the user function
show_even () .
As for memory consumption, we see a sharp jump in more than 140KB after calling the user function
alloc_array () , as well as a large memory allocation at the very beginning of the code execution.
We see all the necessary information about the passed arguments of all functions, so that the developer will be able to compare these reports with the source code.
Code profiling
It is worth mentioning another feature that xdebug provides for finding bottlenecks in the code, which the server administrator can use without making changes to the site code. This is profiling. Profiling is run in the same way as the function’s start-up function, with the exception of the variable cookie name. XDEBUG_PROFILE is used instead of XDEBUG_TRACE. As a result of profiling, we get a file with data that can be recognized by the
callgrind_annotate utility from the
vallgrind team , and can also be displayed graphically using the
KCacheGrind utility under KDE or
WinCacheGrind under Windows.
KCacheGrind among them is the richest in functionality.
You need to configure profiling on demand with directives similar to function directives:
xdebug.profiler_enable = 0
xdebug.profiler_enable_trigger = 1
xdebug.profiler_output_dir = / var / tmp