summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--LICENSE19
-rw-r--r--Makefile4
-rw-r--r--README.md264
-rw-r--r--php-pecl-memprof.spec166
4 files changed, 453 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ed3a21c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2013 Arnaud Le Blanc
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..13af741
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,4 @@
+SRCDIR := $(shell pwd)
+NAME := $(shell basename $(SRCDIR))
+include ../../../common/Makefile
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2d5a721
--- /dev/null
+++ b/README.md
@@ -0,0 +1,264 @@
+# php-memprof
+
+php-memprof profiles memory usage of PHP scripts, and especially can tell which
+function has allocated every single byte of memory currently allocated.
+
+This is different from measuring the memory usage before and after a
+function call:
+
+``` php
+<?php
+
+// script 1
+
+function a() {
+ $data = file_get_contents("huge-file");
+}
+
+a();
+
+$profile = memprof_dump_array();
+
+```
+
+In script 1, a before/after approach would designate file_get_contents() as huge
+memory consumer, while the memory it allocates is actually freed quickly after
+it returns. When dumping the memory usage after a() returns, the memprof
+approach would show that file_get_contents() is a small memory consumer since
+the memory it allocated has been freed at the time memprof_dump_array() is
+called.
+
+
+``` php
+<?php
+
+// script 2
+
+function a() {
+ global $cache;
+ $cache = file_get_contents("huge-file");
+}
+
+a();
+
+$profile = memprof_dump_array();
+```
+
+In script 2, the allocated memory remains allocated after file_get_contents()
+and a() return, and when memprof_dump_array() is called. This time a() and
+file_get_contents() are shown as huge memory consumers.
+
+## How it works
+
+See [INTERNALS.md][7]
+
+## Dependencies
+
+ * [Judy Library][3] (e.g. libjudy-dev or judy package)
+ * C Library with [malloc hooks][1] (optional; allows to track persistent allocations too)
+
+## Install
+
+### Using PECL
+
+ pecl install memprof
+
+### Manually
+
+Download the source and run the following commands in the source directory:
+
+ phpize
+ ./configure
+ make
+ make install
+
+## Loading the extension
+
+The extension can be loaded on the command line, just for one script:
+
+ php -dextension=memprof.so script.php
+
+Or permanently, in php.ini:
+
+ extension=memprof.so
+
+## Usage
+
+Memprof can be enabled during script execution by calling ``memprof_enable()``.
+
+Then the memory usage can be dumped by calling one of the ``memprof_dump_``
+functions. Both tell which functions allocated all the currently allocated
+memory.
+
+Example:
+
+```
+<?php
+
+if (function_exists('memprof_enable')) {
+ memprof_enable();
+}
+
+do_some_work();
+
+if (function_exists('memprof_enable')) {
+ memprof_dump_callgrind(fopen("/tmp/callgrind.out", "w"));
+}
+```
+
+### memprof_enabled()
+
+Returns whether memprof is enabled.
+
+### memprof_enable()
+
+Enables memprof and start tracking memory allocations. Note: any memory
+allocation made before this call is ignored.
+
+### memprof_disable()
+
+Disables memprof and forget previous allocations.
+
+### memprof_dump_callgrind(resource $stream)
+
+The memprof_dump_callgrind function dumps the current memory usage to a stream
+in callgrind format. The file can then be read with tools such as
+[KCacheGrind][2] or [QCacheGrind][6].
+
+``` php
+<?php
+memprof_dump_callgrind(fopen("output", "w"));
+```
+
+Here is a KcacheGrind screenshot:
+
+![KCacheGrind screenshot](http://img820.imageshack.us/img820/5530/screenshot3kve.png)
+
+### memprof_dump_pprof(resource $stream)
+
+The memprof_dump_pprof function dumps the current memory usage to a stream in
+[pprof][4] format.
+
+``` php
+<?php
+memprof_dump_pprof(fopen("profile.heap", "w"));
+```
+
+The file can be visualized using [google-perftools][5]'s [``pprof``][4] tool.
+
+Display annotated call-graph in web browser or in ``gv``:
+
+```
+$ pprof --web profile.heap
+$ # or:
+$ pprof --gv profile.heap
+```
+
+![pprof call-graph screenshot](http://img707.imageshack.us/img707/7697/screenshot3go.png)
+
+Output one line per function, sorted by own memory usage:
+
+```
+$ pprof --text profile.heap
+```
+
+### memprof_dump_array()
+
+``` php
+<?php
+$dump = memprof_dump_array();
+```
+
+The dump exposes the following information:
+
+ * Inclusive and exclusive memory usage of functions (counting only the memory
+ that has is still in use when memprof_dump_array is called)
+ * Inclusive and exclusive blocks count of functions (number of allocated;
+ counting only the blocks that are still in use when memprof_dump_array is
+ called)
+ * The data is presented in call stacks. This way, if a function is called from
+ multiple places, it is possible to see which call path caused it to leak the
+ most memory
+
+Example output:
+
+ Array
+ (
+ [memory_size] => 11578
+ [blocks_count] => 236
+ [memory_size_inclusive] => 10497691
+ [blocks_count_inclusive] => 244
+ [calls] => 1
+ [called_functions] => Array
+ (
+ [main] => Array
+ (
+ [memory_size] => 288
+ [blocks_count] => 3
+ [memory_size_inclusive] => 10486113
+ [blocks_count_inclusive] => 8
+ [calls] => 1
+ [called_functions] => Array
+ (
+ [a] => Array
+ (
+ [memory_size] => 4
+ [blocks_count] => 1
+ [memory_size_inclusive] => 10485825
+ [blocks_count_inclusive] => 5
+ [calls] => 1
+ [called_functions] => Array
+ (
+ [b] => Array
+ (
+ [memory_size] => 10485821
+ [blocks_count] => 4
+ [memory_size_inclusive] => 10485821
+ [blocks_count_inclusive] => 4
+ [calls] => 1
+ [called_functions] => Array
+ (
+ [str_repeat] => Array
+ (
+ [memory_size] => 0
+ [blocks_count] => 0
+ [memory_size_inclusive] => 0
+ [blocks_count_inclusive] => 0
+ [calls] => 1
+ [called_functions] => Array
+ (
+ )
+ )
+ )
+ )
+ )
+ )
+ [memprof_dump_array] => Array
+ (
+ [memory_size] => 0
+ [blocks_count] => 0
+ [memory_size_inclusive] => 0
+ [blocks_count_inclusive] => 0
+ [calls] => 1
+ [called_functions] => Array
+ (
+ )
+ )
+ )
+ )
+ )
+ )
+
+## Todo
+
+ * Support for tracking persistent (non-zend-alloc) allocations when libc
+ doesn't have malloc hooks
+
+[1]: https://www.gnu.org/software/libc/manual/html_node/Hooks-for-Malloc.html#Hooks-for-Malloc
+[2]: http://kcachegrind.sourceforge.net/html/Home.html
+[3]: http://judy.sourceforge.net/index.html
+[4]: https://google-perftools.googlecode.com/svn/trunk/doc/heapprofile.html
+[5]: https://google-perftools.googlecode.com/
+[6]: https://www.google.com/search?q=qcachegrind
+[7]: https://github.com/arnaud-lb/php-memory-profiler/blob/master/INTERNALS.md
+
diff --git a/php-pecl-memprof.spec b/php-pecl-memprof.spec
new file mode 100644
index 0000000..c36fde7
--- /dev/null
+++ b/php-pecl-memprof.spec
@@ -0,0 +1,166 @@
+# spec file for php-pecl-memprof
+#
+# Copyright (c) 2013 Remi Collet
+# License: CC-BY-SA
+# http://creativecommons.org/licenses/by-sa/3.0/
+#
+# Please, preserve the changelog entries
+#
+%{!?php_inidir: %{expand: %%global php_inidir %{_sysconfdir}/php.d}}
+%{!?__pecl: %{expand: %%global __pecl %{_bindir}/pecl}}
+
+# ZTS build is broken
+# https://github.com/arnaud-lb/php-memory-profiler/pull/7
+
+%global with_zts 0
+%global pecl_name memprof
+
+Summary: Memory usage profiler
+Name: php-pecl-%{pecl_name}
+Version: 1.0.0
+Release: 1%{?dist}%{!?nophptag:%(%{__php} -r 'echo ".".PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')}
+License: BSD
+Group: Development/Languages
+URL: http://pecl.php.net/package/%{pecl_name}
+Source0: http://pecl.php.net/get/%{pecl_name}-%{version}.tgz
+
+# https://github.com/arnaud-lb/php-memory-profiler/pull/6
+Source1: https://raw.github.com/arnaud-lb/php-memory-profiler/master/LICENSE
+Source2: https://raw.github.com/arnaud-lb/php-memory-profiler/master/README.md
+
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildRequires: php-devel > 5.3
+BuildRequires: php-pear
+BuildRequires: Judy-devel
+
+Requires(post): %{__pecl}
+Requires(postun): %{__pecl}
+Requires: php(zend-abi) = %{php_zend_api}
+Requires: php(api) = %{php_core_api}
+
+Provides: php-%{pecl_name} = %{version}
+Provides: php-%{pecl_name}%{?_isa} = %{version}
+Provides: php-pecl(%{pecl_name}) = %{version}
+Provides: php-pecl(%{pecl_name})%{?_isa} = %{version}
+
+# Filter shared private
+%{?filter_provides_in: %filter_provides_in %{_libdir}/.*\.so$}
+%{?filter_setup}
+
+
+%description
+Memory usage profiler for PHP scripts.
+
+
+%prep
+%setup -q -c
+mv %{pecl_name}-%{version} NTS
+
+cd NTS
+cp %{SOURCE1} %{SOURCE2} .
+sed -e 's:/lib:/$PHP_LIBDIR:' -i config.m4
+
+# Sanity check, really often broken
+extver=$(sed -n '/#define MEMPROF_VERSION/{s/.* "//;s/".*$//;p}' php_memprof.h)
+if test "x${extver}" != "x%{version}%{?prever:-%{prever}}"; then
+ : Error: Upstream extension version is ${extver}, expecting %{version}%{?prever:-%{prever}}.
+ exit 1
+fi
+cd ..
+
+%if %{with_zts}
+# Duplicate source tree for NTS / ZTS build
+cp -pr NTS ZTS
+%endif
+
+# Create configuration file
+cat > %{pecl_name}.ini << 'EOF'
+; Enable %{pecl_name} extension module
+extension=%{pecl_name}.so
+EOF
+
+
+%build
+cd NTS
+%{_bindir}/phpize
+%configure \
+ --with-libdir=%{_lib} \
+ --with-php-config=%{_bindir}/php-config
+
+make %{?_smp_mflags}
+
+%if %{with_zts}
+cd ../ZTS
+%{_bindir}/zts-phpize
+%configure \
+ --with-libdir=%{_lib} \
+ --with-php-config=%{_bindir}/zts-php-config
+
+make %{?_smp_mflags}
+%endif
+
+
+%install
+rm -rf %{buildroot}
+
+make -C NTS install INSTALL_ROOT=%{buildroot}
+
+# install config file
+install -D -m 644 %{pecl_name}.ini %{buildroot}%{php_inidir}/%{pecl_name}.ini
+
+# Install XML package description
+install -D -m 644 package.xml %{buildroot}%{pecl_xmldir}/%{name}.xml
+
+%if %{with_zts}
+make -C ZTS install INSTALL_ROOT=%{buildroot}
+
+install -D -m 644 %{pecl_name}.ini %{buildroot}%{php_ztsinidir}/%{pecl_name}.ini
+%endif
+
+
+%post
+%{pecl_install} %{pecl_xmldir}/%{name}.xml >/dev/null || :
+
+
+%postun
+if [ $1 -eq 0 ] ; then
+ %{pecl_uninstall} %{pecl_name} >/dev/null || :
+fi
+
+
+%check
+: Minimal load test for NTS extension
+cd NTS
+%{_bindir}/php --no-php-ini \
+ --define extension=modules/%{pecl_name}.so \
+ --modules | grep %{pecl_name}
+
+%if %{with_zts}
+: Minimal load test for ZTS extension
+cd ../ZTS
+%{__ztsphp} --no-php-ini \
+ --define extension=modules/%{pecl_name}.so \
+ --modules | grep %{pecl_name}
+%endif
+
+
+%clean
+rm -rf %{buildroot}
+
+
+%files
+%defattr(-,root,root,-)
+%doc NTS/{LICENSE,README.md}
+%{pecl_xmldir}/%{name}.xml
+%config(noreplace) %{php_inidir}/%{pecl_name}.ini
+%{php_extdir}/%{pecl_name}.so
+
+%if %{with_zts}
+%config(noreplace) %{php_ztsinidir}/%{pecl_name}.ini
+%{php_ztsextdir}/%{pecl_name}.so
+%endif
+
+
+%changelog
+* Sun Oct 6 2013 Remi Collet <remi@fedoraproject.org> - 1.0.0-1
+- initial package, version 1.0.0 (stable)