Hari ini belajar mendebug aplikasi PHP dengan Xdebug ini, lumayan rumit ada 5 step yang harus dipelajari
. Alhamdulillah part one udah sekarang next part yuhuiiii :
http://www.xdebug.org
http://devzone.zend.com/article/2803-Introducing-xdebug
Introducing xdebug
By: Stefan Priebsch
Other Articles in the Series
Part One: Introducing xdebug
Part Two: Tracing PHP Applications with xdebug
Part Three: Profiling PHP Applications With xdebug
Part Four: Debugging PHP applications with xdebug
Part Five: Creating Code Coverage Statistics with xdebug
This article is the first installment of a five-part series of articles covering xdebug, a free and open source swiss army knife tool for PHP developers. xdebug is a PHP extension created by Derick Rethans, one of the PHP core developers. This week, we will show you how to install xdebug and introduce you to some of the basic features. In the subsequent parts of this article series, we will have a closer look at one of xdebug’s main features, namely tracing, profiling, debugging, and code coverage.
Installing the xdebug extension
First of all, we need to install xdebug. As I write this article, the current version is 2.0.1. Since the internal PHP APIs may change between different PHP releases, you must make sure that the version of xdebug you are installing matches the PHP version you are using.
xdebug does not work with any PHP versions before 4.3, and will probably not yet work with PHP 6. This is not a real problem, however, since PHP 4 will reach its end of life in 2008, and PHP 6 will probably not be available before the end of 2008. This gives you enough time to get used to xdebug so you can use it as a helpful tool when it comes to migrating your PHP code to work with the next major or minor PHP release.
Installing on Unix
Before we dig into xdebug’s features, let us get the installation done. On Unix, you can try installing xdebug through PECL, the PHP extension community library. The PECL installation does not work on all systems, though. If the PECL installation does not work out for you, you must compile xdebug from source. But first, try the PECL installation:
pecl install xdebug
If the PECL installation does not work for you, you need to compile xdebug from source. In addition to a C compiler, you will need appropriate versions of the usual build tools (Autoconf, Automake and Libtool). If they are not already installed on your system, you can usually install them by running apt-get install build-essential on Ubuntu or Debian. When configuring xdebug, you will get an error when any required tool is missing. In this case, just install the missing tool, then configure xdebug again.
Furthermore, two helper programs, phpize and php-config that are a part of PHP, are required. If you have not compiled PHP from source, you will probably have to install the developer packages using your distribution’s package manager. On Ubuntu or Debian, you can install the PHP development tools using a command like apt-get install php5-dev.
Please note that phpize and php-config must match the PHP version you are using, so do not just copy them to your system from some other PHP installation. When your development tools are in place, you can download and compile xdebug:
wget http://xdebug.org/link.php?url=xdebug201
tar -xzf xdebug-2.0.1.tgz
cd xdebug-2.0.1
phpize
./configure –enable-xdebug –with-php-config=/usr/bin/php-config
make
cp modules/xdebug.so /usr/lib/apache2/modules/xdebug.so
The php-config path may be different on your system, and depending on the your Apache installation directory, you may need to copy xdebug.so to another directory. Instead of copying the file, you can of course create a link as well.
Installing on Windows
If you are a Windows user, you can download a compiled DLL from xdebug.org. Select the PHP version you are using and click the appropriate link in the Windows modules section in the right column of the page.
You must use the non-debug version of PHP with xdebug. If you have downloaded PHP from php.net, debugging should not be enabled. When in doubt, check the Debug Build entry in the phpinfo() output.
I would recommend putting the downloaded DLL into PHP’s extension directory ext, which should be a subdirectory of your PHP directory. You can put the DLL to any directory, provided that you state the full path to the DLL in php.ini.
Activating the xdebug extension
Now you have the xdebug extension ready, either as a shared object on Unix or a DLL on Windows. To activate xdebug, you must add an entry to php.ini. On Windows, use:
zend_extension_ts=”c:\php\ext\php_xdebug-2.0.1-5.2.1.dll”
On Unix, use:
zend_extension=”/usr/lib/apache2/modules/xdebug.so”
instead. The path to PHP’s extension directory or Apache’s module directory may differ on your system. Make sure you specify the full absolute path, not a relative path.
Please note that on Windows, we use zend_extension_ts, which means that a thread-safe extension is loaded, whereas on Unix, we a non-threadsafe extension is loaded. Depending on your system setup, you must decide for yourself wether you need a thread safe or non-thread-safe extension. If you are not sure wether your PHP installation is thread-safe, check the Thread Safety entry in the phpinfo() output.
You should not load any other Zend extensions while working with xdebug, because they would probably use the same internal mechanisms in the Zend engine, which usually calls for trouble. Not all Zend extensions work together with xdebug. Since you will probably use xdebug on a development system rather than a live system, this is no serious limitation. The most important rule is to not mix xdebug with other PHP debugger extensions.
Having restarted your web server because we changed php.ini, you can check the output of phpinfo() or run php -m at the command line. In each case, xdebug must be listed twice, once as a PHP extension, and as a Zend extension as well.
Take care when you are updating PHP with xdebug installed. If the internal APIs change between the PHP versions (which does not happen on every new version, but will certainly happen when you are close to a project deadline), the new version of PHP will probably not start or at least give you funny errors. If need be, you can always get away with disabling xdebug, at least temporarily, to get an updated PHP version to work and re-enable xdebug as soon as a new version is released that works with the newer API version.
There are quite some php.ini switches to configure xdebug, but most of them have sensible deafult values, so we can start using xdebug without worrying about configuration settings right now. We will take a look at the most important configuration settings as we need to.
Improved var_dump() output
Let’s face it: the most widely used PHP debugger is var_dump(). Given the dynamic nature of PHP, and the fact that PHP is a scripting language, there is nothing wrong in using var_dump() – I do it all the time. The drawback is that you have to modify your program to debug it. The more var_dump() statements you put into your code, the more likely it is that you’ll forget to remove them all once you have found and fixed your bug.
xdebug offers interesting alternatives to the extensive use of var_dump() statements for debugging your code, which we will get to know in the next and last article of this series. But for now, you’ll be glad to hear that xdebug even improves our beloved var_dump() debugger.
When the xdebug extension is loaded, the output of PHP’s var_dump() function is automatically beautified for better readability, as the following screenshot shows:
You can configure the way xdebug formats the var_dump() output by various php.ini settings. First of all, you can change the length of strings that xdebug displays. The default value is 512; longer strings are automatcally truncated.
Wether you want xdebug to display the full string depends on the situation, and the size of data you work with. If you work with big strings, the var_dump() output can get long and hard to read, thus is is a good idea truncate strings for better overview. On the contrary, if you are looking for a specific value in the output, you will probably want the strings to be displayed in full length.
To change the string length xdebug displays, add
xdebug.var_display_max_data=
to php.ini and restart your web server so that this setting takes effect. Alternatively, just like you can change various php.ini settings of the PHP core at script runtime by ini_set, you can add
ini_set(‘xdebug.var_display_max_data’, );
on top of your script to make sure the ini_set-command is executed before your first var_dump() is encountered, otherwise your runtime setting will not take effect. Configuring xdebug at runtime with ini_set saves you from restarting your web server every time you want to modify the xdebug configuration and makes you much more flexible.
You can also control the number of array elements or object properties that xdebug displays. This is done by modifying the xdebug.var_display_max_children setting, which defaults to 128. This value should be sufficient to display all properties of your objects, but if you work with large arrays, you might still need to increase this value.
If you work with nested objects and arrays, you may want to modify the xdebug.var_display_max_depth setting. This setting has a default value of 3, which means that three nested levels of array elements and object relations are displayed.
You can also dump the values of the superglobal variables using the xdebug function xdebug_dump_superglobals(). As the superglobals, especially $_SERVER, are arrays that contain many values, you must explicitly tell xdebug which array keys you would like to see. To do this, set xdebug.dump. where is one of GET, POST, SERVER, COOKIE, FILES, REQUEST, or SESSION. Use the array key(s) that you want xdebug to display as argument, using a comma-separated list if you want to display more than one key per superglobal variable. You can use * as a wildcard to display all keys, which can be useful for $_GET and $_POST.
Using
ini_set(‘xdebug.dump.SERVER’, ‘HTTP_HOST, SERVER_NAME’);
in your PHP script or setting
xdebug.dump.SERVER=HTTP_HOST, SERVER_NAME
in php.ini will display the value of $_SERVER['HTTP_HOST'] and $_SERVER['SERVER_NAME']. To display all GET parameters that were passed to the script, use
xdebug.dump.GET=*
By default, xdebug does not dump undefined values, but just skips the respective array keys. To force display of undefined values, set xdebug.dump_undefined to On. I would recomment turning xdebug.dump_undefined on, because it can be quite confusing if you.
You may wonder why xdebug supports configuring displaying select items of superglobal variables, when the same effect could be achieved with a number of var_dump() statements. First of all, you can have values of different superglobals displayed in one spot in a very readable way. Second, and more imporant, xdebug does not only show these superglobals when you call xdebug_dump_superglobals(), but also when any error occurs in your script, as we will see in the next section.
Better error messages
xdebug also improves the way PHP displays error messages by automatically displaying a stack trace along with every PHP error message, warning and notice. This stack trace lists the history of function calls that lead to an error. As PHP programs become more and more object-oriented, sometimes errors tend to show up deep inside libraries or helper objects. The stack trace allows you to quickly find out where the code piece that caused an error was originally called from.
Since version 5.0, PHP also has a native function debug_print_backtrace() that displays the stack trace, but you’d have to call this function explicitly each time an error occurs, which means that you would have to create a custom error handler just to display stack traces along with error messages. Also, the stack trace created by xdebug is easier to read than the output of the native PHP function.
Like PHP itself, xdebug only displays error messages when display_erorrs is set to On in php.ini. On a development system, this should be the case. If not, add
ini_set(‘display_errors’, ‘Off’);
to your script to change the setting at runtime.
Looking at this stack trace, you can see that the function foo() was called, which called bar(), which in turn called baz(). In addition to the name of the calling functions and where they are located in the source code, xdebug displays a timestamp and the amount of memory used by the script when the function was called.
In the previous section, we have already learned how to configure xdebug to display values of superglobal variables. In addition, you can configure xdebug to display the current values of local variables as well. To display local variables along with every error message, you need to add
xdebug.show_local_vars=1
to php.ini. You can use ini_set as well, which we won’t repeat mentioning from now on. Just keep in mind that you can do a lot of configuration at runtime, but you can obviously not change settings that affect work that is done before your script actually starts.
By the way, the php.ini settings xdebug.var_display_max_data, debug.var_display_max_children, and xdebug.var_display_max_depth that we used above to control the formatting of the var_dump() output also affect the format of variables that xdebug outputs in error messages. The same holds true for display of superglobals.
There is even more. With xdebug.collect_params, you can configure xdebug to display information about the parameters that were passed to a function. xdebug.collect_params takes a numeric parameter, with 0 meaning no additional information, and 4 meaning to display the variable name and always the full contents of each function parameter. A value of 3 will display name and parameter, but honoring the configuration settings mentioned above and thus truncating long variable content. A value of 1 will display the type and number of elements only, whereas 2 will in addition display the full information in a tooltip. Using a tooltip is very convenient for online viewing, but not really suitable for printing the information.
Here is the xdebug configuration used to create the following screenshot:
xdebug.show_local_vars=On
xdebug.dump.SERVER=HTTP_HOST, SERVER_NAME
xdebug.dump_globals=On
xdebug.collect_params=4
This screenshot was taken using the following xdebug configuration:
Please note that the more information you ask xdebug to collect during script execution, the more memory and computing time it will use, thus slowing the execution of your script. Make sure to only enable xdebug on development systems, and never on production systems. It is not a wise idea to display error messages as part of the HTML result on production systems, and it is even less of a good idea to enrich this information with stack traces and potentially variable values. Imagine your web site displaying your database username and password to everybody just because you use an uninitialized variable!
Since superglobal variables should not change at runtime, xdebug by default displays them only on the first error message, not in every error message. If you want xdebug to repeat dumping the global variables on every error, use
xdebug.dump_once=Off
Please note that the extended error display of xdebug does not work if you define a custom error handler using register_error_handler(). This is because xdebug internally uses the same mechanism. Should your scripts use a custom error handler, you can still use the function xdebug_get_function_stack() to output the stack trace in your custom error handler.
Object-oriented code uses exceptions when something goes wrong. Since an exception is not an error, xdebug will not display a stack trace when an exception is thrown, but only if that exception is uncaught. That is because an uncaught exception is a fatal error. To can configure xdebug to display a stack trace whenever an execption is thrown, use
xdebug.show_exception_trace=On
Recursion Limit
Another very useful feature is the fact that xdebug limits recursion depth, thus preventing endless recursion, which usually makes PHP segfault on Unix. On Windows, PHP even locks up the whole system when it falls into endless recursion, which is usually far worse than having PHP segfault.
xdebug prevents endless recursion by stopping the script once a pre-defined nesting depth is reached. This is, in effect, a limit of the stack size. Every recursive function call will increase the stack size, but also nested regular function calls increase the stack size. The default value is 100 and this feature is enabled by default. You can modify this limit, which might be necessary if you work with a script that works on deeply nested recursive data structures.
xdebug.max_nesting_level=
Bear in mind, however, that xdebug does not prevent endless loops with for, while or similar, since they do not increase the stack size. You can easily put an additional counter into such loops, however, that just kills the script after a ridiculously high number of repetitions. I would not recommend doing this in production code, though, since a program can can never tell wether a loop is endless.
Conclusion
xdebug is a small, but very useful tool in PHP development. It should be installed and activated on every PHP installation that is used for development. You should not run xdebug on a production system, though, because most features degrade performance. Personally, I would never want to miss xdebug a single day on my development system, and if it is just for the better formatting of the var_dump() output.
Today, we have just scratched the surface of what xdebug can do to make your every day’s life as a developer much easier. Next week, we will explore the tracing features of xdebug. Tracing helps you better understand your applications, and can be a replacement for putting loads of var_dump() statements into your code.
Make sure you check back here next week for the second part of this series of xdebug articles. Until then: Happy coding – with xdebug enabled.
http://devzone.zend.com/article/2871-Tracing-PHP-Applications-with-xdebug
Tracing PHP Applications with xdebug
By: Stefan Priebsch
Other Articles in the Series
Part One: Introducing xdebug
Part Two: Tracing PHP Applications with xdebug
Part Three: Profiling PHP Applications With xdebug
Part Four: Debugging PHP applications with xdebug
Part Five: Creating Code Coverage Statistics with xdebug
Welcome to the second installment of our five-part series of articles on xdebug, the PHP developer’s swiss army knife extension. The installation of xdebug and its basic features were covered last week. This week, we will explore the tracing feature of xdebug.
As programs, especially object-oriented ones, become more and more complex, the individual code blocks become smaller and smaller. Small code blocks make a program easier to maintain, since changes and additions can be introduced without changing existing code. If you look at an object-oriented PHP program for the first time, however, it seems that the program never does any actual work, but just comprises of a seemingly endless series of delegations.
Though it may sound awkward, this is a sign of quality of object-oriented code. Each piece of code should do exactly one thing and different concerns should be separated. As a rule of thumb, the source code of one method should fit on the screen without scrolling.
These small code bits make software maintainable, but on the other hand it becomes very difficult to understand the actual control flow of a program. While it is rather easy to understand a single class or method, one can have a really hard time trying to figure out the control flow of a complex object-oriented application just by looking at the code.
We could put numerous var_dump() statements into the code to get a feeling for what is going on. As we have learned last week, xdebug beautifies the var_dump() output to make it even more readable. Another option is to add logging to the application. Both techniques, however, require a modification of the source code and come at a performance penalty, since even when the logging is turned off, or commented out, these lines must be parsed. If PHP was a compiled language, we could wrap the var_dump() or logging code into a preprocessor macro, and thus ignore it in a production build. As soon as a problem turns up in a production build, we’d have to switch back to a debug build, which is not always easy.
Creating a trace log
Instead of manually adding logging code to the program code, we can have xdebug create a trace log while the program executes. A trace log is a list of every function and method call a PHP script has performed. The advantage is that every call appears in the trace log, not just the information we chose to log explicitly. On the other hand, the longer a trace logs get, the harder to read it becomes.
A trace log will usually differ with each program run, because the program’s execution path depends on the input data, and the input data fed to the program will probably vary from request to request. Imagine a web application that executes a command issued by a GET parameter. Depending on the command, different code is being executed, resulting in completely different trace logs for each run.
While looking at a trace log helps us understand the control flow of a program by showing one execution path, is is virtually impossible to visualize all execution paths of that same program, unless the program is really simple. One if statement introduces two possible execution paths to a program. With every further if statement, the number of (theoretically) possible execution paths multiplies by two. This is what makes testing software so difficult: in a large program, there are just too many possible execution paths to even think about them all.
But let us now get into creating a trace log. xdebug supports two different trace log formats. By default, trace logs are created in nicely formatted way so that the human user can easily read them. If you want to process the trace logs with another computer program, you should configure xdebug to log in the computer-readable format. The computer-readable format is easier to parse, because in contains no fixed size columns.
If you are using auto prepend scripts that you want to show up in the trace log, or want to create a full trace log of every script that is run, you can put
xdebug.auto_trace=On
xdebug.trace_output_dir=c:\data
into php.ini. As always, remember to restart your web server after changing php.ini.
The second setting defines the path where xdebug will store the trace logs. This path defaults to /tmp which is a good choice on Unix, but calls for trouble on Windows, since the directory c:\tmp (provided that the c: is the current drive) usually does not exist. So you either need to create this directory or specify another existing directory, otherwise xdebug will not be able to create trace logs since has nowhere to store them.
Because trace logs get large, the are not kept in memory, but are written to the disk. As you can imagine, the application will run a multitude slower with tracing enabled since every function and method call at PHP level results in a line written to the log.
Please note that it is not possible to start tracing at script runtime with
ini_set(‘xdebug.auto_trace’, ‘On’);
because auto prepend scripts are executed before your the main script is even started, which would prevent the prepend script to be traced.
Tracing a part of an application
As trace logs grow to a considerable size quickly, it is a good idea to only create a trace log for the “interesting” part of your application instead of – for example – creating a trace log of the application’s initialization process over and over again.
Let us have a look at how to create a trace log of a simple recursive function, the factorial function, that starts and stops trace logging at runtime. If you have already activated tracing in php.ini, you will see a warning telling you that tracing is already active for the script.
As you can tell from the path and filename of the trace log, I have run this example on Windows. If you work on Unix, you must use a different file name, of course. The function xdebug_start_trace starts the trace logging, and xdebug_stop_trace stops trace logging. When this program is run with the xdebug extension enabled, the following trace file is created:
TRACE START [2007-10-26 12:18:48]
0.0068 53384 -> fac() C:\www\fac.php:5
0.0069 53584 -> fac() C:\www\fac.php:10
0.0069 53840 -> fac() C:\www\fac.php:10
0.0070 54096 -> fac() C:\www\fac.php:10
0.0070 54376 -> fac() C:\www\fac.php:10
0.0071 54656 -> fac() C:\www\fac.php:10
0.0072 54936 -> fac() C:\www\fac.php:10
0.0072 55216 -> fac() C:\www\fac.php:10
0.0073 55392 -> xdebug_stop_trace() C:\www\fac.php:13
0.0237 55392
TRACE END [2007-10-26 12:18:48]
On each line, the first column shows the total execution time. The second column is the amount of memory the script was consuming at the time. The last column shows the file, line and function name. xdebug indents each function call according to the call stack level for better readability.
If you want xdebug to show an additional memory delta column in the trace log, add
xdebug.show_mem_delta=On
to php.ini. The memory delta is the difference between the memory size in the current line and the previous line. You can configure this setting at runtime with ini_set, but only before tracing has been started.
The function xdebug_start_trace has an optional second parameter. You can use one or more of the following three options: XDEBUG_TRACE_APPEND appends to an existing trace file, rather than overwriting the file. When XDEBUG_TRACE_COMPUTERIZED is set, xdebug creates a computer-readable trace log rather than human readable log. Using this parameter has the same effect as setting xdebug.trace_format to 1. The parameter XDEBUG_TRACE_HTML makes xdebug format the trace log as HTML.
The following screen shot shows an HTML-formatted xdebug trace log:
Adding more information to the trace log
We can also configure xdebug to log the parameters that were passed to a function. This is very helpful to better understand the program’s control flow, since you can see the values the program deals with.
Logging parameters in a trace log is controlled by the configuration setting xdebug.collect_params, which was already introduced in last week’s article. xdebug.collect_params takes a numeric parameter, with 0 meaning no additional information, and 4 meaning to display the variable name and always the full contents of each function parameter. A value of 3 will display name and parameter, truncating long variable content according to the formatting rules defined by xdebug.var_display_max_data, debug.var_display_max_children, and xdebug.var_display_max_depth that we also already covered last week.
Here is an excerpt of our trace log with the logging of function parameters activated:
0.0033 54320 -> fac($x = 7) C:\www\fac.php:6
0.0034 54432 -> fac($x = 6) C:\www\fac.php:11
Let us add even more information. Using
xdebug.collect_return=On
we can configure xdebug to also log the return values of each function. This makes the trace a little more difficult to read, but looking at the log of our factorial function, you can see how each recursively calculated factorial is being returned:
TRACE START [2007-10-26 12:30:04]
0.0133 55704 +48 -> fac($x = 7) C:\www\fac.php:8
0.0133 55840 +136 -> fac($x = 6) C:\www\fac.php:13
0.0134 56096 +256 -> fac($x = 5) C:\www\fac.php:13
0.0134 56352 +256 -> fac($x = 4) C:\www\fac.php:13
0.0134 56632 +280 -> fac($x = 3) C:\www\fac.php:13
0.0135 56912 +280 -> fac($x = 2) C:\www\fac.php:13
0.0135 57192 +280 -> fac($x = 1) C:\www\fac.php:13
0.0135 57472 +280 -> fac($x = 0) C:\www\fac.php:13
>=> 1
>=> 1
>=> 2
>=> 6
>=> 24
>=> 120
>=> 720
>=> 5040
0.0140 57648 +176 -> xdebug_stop_trace() C:\www\fac.php:26
0.0140 57648
TRACE END [2007-10-26 12:30:04]
As you can see, we have also turned on xdebug.show_mem_delta in this example.
Naming the trace log
Until now, we have explicitly defined the name of our trace log file. This does not always make sense, though. If you need separate trace logs depending on input data or the application state, for example, it is better to have xdebug automatically pick a name. xdebug can pick a name for your trace log, regardless of wether you use the xdebug.auto_trace setting or the xdebug_start_trace() function to activate tracing. In the latter case, just pass an empty argument list to the function, and xdebug will automatically choose a trace file name.
The file name of an xdebug trace always starts with trace. and has the extension .xt. The name part between the two dots defaults to the CRC checksum of the working directory. By the setting xdebug.trace_output_name, you can define another file name. xdebug.trace_output_name takes a string as argument that can contain various format specifiers. The most important format specifiers are:
* %c for the CRC checksum of the current working directory
* %p for the process id
* %r for a random number
* %u for a timstamp with microseconds
* %H the $_SERVER['HTTP_HOST']
* %R the $_SERVER['REQUEST_URI']
Using a clever combination of these format specifiers, you can have xdebug create a number of trace logs and still keep them separate even in complex scenarios like with virtual hosts or concurrent request.
In most cases, you will want to create a trace log for one single run of a script. Therefore, xdebug overwrites existing trace logs of the same name by default. You can configure xdebug to append to an existing file by setting
xdebug.trace_options=1
in php.ini. This might be useful when you tunnel all requests of your application through one index.php file or if you want to trace multiple programs runs into one file because you use different input data for each run. I would recommend keeping a separate trace log for each program run. It is easy to keep them separate by choosing a clever name using the xdebug.trace_output_name setting.
If you need to know at script runtime which trace log name xdebug has chosen, you can call the function xdebug_get_tracefile_name((). You might want to include this name in the script’s output or save the generated HTML for later reference. Especially when errors occur, it can be very useful to have access the actual output the program has produced when analyzing the trace log.
Conclusion
The function tracing feature of xdebug is very useful. You can create logs in your application without adding a single call to a logger function. Since all function calls are logged, you will always have the relevant information in the log file, whereas with a explicit logger function, you might end up adding more and more logger calls until you finally have the relevant information in the log.
You can use tools like grep to find relevant entries or sections in the trace logs, or even write a small PHP program that parses the trace log for you. Tracing is no replacement for a debugger, though. We will look into the debugging features of xdebug in the fourth part of this series.
You should never activate trace logging on a production site, because that would absolutely kill your performance as one line has to be written to a file for every function call in your PHP script. That said, you should never install xdebug on a production server, not because it is not stable or insecure, but just because you don’t want somebody to accidentally turn on trace logging, profiling, or even debugging.
This concludes this’s weeks dose of xdebug, thanks for joining in on the second installment of this five-part article series on xdebug. Next week we will explore how to profile PHP applications with xdebug. Profiling an application is the first step in optimizing it, because you need to know where your application spends most time, before you can figure out how to make your application faster.
So, make sure you check back here next week for the third part in this series of xdebug articles. Until then: Happy tracing – with xdebug enabled, of course.
Ya sekarang part three http://devzone.zend.com/article/2899-Profiling-PHP-Applications-With-xdebug :
Profiling PHP Applications With xdebug
By: Stefan Priebsch
Other Articles in the Series
Part One: Introducing xdebug
Part Two: Tracing PHP Applications with xdebug
Part Three: Profiling PHP Applications With xdebug
Part Four: Debugging PHP applications with xdebug
Part Five: Creating Code Coverage Statistics with xdebug
Welcome back to the third installment of this five-part series of xdebug articles on Zend DevZone. By now, you should at least have tried out xdebug on one of your development systems – if not, you should really do so today. xdebug is a free and open source PHP extension that supports tracing, profiling, debugging, and creating code coverage statistics. I like to call it the PHP developer’s swiss army knife.
The first article described how to install and configure xdebug, and showcased some basic features that are really useful, like beautifying the var_dump() output, or displaying a stack trace with error mesasges. In the second part, we have explored xdebug’s tracing feature. A trace contains each function and method call a script, the execution time, and optionally also the memory size, the parameter values passed to a function, and even the return values. A trace log can help you understand the control flow of a complex program, or even act as a replacement for adding explicit logging. Instead of trying to log all events, you can selectively turn on and off the trace logging and use tools like grep or a small PHP script to analyze the trace log.
In this week’s issue, we will have a look at profiling. Profiling is a feature that seems to be similar to trace logging at first sight. Profiling logs, however, are not meant for human redability, and are not meant to visualize the program’s control flow, but provide the data for a statistical analysis of the program run.
Creating a profiling log
Here is a short excerpt from a profiling log generated by xdebug:
fl=php:internal
fn=php::define
106 3
fl=C:\www\drupal\includes\bootstrap.inc
fn=require_once::C:\www\drupal\includes\bootstrap.inc
1 648
cfn=php::define
calls=1 0 0
13 6
cfn=php::define
calls=1 0 0
18 4
cfn=php::define
calls=1 0 0
23 2
As you can see, a profiling log is not really human-readable. We will have to use additional tools to visualize and analyze the profiling data xdebug has provided us with. Basically, profiling is about counting how many times a statement has been excecuted and how much execution time it took.
Creating a profiling log seriously degrades your PHP script’s performance, like creating a trace log does, because several lines have to be written to a file for each function or method call. Therefore, you should never profile on a production system. There are rare cases when you absolutely need to profile on the live system, In that case, keep in mind that you should be careful in mixing xdebug with other Zend extensions that might be used on your production system, like loaders, optimizers, or caches.
To have xdebug write a profiling log, add
xdebug.profiler_enable=On
to php.ini. As you know by now, you must restart your web server for the modified php.ini to take effect.
Please note that you cannot enable profiling at script runtime with ini_set, because this would prevent any auto prepend scripts from being profiles.
Since the profiling log is designed to be machine-readable, there are no additional configuration settings that allow displaing additional information in the profiling log, like there are for the trace log. But there are some general configuration settings that affect profiling. Most of them are similar to settings we have already used together with trace logging.
First of all, xdebug writes profiling logs to /tmp by default, which causes problems on Windows because this directory usually does not exist. You should therefore always specify an existing output directory in php.ini on Windows, for example:
xdebug.profiler_output_dir=”c:\traces”
By default, xdebug overwrites an existing profiling log. You can configure xdebug append to an existing profiling log instead of overwriting log by adding
xdebug.profiler_append=On
to php.ini. Sometimes, however, you don’t want to profile all files, yet turning on the profiler at runtime is problematic because of the auto_prepend files. Instead of permanently turning on the profiler, you can add
xdebug.profiler_enable_trigger=On
to php.ini. Now you can enable and disable profiling by passing the special GET or POST parameter XDEBUG_PROFILE to a PHP script. This will turn on profiling just for the one PHP script that receives the parameter. You need not set a value for XDEBUG_PROFILE, it is sufficient to append the parameter to the URL: test.php?XDEBUG_PROFILE.
Naming the profiling log
The default file name of an xdebug profiling log is cachegrind.out. plus the process id. Just like you can change the trace log name in php.ini, you can change the profiling log name as well with xdebug.profiler_output_name. This setting takes a string as argument that can contain various format specifiers. The most important format specifiers are:
* %p for the process id
* %r for a random number
* %u for a timstamp with microseconds
* %H for the value of $_SERVER['HTTP_HOST']
* %R for the value of $_SERVER['REQUEST_URI']
* %s the name including full path, with slashes converted to underscores
Please note that the %s modifier is only available for xdebug.profiler_output_name, not for xdebug.trace_output_name. If you want to know the current profiling log filename, you can call the function xdebug_get_profiler_filename(), which returns the file name as a string.
Analyzing xdebug profiling logs
We have already mentioned above that we will need an additional tool to visualize the data from the xdebug profiling logs. xdebug’s profiling logs are in a Cachegrind compatible format. Cachegrind is a cache time profiler that is part of the Valgrind suite, a suite of tools for debugging and profiling Linux software Cachegrind was originally used to locate cache misses, memory references and the instructions executed by a program. Another tool of the Valgrind suite, Callgrind, draws call graphs. A call graph shows which how functions are called by other functions. In the PHP context, we can use these tools to visualize and analyze profiling logs generated by xdebug.
The tool commonly used work with xdebug profiling data is KCachegrind. KCachegrind is freely available under GPL, but runs only on Unix. Luckily, there is a similar tool for Windows, WinCachegrind, which is also free software. Let us have a look at the Windows version first.
WinCacheGrind: analyzing profiling logs on Windows
The current version of WinCachegrind is 1.0.0.12. This version dates back to 2005, which shows that WinCachegrind is not being very actively maintained. If you look at Wincachegrind’s release notes, the authors themselves mention that the program is buggy shows some strange behaviour.
I would therefore recommend using KCachegrind in a virtual machine running a recent Linux distribution like Ubuntu. There are various free virtual machines products available for Windows, so there is no additional cost for setting up a dedicated virtual machine just for analyzing profiling data. If you cannot use Unix or a virtual machine for whatever reason, you can still go with WinCachegrind, at least for a basic analysis of profiling data. WinCachegrind does, however, not support drawing call graphs, like KCachegrind does.
The installation of Wincachegrind is extremely easy. Start the installer by double-clicking, accept the license, and you are all set. Now you can start the program and open one of the cachegrind profiling logs created by xdebug.
By clicking on the clock or sigma icon, you can toggle between display of absolute times in milliseconds and a percentage view. The Percentage view shows how many percent of the whole code block’s total execution timeeach function call has used . These percentages do usually not add up to 100% because the code in the code block itself also takes some time to execute.
Two very useful settings are Profiler -> Hide Fast Functions and Profiler -> Hide Library Functions. The first switch hides functions that do not take a long time to execute. These are most of the built-in PHP functions, as they do not account for a major part of the script runtime, but can be small and fast user functions as well.
The second setting, Profiler -> Hide Library Functions hides all built-in PHP functions. When you toggle one of both of these switches, you will see less unimportant data, which allows you to focus on the those parts of the code that are a better basis for optimizations.
The main windows of WinCachegrind has two tabs, Line by line and Overall. Both tabs show the same information, but Overall aggregates the information for a better overwiew. Self time lists the execution time of the code in the current block, whereas Cumulative time (Cum.) also takes the execution time of all functions called from this block into account.
KCacheGrind: analyzing profiling logs on Unix
The Unix version KCachegrind offers much more functionality than WinCachegrind. KCachegrind visualizes the profiling data and draws call graphs.
First of all, we need to install KCachegrind. The current release is 0.4.6. A newer version, 0.10.1, is available, but this version seems to be part of the full Valgrind package only.
If possible, use your system’s packet manager to install the KCachegrind package. KCachegrind uses GraphViz to draw the call graphs, so you must also install the GraphViz package if your packet manager does not automatically install it as a required dependecy.
If you cannot find binary packages of KCachegrind, you can still compile KCachegrind by yourself. After downloading the source code, run
./configure –prefix=/opt/kde3
make
make install
As you can see, the path to the KDE libraries on your system is required. To configure the KCachegrind sources. If you are not sure where the KDE libraries are located on your system, use
kde-config –prefix
to display the location of the KDE libraries.
After installation, you can start KCacheGrind from the command line with
/opt/kde3/bin/kcachegrind &
The tabular data displayed by KCachegrind is similar to WinCachegrind. You can also switch between absoulte time and percentage values. Some of the features KCachegrind offers do not apply to PHP code, though. The following screenshot shows a call graph of phpMyAdmin in KCachegrind:
As you can see, the major part of execution time in this example is taken up by common.inc.php. The following screenshot shows a visualization of the functions called by common.inc.php:
In this code block, two more require_once statements are executed, which take about one half of common.inc.php’s execution time to complete. By double-clicking on one of the boxes, you can drill down into the application’s profiling data.
Optimizing Your Code Based on Profiling Data
Always profile your application before you start optimizing. You might be tempted to start optimizing a program where you think the optimization makes sense – but in most cases our guesses are just wrong. Optimizations, in gemeral, only make sense when a part of the program is responsible for a major part of the overall execution time. There is no point in optimizing a program part that but accounts only for a few milliseconds of execution time. Even if you speed up such a program part by more than 50%, the overall performance increase is not measurable.
Yet, if multiple instances of of the program are run concurrently, as it is the case on a high-load web server with multiple Apache processes serving the requests, you still might want to optimize parts of your program that do not account for a major part of the overall execution time. In this case optimizing will not make serving one individual request much faster, but allow your server to handle a higher load since serving each individual request requires less resources.
When you look at the execution times in your profiling data, keep in mind that the absolute figures are less important than the the relative ratio of the measured times. Measured on another system, the absolute times can be very different. But before you get into code optimization, there are a few things to keep in mind.
An important rule of thumb in optimization is that you should try to reduce the number input/output operations first. Some I/O always takes very long in comparison to quite some computation. Thus reducing I/O operations can be a very efficient way of speeding up your programs. Removing one I/O call can give you more speedup instantly than spending a couple of hours optimizing code. So you should ocus on I/O first, before you go for the code.
Still, before you invest time in optimizing your code, you should consider scaling the hardware. You can buy a bigger server or take benefit from PHP’s shared nothing architecture that allows you to easily increase performance by adding another server and splitting the load between the two. Developer time is rather expensive compared to the price of a new server. And if you scale by hardware, you can be sure that you get an immediate speedup in return, without having to touch the existing PHP code. When a developer spends one or two days optimizinig code, you can never tell how much speedup the optimization will gain. And, on top of that, you can’t really be sure that no bugs or security risks were introduced inadvertedly.
Especially when dealing with web sites, statifying content is the best possible performance optimization. Imagine a high traffic site where a PHP script creates the home page on every request, pulling information from a database or XML files. If the page changes every couple of minutes, you can automatically re-create the static copy. Even if statifying the whole page is not possible, because you need to include personalized content, you can still statify certain content blocks.
Yet another level of performance optimization that does not require changing the PHP code is possible. As we know, PHP is an interpreted language. This means that at runtime, every script is being translated to an intermediate code that is executed. This translation is repeated every time a script is being called. This makes PHP – by design – slower in comparison to compiled languages like C or Java, where no code parsing occurs at runtime. For PHP, you can use opcode caches to save and reuse the intermediate code, thus making the script faster since the pre-parsed intermediate code can be reused, so the PHP code does not have to parsed every time a script is executed.
All this is not to say that there is not a time and place for optimizing PHP code. Some clever code optimizations can increase the performance a lot. Bear in mind, however, that changing the code always has a risk of introducing bugs and security issues. Even if you just change the behaviour in a harmless way, existing tests may fail, requiring care and attention. Also, do not forget that optimized code is more difficult to read.
Conclusion
Creating – and visualizing – a profiling log is the most important precondition for PHP code optimization. You need to know where your application spends the most time, and start optimizing right there, otherwise you will potentially waste your time and energy by optimizing a program in the wrong spot. So use profiling to find out where your application spends most time and start optimizing right there – if you have at least considered other measures.
Next week, we will explore the debugging features of xdebug. xdebug can turn your PHP-enabled server into a remote debugging server. Using a compatible debug client like Eclipse PDT, you can debug your script without changing a single line of code, set breakpoints, step through the code, or watch how and when variable values change.
So, make sure you check back here next week for the fourth part in this series of xdebug articles. Until then: Happy profiling with xdebug!
The next part…tapi dari 1-3 apa ya yang cha dapet? hmmm…bisa install n ngegabungin xdebug di wamp, bikin tracing code yang diexecute di satu file, profiling file juga…hmmmm…tujuannya di part four neh
ayooo mulai :
Debugging PHP applications with xdebug
By: Stefan Priebsch
Other Articles in the Series
Part One: Introducing xdebug
Part Two: Tracing PHP Applications with xdebug
Part Three: Profiling PHP Applications With xdebug
Part Four: Debugging PHP applications with xdebug
Part Five: Creating Code Coverage Statistics with xdebug
Welcome to the fourth installment in our five-part series of xdebug articles on the Zend DevZone. This week, we will explore debugging PHP code with xdebug, the swiss army knife for PHP developers. In this article, we assume that you have xdebug installed on your system. If you haven’t, the first article of the series explains how to install and configure xdebug.
Debugging software is not exactly a fun job for developers. The most widely used debugger for PHP still seems to be a var_dump() statement, possibly in conjunction with die() to halt program execution at a certain point. While there is nothing wrong using var_dump() statements in itself, you still need to change the program code to debug a PHP script. And worse, after you have finished debugging, you must remove all var_dump() statements again (well you should, at least). It may well be that a few days later you’ll find yourself adding the very same var_dump() statements to your code again because you need to go hunting another bug.
Of course, you could just comment out the var_dump() statements, but that looks really ugly in the code. Another option would be to wrap the var_dump() in conditional clauses, and only execute them when, say, a constant DEBUG is defined. This affects performance, because even if the var_dump() statements are not executed, the conditional clause must be executed. And besides, it looks even uglier in the code.
As we have already learned in the second articles of this series, having xdebug create a trace log might be a better option in this case, because you do not have to change the program code. But a trace log, even if only created for a part of the application, provides us with a lot of information that may not be necessary for the debugging process, so using a debugger is a much better solution. A debugger allows you to pause program execution at any time, inspect or modify the current variable values, and then continue the program. By executing a program step by step, you can closely watch the code doing its job, which hopefully helps you to find out quickly where things go wrong.
Beyond var_dump, debugging in PHP has been problematic for a long time, at least if you were not willing to spend money on a commercial IDE that supports debugging. With xdebug, open source debugging of PHP code has – in theory – been possible for quite a while now. I say theoretically because until recently no good and free debug client for xdebug was available for both Windows and Unix.
This fall, the problem of having no release-quality cross-platform xdebug client has been solved with the release of Eclipse PDT. Eclipse PDT is a free IDE for PHP supporting xdebug out of the box. So, without further ado, let us install Eclipse PDT to get started with debugging.
Installing Eclipse PDT
Eclipse PDT (PDT is short for PHP Development Tools) is written in Java, and thus works on most platforms where a Java Runtime Environment is present. If you have no Java Runtime Environment installed on your computer, you can download your copy from www.sun.com.
You can download a ready-to-go-package of Eclipse PDT from http://www.eclipse.org/pdt. You should choose the appropriate version for your platform, though all packages are basically only compressed archives of the necessary files to run eclipse. Still, the Windows version comes with an .exe file that starts Eclipse, which is way more user-friendly than calling the Java classes directly.
As I write this article, the current Release Build version of Eclipse PDT is R20070917. Choose the latest available version and have some coffee ready, because the Eclipse PDT download is over 100 MB in size. But since it is a Java application, having a nice cup of coffee while you wait for the download to complete should be suitable. Once the download has completed, unpack the downloaded archive and you are ready to start Eclipse PDT.
How debugging works
Before we get into the configuration of xdebug and Eclipse PDT, let us have a look at how PHP debugging with xdebug actually works. This will help you better understand the configuration described below.
When debugging is enabled in php.ini, xdebug controls the program execution in PHP, which basically means that xdebug can pause and resume program execution at any time. When program execution is paused, xdebug can retrieve information about the current program state, like reading variable values. It is even possible for xdebug to change the value of a variable, then continue the script execution with a modified value.
The xdebug extension is a server, expecting client connections at a certain configurable port. There are two protocols that can be used to communicate between the xdebug client and the xdebug server, GDB and DBGp. GDB is an older protocol, which has been superceded by DBGp. By sending commands to the xdebug server, the xdebug client acts as a sort of remote control for PHP, telling PHP to pause execution, execute one step, or to continue the program execution. The client is usually embedded into an editor or the IDE (in our case, into Eclipse PDT), so you will not have to deal with the debug protocol itself.
The PHP server with xdebug can run on a another system than the one running the xdebug client. That is why xdebug is called a remote debugger. For simplicity, we will set up the debugging server and client on the same computer.
There are two different modes of starting a debug session with xdebug. They are controlled by the php.ini setting xdebug.remote_mode. The default setting is req, which makes xdebug always connect to the debug client when a script is started. If you want xdebug to only connect to the debug client on a breakpoint or an error in the script, you can set xdebug.remote_mode to jit. I would recommend keeping the default setting, which you can achieve by putting no xdebug.remote_mode setting into php.ini.
To actually start a debug session, you must pass a parameter XDEBUG_SESSION_START to the script by GET, POST, or cookie. The value of this parameter is the debug session name, which should be unique at a given point in time, so that xdebug can distinguish different debug sessions running concurrently. To end a debug session, you need to pass XDEBUG_SESSION_STOP to the script.
Instead of manually dealing with the starting and stopping of debug sessions, you can install a firefox plugin that allows you to conveniently start and stop a debug session with a mouse click.
Using Eclipse PDT, you will not even have to worry about the browser plugin, as the IDE takes care of passing the appropriate parameters to the browser. xdebug also requires setting of a IDE key, which you also do not have to worry about, because Eclipse sets it for you.
Configuring xdebug
Now let us configure xdebug debugging. Add the following settings to php.ini:
xdebug.remote_enable=On
xdebug.remote_host=”localhost”
xdebug.remote_port=9000
xdebug.remote_handler=”dbgp”
Make sure you add these lines after the zend_extension_ts line that loads the xdebug extension. The first entry enables the debugger. The second entry defines that the debug client runs on localhost – this could be any valid DNS name or IP address, if the client and the server were not on the same computer.
The third setting defines the port the xdebug server expects the client to listen on (which, strictly speaking, makes the client a server, but let us not get confused about this). By default, the port is 9000. This port is configured in Eclipse by default, and if there is no compelling reason to do otherwise, you should keep the default port. If you want to change the port, keep in mind that you have to configure the same port number in Eclipse and php.ini.
Also, make sure that no firewall gets into your way. When you start Eclipse, you might see a warning that Java is trying to set up a server, bind to a port, access the network, or perform some obscure potentially dangerous action. Of course, it’s not dangerous, it’s just the xdebug client trying to listen at port 9000. If you have problems with debugging, check if there are any firewalls between the debug server and the debug client that might block port 9000.
Last but not least, we have to tell xdebug which protocol the client speaks. Eclipse PDT speaks DBGp, so you may not change this setting.
Configuring Eclipse PDT
To configure Eclipse PDT, start Eclipse by double-clicking the Eclipse executable. Create a new PHP project. Let us name the project debug_test. Now, create a file debug.php in the project, add some code, then save the file.
Now let us configure Eclipse for debugging with xdebug. First of all, we will configure Eclipse to launch projects in an external browser instead of its internal browser. When an external browser is configured, all debugging sessions will be launched in an external browser as well. Using an external browser is not absolutely necessary, but I prefer to work with Firefox instead of Eclipse’s internal browser. Choose Window from the menu bar, then select Preferences (see the following screenshot). Expand the General subtree, and click Web Browser. Now, select the radio button Use external browser and click Apply.
Eclipse PDT supports the Zend debugger and xdebug. By default, the Zend debugger is activated. To change this setting to xdebug, expand the PHP subtree and the Debug subtree of PHP. Then, change PHP debugger to Xdebug and click Apply.
Now, choose Run from the menu bar and click the entry Open Debug Dialog. Then, double click PHP Web Page to create a new debug configuration.
You will see a window with three tabs, Server, Advanced, and Common. When not already selected, choose Xdebug as Server Debugger. In the File / Project text field, you must enter the path to the script you want to debug. This path must be relative to your workspace. On my system, this is /debug_test/debug.php. Click Browse and select the file debug.php in the debug_test directory.
Eclipse needs to know the URL matching the script filename and path you just entered. This is required to highlight the currently executed line in the source code. The URL text field shows the URL that is mapped to the filename. By default, the URL text field deactivated because the Auto Generate checkbox is activated. If the displayed URL does not match the URL you would type into your browser to see the script you have specified in File / Project, uncheck Auto Generate and enter the correct URL in the URL text field. If thg script to debug requires any GET parameters, you can append them to the URL given here.
Do not forget to click Apply to save the changes. The following screenshot shows how the debug configuration looks like on my system:
Change to the Advanced tab and make sure that Open in Browser and the radio button Debug All Pages both are checked. Now you can close the configuration window and start a debugging session.
Debugging a PHP script
To start debugging a PHP script, choose Run from the menu bar and select Debug. You can shortcut this by pressing the F11 key. Eclipse will ask you wether you want to open the debug view. You should tick the checkbox to remember this setting, otherwise you will be asked the same question every time you start debugging.
The following screenshot shows Eclipse’s debug view of my debug.php script (which contains some pointless code):
Eclipse has opened your system’s default browser (or whatever browser you have configured it to open). You will not see output in the browser window yet, because Eclipse by default pauses script execution on the first line of the script, as if a breakpoint were set on this line. If you want to disable this behaviour, uncheck the Break at First Line checkbox in the Breakpoint section from the debug dialog configuration window.
As the screenshot shows, you can see the source code of the debugged file, with the currently executed line marked with an arroq to the left of the line number. In the above right area, you can choose between different tabs. The Variables tab shows the current values of all variables in the current scope. The superglobal variables are valid in every scope, so they are always displayed. The Breakpoints tab lets you view and edit all breakpoints in your script. Eclipse will remember any breakpoints you have set on the code, even if you close and restart Eclipse.
You can now continue program execution until the next breakpoint is reached, execute just one step, or step into the next function, or out of the next function scope by clicking the appropriate icons in the Debug top left area of the window. Stepping is useful when you have located the problem area in your code and want to watch closely what is happening. You will see the variable values change with each step.
Changing Variables at Runtime
You can even change variable values during script runtime. While this does not really fix a bug, it might be useful to provoke certain errors without modifying the source code. To change a variable, click the current value, modify it and press Enter.
Breakpoints
A breakpoint pauses script execution and allows you to inspect variable values, then continue the program. The program execution is also paused if an exception occurs in your PHP code. To set a breakpoint, right-click a line number in the source code, then choose Toggle Breakpoints from the context menu. You can remove breakpoints in the same way, or remove them in the Breakpoints tab by right-clicking a breakpoint and selecting Remove from the context menu.
You can even add a condition to a breakpoint. Conditional breakpoints will only pause the program execution when the given condition is met. This is can be very useful when the same piece of code is executed multiple times with different parametrization. To add a condition to a breakpoint, right-click the breakpoint icon to the left of the line number in the source code view. Choose Breakpoint Properties. You can remove conditions in the same way or by right-clicking a breakpoint and selecting Set Condition from the context menu in the Breakpoints tab.
Check Enable Set Condition and enter a condition in PHP code into the text field. In my debug.php, the function test() is called on line 11 and a breakpoint is set on this line. By adding the condition $a != ” xdebug will only pause program execution on this line when the local variable $a is non-empty at the time the breakpoint is reached.
To end a debugging session, highlight Remote Launch in the top left pane, then click the Terminate icon which is located between the Run and the various Step icons. If you had Eclipse run the script in an external browser, you need to close the browser window manually.
Conclusion
Remote debugging is a great interactive and non-intrusive way of finding bugs in your PHP scripts. Instead of putting var_dump() statements into your code, or working your way through a long trace log including parameter and return values, debugging gives you a macrosopic view of critical areas in your code.
Next week, we will explore creating code coverage statistics with xdebug. To aggregate the statistics and create a nicely formatted HTML report of the code coverage in our projects, we will also use phing and PHPUnit. So, make sure you check back here next week for the fifth and final part in this series of xdebug articles. Until then: Happy debugging – with xdebug, of course.
Alhamdulillah dah berhasil di part four neh
…sekarang bisa debug pake eclipse untuk php horeeee…:D…lanjut next part ga yach? nanya dulu ah dah tercapai maksud dan tujuannya belum neh ![]()
Ternyata udah kata TL tinggal bikin dokumentasinya semangat…









