Skip to content

Commit b37e5f5

Browse files
authored
Add support for hprof in order to have profiling. (#3881)
1 parent c385fdc commit b37e5f5

File tree

11 files changed

+274
-4
lines changed

11 files changed

+274
-4
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,6 @@ docker-compose.yml
6161
Lychee/*
6262
/public/embed/
6363
report_*
64+
65+
# XHProf output
66+
public/vendor/

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
"filp/whoops": "^2.5",
110110
"friendsofphp/php-cs-fixer": "^3.3",
111111
"itsgoingd/clockwork": "^5.1",
112+
"laracraft-tech/laravel-xhprof": "^1.2",
112113
"larastan/larastan": "^3.2",
113114
"lychee-org/phpstan-lychee": "^2.0.1",
114115
"mockery/mockery": "^1.5",

composer.lock

Lines changed: 59 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/xhprof.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
return [
4+
'enabled' => env('XHPROF_ENABLED', false),
5+
6+
'skip' => [
7+
'/__clockwork/',
8+
'/_debugbar/',
9+
],
10+
];
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\DB;
6+
use Illuminate\Support\Facades\Schema;
7+
8+
class CreateXHProfTable extends Migration
9+
{
10+
/**
11+
* Run the migrations.
12+
*
13+
* @return void
14+
*/
15+
public function up()
16+
{
17+
if (DB::connection()->getDriverName() !== 'mysql') {
18+
return;
19+
}
20+
21+
Schema::create('details', function (Blueprint $table) {
22+
$table->id('idcount');
23+
$table->char('id', 64);
24+
$table->char('url', 255)->nullable();
25+
$table->char('c_url', 255)->nullable();
26+
$table->timestamp('timestamp')->useCurrent()->useCurrentOnUpdate();
27+
$table->char('server name', 64)->nullable();
28+
$table->binary('perfdata')->nullable();
29+
$table->tinyInteger('type')->nullable();
30+
$table->binary('cookie')->nullable();
31+
$table->binary('post')->nullable();
32+
$table->binary('get')->nullable();
33+
$table->integer('pmu')->nullable();
34+
$table->integer('wt')->nullable();
35+
$table->integer('cpu')->nullable();
36+
$table->char('server_id', 64)->nullable();
37+
$table->char('aggregateCalls_include', 255)->nullable();
38+
39+
$table->index('url');
40+
$table->index('c_url');
41+
$table->index('cpu');
42+
$table->index('wt');
43+
$table->index('pmu');
44+
$table->index('timestamp');
45+
$table->index(['server name', 'timestamp']);
46+
});
47+
48+
if (DB::connection()->getDriverName() === 'mysql') {
49+
DB::statement('ALTER TABLE details MODIFY COLUMN `perfdata` LONGBLOB');
50+
DB::statement('ALTER TABLE details MODIFY COLUMN `cookie` LONGBLOB');
51+
DB::statement('ALTER TABLE details MODIFY COLUMN `post` LONGBLOB');
52+
}
53+
}
54+
55+
/**
56+
* Reverse the migrations.
57+
*
58+
* @return void
59+
*/
60+
public function down()
61+
{
62+
if (DB::connection()->getDriverName() !== 'mysql') {
63+
return;
64+
}
65+
66+
Schema::dropIfExists('details');
67+
}
68+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
class AddIndexToXHProfTable extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* @return void
13+
*/
14+
public function up()
15+
{
16+
if (DB::connection()->getDriverName() !== 'mysql') {
17+
return;
18+
}
19+
20+
Schema::table('details', function (Blueprint $table) {
21+
$table->dropColumn('idcount');
22+
$table->char('id', 64)->primary()->change();
23+
});
24+
}
25+
26+
/**
27+
* Reverse the migrations.
28+
*
29+
* @return void
30+
*/
31+
public function down()
32+
{
33+
if (DB::connection()->getDriverName() !== 'mysql') {
34+
return;
35+
}
36+
37+
Schema::table('details', function (Blueprint $table) {
38+
$table->dropPrimary();
39+
});
40+
41+
Schema::table('details', function (Blueprint $table) {
42+
$table->id('idcount');
43+
$table->char('id', 64)->change();
44+
});
45+
}
46+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# How to Enable and Use XHProf in Lychee
2+
3+
XHProf is a profiling tool for PHP applications. This guide explains how to install, configure, and use XHProf in the Lychee project.
4+
5+
---
6+
7+
**This is only possible if you are using a MySQL/MariaDB database, as the XHProf storage implementation relies on it.**
8+
9+
## Installation
10+
11+
1. **Install XHProf**
12+
13+
Run the following commands to install XHProf and its dependencies:
14+
15+
```bash
16+
sudo add-apt-repository ppa:ondrej/php
17+
sudo apt-get update
18+
sudo apt-get install php php-xhprof graphviz
19+
```
20+
21+
Verify the installation:
22+
23+
```bash
24+
php -i | grep xhprof
25+
```
26+
27+
If necessary, restart your web server or PHP-FPM service.
28+
29+
2. **Download the Visualization Tool**
30+
31+
Clone the XHProf visualization tool into the `public/vendor` directory:
32+
33+
```bash
34+
git clone [email protected]:preinheimer/xhprof.git ./public/vendor/xhprof
35+
```
36+
37+
3. **Set Up the Configuration**
38+
39+
Copy the sample configuration file and update it:
40+
41+
```bash
42+
cp public/vendor/xhprof/xhprof_lib/config.sample.php public/vendor/xhprof/xhprof_lib/config.php
43+
nano public/vendor/xhprof/xhprof_lib/config.php
44+
```
45+
46+
Update the following settings in the configuration file:
47+
48+
- **Database Credentials:** Set the database credentials to match your environment.
49+
- **Enable `dot_binary`:** Uncomment and configure the `dot_binary` section.
50+
- **Control IPs:** If running locally, set `$controlIPs` to `false`.
51+
52+
---
53+
54+
## Usage
55+
56+
1. **Enable XHProf**
57+
58+
Set the following environment variable in your `.env` file:
59+
60+
```env
61+
XHPROF_ENABLED=true
62+
```
63+
64+
2. **Profile Requests**
65+
66+
Once enabled, every request to your application will be profiled automatically.
67+
68+
3. **View Profiling Results**
69+
70+
Open the following URL in your browser to view the profiling results:
71+
72+
```
73+
<your-host>/vendor/xhprof/xhprof_html/
74+
```
75+
76+
---
77+
78+
*Last updated: December 22, 2025*

rector.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
__DIR__ . '/app/Metadata/Laminas/Unicode.php',
3333

3434
// Ignore all the test directory for now...
35-
__DIR__ . '/tests/*'
35+
__DIR__ . '/tests/*',
36+
// Ignore all hprof related files
37+
__DIR__ . '/public/vendor/xhprof/*',
38+
3639
]);
3740
return $rectorConfig;

tests/Unit/Actions/Db/OptimizeDbTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ public function testOptimizeDb(): void
3232
{
3333
$optimize = new OptimizeDb();
3434
$output = count($optimize->do());
35-
self::assertTrue(in_array($output, [3, 34], true), 'OptimizeDb should return either 3 or 34: ' . $output);
35+
self::assertTrue(in_array($output, [3, 34, 35], true), 'OptimizeDb should return either 3 or 34 or 35: ' . $output);
3636
}
3737
}

tests/Unit/Actions/Db/OptimizeTablesTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ public function testOptimizeTables(): void
3232
{
3333
$optimize = new OptimizeTables();
3434
$output = count($optimize->do());
35-
self::assertTrue(in_array($output, [3, 34], true), 'OptimizeTables should return either 3 or 30: ' . $output);
35+
self::assertTrue(in_array($output, [3, 34, 35], true), 'OptimizeTables should return either 3 or 34 or 35: ' . $output);
3636
}
3737
}

0 commit comments

Comments
 (0)