Codex/implement php apm with c for linux gek86k#5
Conversation
| @@ -0,0 +1 @@ | |||
| config.m4 | |||
There was a problem hiding this comment.
[INFO] Adding a .aignore entry for config.m4 will prevent this file from being processed by tools that respect .aignore. Ensure that config.m4 does not contain any required configuration for the build or deployment, as it will now be ignored.
| - **Native C extension** for low-level telemetry (CPU, wall time, memory, sampling, errors/exceptions). | ||
| - **PHP userland layer** that converts native telemetry to OpenTelemetry traces and metrics. | ||
|
|
||
| The extension targets **PHP 8.1+** on Linux and uses **CLOCK_THREAD_CPUTIME_ID** for CPU and **CLOCK_MONOTONIC** for wall time. |
There was a problem hiding this comment.
[SUGGESTION] Consider clarifying platform support: the extension currently targets Linux; note whether Windows/macOS are supported or if there are plans for cross‑platform builds.
| ```ini | ||
| extension=apm | ||
| apm.enabled=1 | ||
| apm.enable_sampling=1 |
There was a problem hiding this comment.
[SUGGESTION] The apm.sample_interval_us setting is in microseconds; consider adding a note about the unit and typical values to help users configure it correctly.
| @@ -0,0 +1,7 @@ | |||
| PHP_ARG_ENABLE(apm, whether to enable apm support, | |||
There was a problem hiding this comment.
[ERROR] The repository's .aignore now lists config.m4, which means this file will be ignored by tools that respect .aignore. As a result, the PHP extension may not be configured or built correctly. Consider removing config.m4 from .aignore or adjusting the ignore rules.
| use OpenTelemetry\SDK\Trace\SpanExporter\OtlpHttpExporter; | ||
| use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor; | ||
|
|
||
| require __DIR__ . '/vendor/autoload.php'; |
There was a problem hiding this comment.
[SUGGESTION] The script assumes a vendor/autoload.php exists relative to this file. In many setups the example may be run from a different working directory, causing a fatal error. Consider using an absolute path or checking file existence before requiring.
| require __DIR__ . '/vendor/autoload.php'; | ||
|
|
||
| $resource = ResourceInfo::create(['service.name' => 'php-apm-service']); | ||
| $exporter = new OtlpHttpExporter('http://localhost:4318/v1/traces'); |
There was a problem hiding this comment.
[SUGGESTION] The OtlpHttpExporter URL is hard‑coded to localhost:4318. In production this will likely need to be configurable via environment variable or config file.
| #define PHP_APM_H | ||
|
|
||
| #include <stdint.h> | ||
| #include <stdio.h> |
There was a problem hiding this comment.
[SUGGESTION] The <stdio.h> header is included but not used anywhere in this file. Consider removing it to reduce compilation overhead and avoid unnecessary dependencies.
| @@ -0,0 +1 @@ | |||
| config.m4 | |||
There was a problem hiding this comment.
[INFO] Adding .aignore to block config.m4 is fine. Ensure that this pattern does not unintentionally prevent needed configuration files from being accessed in the future.
|
[INFO] The addition of |
|
[INFO] The new README provides a clear overview of the hybrid PHP APM agent, build instructions, and configuration details. Consider adding a brief licensing notice and a link to contribution guidelines for completeness, but otherwise the documentation looks solid. |
|
|
||
| - Sampling is enabled by default; function-level timing is gated to avoid overhead. | ||
| - Stack samples are captured in a ring buffer and flushed in userland. | ||
| - Flamegraphs can be generated from sampled stacks (folded stack format). |
There was a problem hiding this comment.
[SUGGESTION] Consider adding a License section (e.g., MIT) at the end of the README to clarify usage rights.
| @@ -0,0 +1,7 @@ | |||
| PHP_ARG_ENABLE(apm, whether to enable apm support, | |||
| [ --enable-apm Enable apm support]) | |||
|
|
|||
There was a problem hiding this comment.
[INFO] The description for the --enable-apm flag is concise, but consider adding a brief note about the default behavior (disabled) for clarity to users.
| @@ -0,0 +1,7 @@ | |||
| PHP_ARG_ENABLE(apm, whether to enable apm support, | |||
There was a problem hiding this comment.
[INFO] Consider specifying the default value (e.g., "no") in PHP_ARG_ENABLE to make the default behavior explicit for users.
| require __DIR__ . '/vendor/autoload.php'; | ||
|
|
||
| $resource = ResourceInfo::create(['service.name' => 'php-apm-service']); | ||
| $exporter = new OtlpHttpExporter('http://localhost:4318/v1/traces'); |
There was a problem hiding this comment.
[SUGGESTION] The OTLP endpoint URL is hard‑coded. Consider making it configurable via an environment variable or configuration file to allow different deployment environments.
|
|
||
| require __DIR__ . '/vendor/autoload.php'; | ||
|
|
||
| $resource = ResourceInfo::create(['service.name' => 'php-apm-service']); |
There was a problem hiding this comment.
[SUGGESTION] The service name is hard‑coded. Consider making it configurable (e.g., via env var) so the same code can be reused across services.
| $span->recordException($e); | ||
| $span->setStatus(StatusCode::STATUS_ERROR, $e->getMessage()); | ||
| } finally { | ||
| $metrics = apm_get_request_metrics(); |
There was a problem hiding this comment.
[WARNING] Ensure the APM extension is loaded before calling apm_get_request_metrics() etc., otherwise this will cause a fatal error if the extension is missing.
|
[INFO] Added |
| - **Native C extension** for low-level telemetry (CPU, wall time, memory, sampling, errors/exceptions). | ||
| - **PHP userland layer** that converts native telemetry to OpenTelemetry traces and metrics. | ||
|
|
||
| The extension targets **PHP 8.1+** on Linux and uses **CLOCK_THREAD_CPUTIME_ID** for CPU and **CLOCK_MONOTONIC** for wall time. |
There was a problem hiding this comment.
[SUGGESTION] The extension currently targets Linux only. It would be helpful to note any platform limitations or future plans for macOS/Windows support.
|
|
||
| ```sh | ||
| phpize | ||
| ./configure --enable-apm |
There was a problem hiding this comment.
[SUGGESTION] Consider adding a comment that the value is in microseconds to avoid confusion for users adjusting the sampling interval.
| extension=apm | ||
| apm.enabled=1 | ||
| apm.enable_sampling=1 | ||
| apm.sample_interval_us=10000 |
There was a problem hiding this comment.
[SUGGESTION] Consider adding a comment that the value is in microseconds to avoid confusion for users adjusting the sampling interval.
| use OpenTelemetry\SDK\Trace\SpanExporter\OtlpHttpExporter; | ||
| use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor; | ||
|
|
||
| require __DIR__ . '/vendor/autoload.php'; |
There was a problem hiding this comment.
[SUGGESTION] Consider checking that the autoload file exists before requiring it to avoid a fatal error when the example is run outside of a Composer-installed environment.
| require __DIR__ . '/vendor/autoload.php'; | ||
|
|
||
| $resource = ResourceInfo::create(['service.name' => 'php-apm-service']); | ||
| $exporter = new OtlpHttpExporter('http://localhost:4318/v1/traces'); |
There was a problem hiding this comment.
[SUGGESTION] The OTLP endpoint URL is hard‑coded. Consider making it configurable via an environment variable or configuration file to allow different deployment environments.
|
|
||
| static uint32_t apm_ring_next(uint32_t value, uint32_t capacity) | ||
| { | ||
| value++; |
There was a problem hiding this comment.
[WARNING] The apm_ring_next function assumes capacity is non-zero. If APM_G(buffer_size) were set to 0, this could cause a division by zero or infinite loop. Consider adding a guard to handle zero capacity gracefully.
| return apm_clock_now(CLOCK_THREAD_CPUTIME_ID); | ||
| } | ||
|
|
||
| static void apm_log(const char *format, ...) |
There was a problem hiding this comment.
[SUGGESTION] apm_log logs every call as E_NOTICE which may flood logs in production. Consider using a lower log level or making logging configurable.
| apm_ring_push_sample(&entry, &cpu_point); | ||
| } | ||
|
|
||
| static void apm_sampler_signal_handler(int signo) |
There was a problem hiding this comment.
[CRITICAL] The signal handler apm_sampler_signal_handler invokes apm_capture_sample, which uses many PHP runtime functions (e.g., zend hash operations, memory allocation). These are not async-signal-safe and can cause undefined behavior or crashes. Consider redesigning sampling to use a safe flag set in the handler and perform the capture in the main request loop.
| @@ -0,0 +1,59 @@ | |||
| # PHP APM (Hybrid C + PHP OpenTelemetry) | |||
There was a problem hiding this comment.
[SUGGESTION] Consider adding a brief "License" section to clarify the project's licensing terms for downstream users.
| ## Build and install | ||
|
|
||
| ```sh | ||
| phpize |
There was a problem hiding this comment.
[SUGGESTION] Consider adding a note about required build dependencies (e.g., php-dev, autoconf, libtool) before running phpize and configure, to help users set up the environment correctly.
| require __DIR__ . '/vendor/autoload.php'; | ||
|
|
||
| $resource = ResourceInfo::create(['service.name' => 'php-apm-service']); | ||
| $exporter = new OtlpHttpExporter('http://localhost:4318/v1/traces'); |
There was a problem hiding this comment.
[SUGGESTION] Consider reading the OTLP endpoint from an environment variable (e.g., OTEL_EXPORTER_OTLP_ENDPOINT) instead of hard‑coding http://localhost:4318/v1/traces. This makes the example more flexible for different deployment environments.
| use OpenTelemetry\SDK\Trace\SpanExporter\OtlpHttpExporter; | ||
| use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor; | ||
|
|
||
| require __DIR__ . '/vendor/autoload.php'; |
There was a problem hiding this comment.
[SUGGESTION] The autoload path assumes a vendor directory next to this script. Consider using Composer's autoload via require __DIR__.'/../vendor/autoload.php'; or checking for its existence to avoid runtime errors when the example is run from a different location.
| { | ||
| apm_globals->enabled = 1; | ||
| apm_globals->enable_sampling = 1; | ||
| apm_globals->enable_function_timing = 0; |
There was a problem hiding this comment.
[WARNING] Consider validating that apm.sample_interval_us is greater than zero. A zero interval would create a timer with no delay, potentially causing a busy loop and high CPU usage.
|
[INFO] The PR adds a new |
|
|
||
| This repository provides a production-grade **hybrid** PHP APM agent: | ||
|
|
||
| - **Native C extension** for low-level telemetry (CPU, wall time, memory, sampling, errors/exceptions). |
There was a problem hiding this comment.
[SUGGESTION] Consider adding a brief explanation of what "hybrid" means in this context (i.e., combination of native C extension and PHP userland) for readers unfamiliar with the term.
| use OpenTelemetry\SDK\Trace\SpanExporter\OtlpHttpExporter; | ||
| use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor; | ||
|
|
||
| require __DIR__ . '/vendor/autoload.php'; |
There was a problem hiding this comment.
[SUGGESTION] The script assumes a vendor/autoload.php exists relative to this file. Ensure the example includes installation instructions or uses a more robust autoload path to avoid runtime errors.
|
|
||
| ZEND_DECLARE_MODULE_GLOBALS(apm) | ||
|
|
||
| static uint64_t apm_clock_now(clockid_t clock_id) |
There was a problem hiding this comment.
[WARNING] The apm_clock_now function returns 0 on clock_gettime failure, which is indistinguishable from a valid timestamp of 0. Consider returning an error indicator or logging the failure to avoid silent incorrect timing data.
| } | ||
| } | ||
|
|
||
| static void apm_ring_init(uint32_t capacity) |
There was a problem hiding this comment.
[WARNING] apm_ring_init should validate that capacity is greater than zero before allocating memory. A zero capacity would allocate zero‑length buffers and later cause dereferencing NULL pointers in apm_ring_push_sample. Consider adding a guard and fallback to a safe default size.
| apm_globals->enabled = 1; | ||
| apm_globals->enable_sampling = 1; | ||
| apm_globals->enable_function_timing = 0; | ||
| apm_globals->sample_interval_us = 10000; |
There was a problem hiding this comment.
[WARNING] The INI setting apm.buffer_size is assigned directly without validation. If a user configures a zero or excessively large value, memory allocation in apm_ring_init may fail or allocate zero‑length buffers, leading to crashes. Consider adding a sanity check (e.g., enforce a minimum of 1 and a reasonable maximum).
| APM_G(ring).head = 0; | ||
| APM_G(ring).tail = 0; | ||
| APM_G(ring).dropped = 0; | ||
| APM_G(ring).samples = ecalloc(capacity, sizeof(apm_sample_entry)); |
There was a problem hiding this comment.
[WARNING] Memory allocation with ecalloc for samples and cpu_points is not checked for failure. If allocation returns NULL, subsequent dereferences will segfault. Consider checking the return value and handling OOM gracefully.
| zend_bool enabled; | ||
| zend_bool enable_sampling; | ||
| zend_bool enable_function_timing; | ||
| zend_long sample_interval_us; |
There was a problem hiding this comment.
[WARNING] sample_interval_us is defined as zend_long (signed). Since it represents a duration in microseconds, a negative value would be nonsensical and could cause timer calculations to behave incorrectly. Consider using an unsigned type (e.g., zend_ulong or uint64_t) and validate the value in the INI handler.
No description provided.