Skip to content

Commit

Permalink
Add support for pyroscope memory profiling
Browse files Browse the repository at this point in the history
  • Loading branch information
danog committed May 20, 2024
1 parent a2c759c commit 20f308c
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 56 deletions.
21 changes: 12 additions & 9 deletions src/EventHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
use danog\MadelineProto\EventHandler\Filter\Filter;
use danog\MadelineProto\EventHandler\Filter\FilterAllowAll;
use danog\MadelineProto\EventHandler\Update;
use danog\MadelineProto\Settings\Prometheus;
use danog\MadelineProto\Settings\Metrics;
use Generator;
use PhpParser\Node\Name;
use ReflectionAttribute;
Expand Down Expand Up @@ -130,16 +130,19 @@ final public static function startAndLoop(string $session, ?SettingsAbstract $se
self::cachePlugins(static::class);
$settings ??= new SettingsEmpty;
$API = new API($session, $settings);
$prometheus = false;
if ($settings instanceof Settings) {
$settings = $settings->getPrometheus();
$settings = $settings->getMetrics();
}
if ($settings instanceof Prometheus) {
$prometheus = $settings->getReturnMetricsFromStartAndLoop();
}
if (isset($_GET['metrics']) && $prometheus) {
Tools::closeConnection($API->renderPromStats());
return;
if ($settings instanceof Metrics
&& $settings->getReturnMetricsFromStartAndLoop()
) {
if (isset($_GET['metrics'])) {
Tools::closeConnection($API->renderPromStats());
return;
} elseif (isset($_GET['pprof'])) {
Tools::closeConnection($API->getMemoryProfile());
return;
}
}
$API->startAndLoopInternal(static::class);
}
Expand Down
7 changes: 7 additions & 0 deletions src/InternalDoc.php
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,13 @@ final public static function getMaxMaps(): ?int
{
return \danog\MadelineProto\Tools::getMaxMaps();
}
/**
* Get memory profile with memprof.
*/
final public function getMemoryProfile(): string
{
return $this->wrapper->getAPI()->getMemoryProfile();
}
/**
* Get TL namespaces.
*/
Expand Down
57 changes: 42 additions & 15 deletions src/MTProto.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

namespace danog\MadelineProto;

use Amp\ByteStream\ReadableBuffer;
use Amp\Cache\Cache;
use Amp\Cache\LocalCache;
use Amp\Cancellation;
Expand Down Expand Up @@ -65,6 +66,7 @@
use danog\MadelineProto\MTProtoTools\PeerDatabase;
use danog\MadelineProto\MTProtoTools\PeerHandler;
use danog\MadelineProto\MTProtoTools\ReferenceDatabase;
use danog\MadelineProto\MTProtoTools\ResponseInfo;
use danog\MadelineProto\MTProtoTools\UpdateHandler;
use danog\MadelineProto\Settings\Database\DriverDatabaseAbstract;
use danog\MadelineProto\Settings\TLSchema;
Expand Down Expand Up @@ -543,7 +545,7 @@ public function renderPromStats(?RendererInterface $renderer = null): string
*/
public function getPromGauge(string $namespace, string $name, string $help, array $labels = []): ?BetterGauge
{
if (!$this->getSettings()->getPrometheus()->getEnableCollection()) {
if (!$this->getSettings()->getMetrics()->getEnablePrometheusCollection()) {
return null;
}
return GarbageCollector::$prometheus->getOrRegisterGauge(
Expand All @@ -563,7 +565,7 @@ public function getPromGauge(string $namespace, string $name, string $help, arra
*/
public function getPromCounter(string $namespace, string $name, string $help, array $labels = []): ?BetterCounter
{
if (!$this->getSettings()->getPrometheus()->getEnableCollection()) {
if (!$this->getSettings()->getMetrics()->getEnablePrometheusCollection()) {
return null;
}
return GarbageCollector::$prometheus->getOrRegisterCounter(
Expand All @@ -584,7 +586,7 @@ public function getPromCounter(string $namespace, string $name, string $help, ar
*/
public function getPromSummary(string $namespace, string $name, string $help, array $labels = [], int $maxAgeSeconds = 600, ?array $quantiles = null): ?BetterSummary
{
if (!$this->getSettings()->getPrometheus()->getEnableCollection()) {
if (!$this->getSettings()->getMetrics()->getEnablePrometheusCollection()) {
return null;
}
return GarbageCollector::$prometheus->getOrRegisterSummary(
Expand All @@ -607,7 +609,7 @@ public function getPromSummary(string $namespace, string $name, string $help, ar
*/
public function getPromHistogram(string $namespace, string $name, string $help, array $labels = [], ?array $buckets = null): ?BetterHistogram
{
if (!$this->getSettings()->getPrometheus()->getEnableCollection()) {
if (!$this->getSettings()->getMetrics()->getEnablePrometheusCollection()) {
return null;
}
return GarbageCollector::$prometheus->getOrRegisterHistogram(
Expand Down Expand Up @@ -938,7 +940,7 @@ private function cleanupProperties(): void
'php_version' => PHP_VERSION,
'madeline_version' => API::RELEASE,
]);
$endpoint = $this->getSettings()->getPrometheus()->getMetricsBindTo();
$endpoint = $this->getSettings()->getMetrics()->getMetricsBindTo();
$this->promServer?->stop();
if ($endpoint === null) {
$this->promServer = null;
Expand All @@ -955,10 +957,25 @@ public function __construct(
}
public function handleRequest(ServerRequest $request): ServerResponse
{
if ($request->getUri()->getPath() === '/metrics') {
return new ServerResponse(
status: HttpStatus::OK,
headers: ['Content-Type' => 'text/plain'],
body: $this->API->renderPromStats(),
);
}
if ($request->getUri()->getPath() === '/debug/pprof') {
return new ServerResponse(
status: HttpStatus::OK,
headers: ['Content-Type' => 'text/plain'],
body: $this->API->getMemoryProfile(),
);
}
$result = ResponseInfo::error(HttpStatus::NOT_FOUND);
return new ServerResponse(
status: HttpStatus::OK,
headers: ['Content-Type' => 'text/plain'],
body: $this->API->renderPromStats(),
$result->getCode(),
$result->getHeaders(),
$result->getCodeExplanation()
);
}
}, new DefaultErrorHandler);
Expand Down Expand Up @@ -1317,10 +1334,10 @@ private function updateSettingsInternal(SettingsAbstract $settings, bool $recurs
$this->cleanupProperties();
$this->settings->getDb()->applyChanges();
}
if ($this->settings->getPrometheus()->hasChanged()) {
if ($this->settings->getMetrics()->hasChanged()) {
$this->logger->logger("The prometheus settings have changed!", Logger::WARNING);
$this->cleanupProperties();
$this->settings->getPrometheus()->applyChanges();
$this->settings->getMetrics()->applyChanges();
}
if ($this->settings->getSerialization()->hasChanged()) {
$this->logger->logger("The serialization settings have changed!", Logger::WARNING);
Expand Down Expand Up @@ -1900,24 +1917,34 @@ public function report(string $message, string $parseMode = ''): void
}
}
/**
* Report memory profile with memprof.
* Get memory profile with memprof.
*/
public function reportMemoryProfile(): void
public function getMemoryProfile(): string
{
if (!\extension_loaded('memprof')) {
throw Exception::extension('memprof');
}
if (!memprof_enabled()) {
throw new Exception("Memory profiling is not enabled, set the MEMPROF_PROFILE=1 environment variable or GET parameter to enable it.");
}

$current = "Current memory usage: ".round(memory_get_usage()/1024/1024, 1) . " MB";
$file = fopen('php://memory', 'r+');
memprof_dump_pprof($file);
fseek($file, 0);

return stream_get_contents($file);
}

/**
* Report memory profile with memprof.
*/
public function reportMemoryProfile(): void
{
$pprof = $this->getMemoryProfile();

$current = "Current memory usage: ".round(memory_get_usage()/1024/1024, 1) . " MB";
$file = [
'_' => 'inputMediaUploadedDocument',
'file' => $file,
'file' => new ReadableBuffer($pprof),
'attributes' => [
['_' => 'documentAttributeFilename', 'file_name' => 'report.pprof'],
],
Expand Down
6 changes: 4 additions & 2 deletions src/MTProtoTools/ResponseInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ final class ResponseInfo
private int $code = HttpStatus::OK;
/**
* Header array.
*
* @var array<non-empty-string, string|list<string>>
*/
private array $headers = [];
/**
Expand Down Expand Up @@ -124,7 +126,7 @@ private function __construct(string $method, array $headers, array|int $messageM
} elseif ($size > 0) {
$this->headers['Content-Length'] = (string) $size;
}
$this->headers['Content-Type'] = $messageMedia['mime'];
$this->headers['Content-Type'] = (string) $messageMedia['mime'];
$this->headers['Cache-Control'] = 'max-age=31556926';
$this->headers['Content-Transfer-Encoding'] = 'Binary';
$this->headers['Accept-Ranges'] = 'bytes';
Expand Down Expand Up @@ -207,7 +209,7 @@ public function getCode(): int
/**
* Get header array.
*
* @return array Header array
* @return array<non-empty-string, string|list<string>> Header array
*/
public function getHeaders(): array
{
Expand Down
32 changes: 16 additions & 16 deletions src/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
use danog\MadelineProto\Settings\Files;
use danog\MadelineProto\Settings\Ipc;
use danog\MadelineProto\Settings\Logger;
use danog\MadelineProto\Settings\Metrics;
use danog\MadelineProto\Settings\Peer;
use danog\MadelineProto\Settings\Prometheus;
use danog\MadelineProto\Settings\RPC;
use danog\MadelineProto\Settings\SecretChats;
use danog\MadelineProto\Settings\Serialization;
Expand Down Expand Up @@ -55,9 +55,9 @@ final class Settings extends SettingsAbstract
*/
protected Files $files;
/**
* Prometheus settings.
* Metrics settings.
*/
protected Prometheus $prometheus;
protected Metrics $metrics;
/**
* IPC server settings.
*/
Expand Down Expand Up @@ -110,7 +110,7 @@ public function __construct()
$this->files = new Files;
$this->logger = new Logger;
$this->peer = new Peer;
$this->prometheus = new Prometheus;
$this->metrics = new Metrics;
$this->rpc = new RPC;
$this->secretChats = new SecretChats;
$this->serialization = new Serialization;
Expand All @@ -125,8 +125,8 @@ public function __wakeup(): void
if (!isset($this->voip)) {
$this->voip = new VoIP;
}
if (!isset($this->prometheus)) {
$this->prometheus = new Prometheus;
if (!isset($this->metrics)) {
$this->metrics = new Metrics;
}
}
/**
Expand All @@ -145,8 +145,8 @@ public function merge(SettingsAbstract $settings): void
$this->connection->merge($settings);
} elseif ($settings instanceof Files) {
$this->files->merge($settings);
} elseif ($settings instanceof Prometheus) {
$this->prometheus->merge($settings);
} elseif ($settings instanceof Metrics) {
$this->metrics->merge($settings);
} elseif ($settings instanceof Logger) {
$this->logger->merge($settings);
} elseif ($settings instanceof Peer) {
Expand Down Expand Up @@ -178,7 +178,7 @@ public function merge(SettingsAbstract $settings): void
$this->auth->merge($settings->auth);
$this->connection->merge($settings->connection);
$this->files->merge($settings->files);
$this->prometheus->merge($settings->prometheus);
$this->metrics->merge($settings->metrics);
$this->logger->merge($settings->logger);
$this->peer->merge($settings->peer);
$this->rpc->merge($settings->rpc);
Expand Down Expand Up @@ -277,21 +277,21 @@ public function setFiles(Files $files): self
}

/**
* Get prometheus settings.
* Get metrics settings.
*/
public function getPrometheus(): Prometheus
public function getMetrics(): Metrics
{
return $this->prometheus;
return $this->metrics;
}

/**
* Set prometheus settings.
* Set metrics settings.
*
* @param Prometheus $prometheus File management settings.
* @param Metrics $metrics File management settings.
*/
public function setPrometheus(Prometheus $prometheus): self
public function setMetrics(Metrics $metrics): self
{
$this->prometheus = $prometheus;
$this->metrics = $metrics;

return $this;
}
Expand Down
Loading

0 comments on commit 20f308c

Please sign in to comment.