Skip to content

Commit 9a97514

Browse files
committed
Merge pull request #46 from BitOne/php7
Simplifies code and add summary analyzer
2 parents fecfcae + 6e8e70b commit 9a97514

19 files changed

+786
-371
lines changed

.travis.yml

+6-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ sudo: false
33
language: php
44

55
php:
6-
- 5.4
76
- 5.5
87
- 5.6
98
- 7.0
@@ -19,11 +18,11 @@ before_script:
1918
- phpenv config-rm xdebug.ini
2019

2120
script:
22-
- cd php$(echo $TRAVIS_PHP_VERSION | cut -b 1)/
21+
- cd extension/php$(echo $TRAVIS_PHP_VERSION | cut -b 1)/
2322
- phpize
24-
- configure
23+
- ./configure
2524
- make
26-
- make test
27-
28-
after_script:
29-
- ./.travis.scripts/show-errors.sh
25+
- REPORT_EXIT_STATUS=1 make test
26+
- cd ../../analyzer
27+
- composer install
28+
- vendor/bin/phpspec run

README.md

+130-76
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,16 @@ Its main goal is to help you understand memory leaks: by looking at data present
66

77
One of the main source of inspiration for this tool is the Java jmap tool with the -histo option (see `man jmap`).
88

9+
![Travis CI Results](https://travis-ci.org/BitOne/php-meminfo.svg?branch=master)
10+
911
Compatibility
1012
-------------
11-
PHP 5.6 and PHP 7.1 (may work on PHP 7.0 and 7.2, but not tested yet).
13+
PHP 5.5, 5.6, 5.7, 7.0, 7.1, and 7.2.
14+
15+
May compiles and works with PHP 5.3 and 5.4 but hasn't been tested with these versions.
1216

1317
Compilation instructions
1418
------------------------
15-
You will need the `phpize` command. It can be installed on a Debian based system by:
16-
```bash
17-
$ apt-get install php5-dev
18-
```
19-
for PHP 5, or
20-
```bash
21-
$ apt-get install php7.1-dev
22-
```
23-
for PHP 7.1 when using the Ondrej repository from sury.org.
24-
25-
Once you have this command, follow this steps:
26-
2719
## Compilation
2820
From the root of the `extension/php5` for PHP 5 or `extension/php7` for PHP 7 directory:
2921

@@ -52,90 +44,156 @@ $ composer install
5244

5345
Usage
5446
-----
55-
The extension has one main function: `meminfo_info_dump`.
47+
## Dumping memory content
48+
49+
```php
50+
meminfo_dump(fopen('/tmp/my_dump_file.json', 'w'));
51+
52+
```
5653

5754
This function generates a dump of the PHP memory in a JSON format. This dump can be later analyzed by the provided analyzers.
5855

5956
This functions takes a stream handle as a parameter. It allows you to specify a file (ex `fopen('/tmp/file.txt', 'w')`, as well as to use standard output with the `php://stdout` stream.
6057

61-
## Information gathered
62-
Each memory item (string, boolean, objects, array, etc...) is dumped with the following information:
63-
- in-memory address
64-
- type (object, array, int, string, ...)
65-
- class (only for objects)
66-
- object handle (only for objects)
67-
- self size (without the size of the linked objects)
68-
- is_root (tells if the item is directly linked to a declared variable in the PHP program)
69-
- symbol name (name of the variable name in the PHP program, if the item is linked to a variable)
70-
- execution frame (name of the method where the variable has been declared)
71-
- children: list of linked items with the key name in case of array or property name if object, associated to their address in memory
72-
73-
### Dumping memory info
74-
```php
75-
meminfo_info_dump(fopen('/tmp/my_dump_file.json', 'w'));
58+
## Displaying a summary of items in memory
59+
```bash
60+
$ bin/analyzer summary <dump-file>
7661

62+
Arguments:
63+
dump-file PHP Meminfo Dump File in JSON format
7764
```
78-
Memory Leak Consequences
79-
------------------------
8065

81-
The main consequences of a memory leak are:
82-
- increasing memory usage
83-
- decreasing performances
66+
### Example
67+
```bash
68+
$ bin/analyzer summary /tmp/my_dump_file.json
69+
+----------+-----------------+-----------------------------+
70+
| Type | Instances Count | Cumulated Self Size (bytes) |
71+
+----------+-----------------+-----------------------------+
72+
| string | 132 | 7079 |
73+
| MyClassA | 100 | 7200 |
74+
| array | 10 | 720 |
75+
| integer | 5 | 80 |
76+
| float | 2 | 32 |
77+
| null | 1 | 16 |
78+
+----------+-----------------+-----------------------------+
79+
```
8480

85-
Decreasing performances is usually the most visible part. As memory leak saturates the garbage collector buffer, it runs far more often, without being able to free any memory. This leads to a high CPU usage of the process, with a lot of time spent in the garbage collector instead of your code (garbage collector doesn't run in parallel with the user code in PHP, it has to interrupt it).
81+
## Querying the memory dump to find specific objects
82+
```bash
83+
$ bin/analyzer query [options] [--] <dump-file>
8684

87-
See https://speakerdeck.com/bitone/hunting-down-memory-leaks-with-php-meminfo for a more detailed insight on how memory leak can occur.
85+
Arguments:
86+
dump-file PHP Meminfo Dump File in JSON format
8887

89-
Memory Leak Hunting Process
90-
----------------------------
91-
## Overview
92-
1. dump memory state with `meminfo_info_dump`
93-
2. use the *summary* command of the analyzer to display the item type that is the most present in memory. It's even better to use the summary to display the evolution of objects in memory in order, as the evolution will show where the memory leak really is
94-
3. use the *query* command of the analyzer to find one item from the class that is leaking
95-
4. use the *ref-path* command analyzer to find out the references that still hold this object in memory
88+
Options:
89+
-f, --filters=FILTERS Filter on an attribute. Operators: =, ~. Example: class~User (multiple values allowed)
90+
-l, --limit=LIMIT Number of results limit (default 10).
91+
-v Increase the verbosity
92+
```
9693

97-
## Object Leaks
98-
On object oriented programming, a memory leak usually consists of *objects* leak.
94+
### Example
9995

100-
## Memory state dump
96+
```bash
97+
$ bin/analyzer query -v -f "class=MyClassA" -f "is_root=0" /tmp/php_mem_dump.json
98+
+----------------+-------------------+------------------------------+
99+
| Item ids | Item data | Children |
100+
+----------------+-------------------+------------------------------+
101+
| 0x7f94a1877008 | Type: object | myObjectName: 0x7f94a185cca0 |
102+
| | Class: MyClassA | |
103+
| | Object Handle: 1 | |
104+
| | Size: 72 B | |
105+
| | Is root: No | |
106+
+----------------+-------------------+------------------------------+
107+
| 0x7f94a1877028 | Type: object | myObjectName: 0x7f94a185cde0 |
108+
| | Class: MyClassA | |
109+
| | Object Handle: 2 | |
110+
| | Size: 72 B | |
111+
| | Is root: No | |
112+
+----------------+-------------------+------------------------------+
113+
| 0x7f94a1877048 | Type: object | myObjectName: 0x7f94a185cf20 |
114+
| | Class: MyClassA | |
115+
...
101116

102-
### Analyzing a memory dump
103-
The analyzer is available from the `analyzer/` directory. It will be invoked with:
104-
``` bash
105-
$ bin/analyzer
106117
```
107118

108-
#### Querying a memory dump
109-
The `query` command on the analyzer allows you to filter out some items from a memory dump. The `-f` option can be used several times, effectively *anding* the filters. The supported operators are exact match `=` and regexp match `~`.
119+
## Displaying the reference path
120+
The reference path is the path between a specific item in memory (identified by it's
121+
pointer address) and all the intermediary items up to the one item that is attached
122+
to a variable still alive in the program.
110123

111-
The `-v`option display all the information of the items found.
124+
This path shows who are the items responsible for the memory leak of the specific item
125+
provided.
112126

113-
##### Examples
114-
- finding array that are not directly linked to a variable
115-
```bash
116-
$ bin/analyzer query -f "type=array" -f "is_root=0" my_dump_file.json
117-
```
118-
- finding objects whose class name contains `Product` and linked to a variable
119127
```bash
120-
$ bin/analyzer query -f "class~Product" -f "is_root=1" -v my_dump_file.json
121-
```
128+
$ bin/analyzer ref-path <item-id> <dump-file>
122129

123-
#### Finding out why an object has not been removed from memory
124-
When you are tracking down a memory leak, it's very interesting to understand why an object is still in memory.
130+
Arguments:
131+
item-id Item Id in 0xaaaaaaaa format
132+
dump-file PHP Meminfo Dump File in JSON format
125133

126-
The analyzer provides the `ref-path` command that load the memory dump as a graph in memory and findout all paths linking an item to a root (a variable define in an execution frame).
134+
Options:
135+
-v Increase the verbosity
136+
```
127137

128-
Without the `-v` option, the output will contains only item memory address and key/property name. Adding the `-v` option will display all the information of the linked items.
138+
### Example
129139

130140
```bash
131-
$ bin/analyzer ref-path my_dump_file.json 0x12345678
141+
$ bin/analyzer ref-path 0x7f94a1877068 /tmp/php_mem_dump.json
142+
Found 1 paths
143+
Path from 0x7f94a1856260
144+
+--------------------+
145+
| Id: 0x7f94a1877068 |
146+
| Type: object |
147+
| Class: MyClassA |
148+
| Object Handle: 4 |
149+
| Size: 72 B |
150+
| Is root: No |
151+
| Children count: 1 |
152+
+--------------------+
153+
^
154+
|
155+
3
156+
|
157+
|
158+
+---------------------+
159+
| Id: 0x7f94a185cb60 |
160+
| Type: array |
161+
| Size: 72 B |
162+
| Is root: No |
163+
| Children count: 100 |
164+
+---------------------+
165+
^
166+
|
167+
second level
168+
|
169+
|
170+
+--------------------+
171+
| Id: 0x7f94a185ca20 |
172+
| Type: array |
173+
| Size: 72 B |
174+
| Is root: No |
175+
| Children count: 1 |
176+
+--------------------+
177+
^
178+
|
179+
first level
180+
|
181+
|
182+
+---------------------------+
183+
| Id: 0x7f94a1856260 |
184+
| Type: array |
185+
| Size: 72 B |
186+
| Is root: Yes |
187+
| Execution Frame: <GLOBAL> |
188+
| Symbol Name: myRootArray |
189+
| Children count: 1 |
190+
+---------------------------+
132191
```
133192

134-
Usage in production
135-
-------------------
136-
PHP Meminfo can be used in production, as it does not have any impact on performances outside of the call to the `meminfo` functions.
193+
A worflow to find and understand memory leak by using PHP Meminfo
194+
-----------------------------------------------------------------
137195

138-
Nevertheless, production environment is not where you debug ;)
196+
[Hunting down memory leaks](doc/hunting_down_memory_leaks.md)
139197

140198
Other memory debugging tools for PHP
141199
-------------------------------------
@@ -148,7 +206,7 @@ Provides aggregated data about memory usage by functions. Far less resource inte
148206
Troubleshooting
149207
---------------
150208

151-
## "Call to undefined function" when calling `meminfo_info_dump`
209+
## "Call to undefined function" when calling `meminfo_dump`
152210
It certainly means the extension is not enabled.
153211

154212
Check the PHP Info output and look for the MemInfo data.
@@ -157,8 +215,4 @@ To see the PHP Info output, just create a page calling the `phpinfo();` function
157215

158216
Credits
159217
-------
160-
Thanks to Derick Rethans on his inspirational work on the essential XDebug. See http://www.xdebug.org/
161-
162-
Video presentation (in French)
163-
------------------------------
164-
https://www.youtube.com/watch?v=wZjnj1PAJ78
218+
Thanks to Derick Rethans for his inspirational work on the essential XDebug. See http://www.xdebug.org/

analyzer/src/BitOne/PhpMemInfo/Console/Command/ReferencePathCommand.php

+4-4
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ protected function configure()
2929
->setName('ref-path')
3030
->setDescription('Find reference paths to an item')
3131
->addArgument(
32-
'dump-file',
32+
'item-id',
3333
InputArgument::REQUIRED,
34-
'PHP Meminfo Dump File in JSON format'
34+
'Item Id in 0xaaaaaaaa format'
3535
)
3636
->addArgument(
37-
'item-id',
37+
'dump-file',
3838
InputArgument::REQUIRED,
39-
'Item Id in 0xaaaaaaaa format'
39+
'PHP Meminfo Dump File in JSON format'
4040
);
4141
}
4242

doc/example.php

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
class MyClassA {
4+
private $myObjectName;
5+
6+
public function __construct($name)
7+
{
8+
$this->myObjectName = $name;
9+
}
10+
};
11+
12+
$myString = "My very nice string";
13+
$myFloat = 3.14;
14+
$myInt = 42;
15+
$myNull = null;
16+
17+
$myRootArray = [];
18+
19+
for($i = 0; $i < 100; $i++) {
20+
$myRootArray['first level']['second level'][] = new MyClassA('My nice object name '.$i);
21+
};
22+
23+
meminfo_dump(fopen('/tmp/php_mem_dump.json','w'));
24+
25+

0 commit comments

Comments
 (0)