diff --git a/tests/xdebug/3.0.0/line-breakpoint.inc b/tests/xdebug/3.0.0/line-breakpoint.inc
new file mode 100644
index 00000000000..505c5f3f11b
--- /dev/null
+++ b/tests/xdebug/3.0.0/line-breakpoint.inc
@@ -0,0 +1,17 @@
+log();
+echo "post-hook\n";
diff --git a/tests/xdebug/3.0.0/line-breakpoint.phpt b/tests/xdebug/3.0.0/line-breakpoint.phpt
new file mode 100644
index 00000000000..fc07514f7af
--- /dev/null
+++ b/tests/xdebug/3.0.0/line-breakpoint.phpt
@@ -0,0 +1,60 @@
+--TEST--
+Line breakpoint on interface-hook
+--FILE--
+ "xdebug-" . phpversion('xdebug'), "datadog.trace.sources_path" => __DIR__ . "/..", "datadog.logs_injection" => 0], ["show-stdout" => true]);
+
+?>
+--EXPECTF--
+
+
+
+-> feature_set -i 1 -n breakpoint_details -v 1
+
+
+
+-> feature_set -i 2 -n resolved_breakpoint -v 1
+
+
+
+-> step_into -i 3
+
+
+
+-> breakpoint_set -i 4 -t line -f %s/line-breakpoint.inc -n 15
+
+
+
+-> breakpoint_set -i 5 -t line -f %s/line-breakpoint.inc -n 17
+
+
+
+-> run -i 6
+
+%S
+
+-> run -i 7
+
+%S
+
+-> detach -i 8
+
+
+
+pre-hook
+hey
+post-hook
diff --git a/tests/xdebug/DDTrace/Integrations/Logs/LogsIntegration.php b/tests/xdebug/DDTrace/Integrations/Logs/LogsIntegration.php
new file mode 100644
index 00000000000..25dd6fef323
--- /dev/null
+++ b/tests/xdebug/DDTrace/Integrations/Logs/LogsIntegration.php
@@ -0,0 +1,10 @@
+port;
+ }
+
+ public function setPort($port)
+ {
+ $this->port = $port;
+ }
+
+ protected function getIPAddress()
+ {
+ return "127.0.0.1";
+ }
+
+ protected function getAddress()
+ {
+ return "tcp://" . $this->getIPAddress() . ":" . $this->getPort();
+ }
+
+ public function __construct()
+ {
+ $this->tmpDir = getTmpDir();
+ }
+
+ private function open( &$errno, &$errstr )
+ {
+ $socket = @stream_socket_server( $this->getAddress(), $errno, $errstr );
+ if ( $socket )
+ {
+ $name = stream_socket_get_name( $socket, false );
+ $name = explode( ":", $name );
+ $this->port = array_pop( $name );
+ }
+ return $socket;
+ }
+
+ private function launchPhp( &$pipes, $filename, array $ini_options = [], array $extra_options = [] )
+ {
+ @unlink( $this->tmpDir . 'error-output.txt' );
+ @unlink( $this->tmpDir . 'remote_log.txt' );
+
+ $descriptorspec = array(
+ 0 => array( 'pipe', 'r' ),
+ 1 => array( 'pipe', 'w' ),
+ 2 => array( 'file', $this->tmpDir . 'error-output.txt', 'a' )
+ );
+
+ $default_options = array(
+ "xdebug.mode" => "debug",
+ "xdebug.start_with_request" => "'yes'",
+ "xdebug.client_host" => $this->getIPAddress(),
+ "xdebug.client_port" => $this->getPort(),
+ 'xdebug.control_socket' => "'no'",
+ );
+
+ $env_vars = array_key_exists( 'env', $extra_options ) ? $extra_options['env'] : [];
+ $env_vars += $_ENV;
+
+ $options = (getenv('TEST_PHP_ARGS') ?: '');
+ $ini_options = array_merge( $default_options, $ini_options );
+ foreach ( $ini_options as $key => $value )
+ {
+ $options .= " -d{$key}=$value";
+ }
+
+ if ( array_key_exists( 'auto_prepend', $extra_options ) )
+ {
+ $prependFile = "{$this->tmpDir}auto-prepend.inc";
+ file_put_contents( $prependFile, $extra_options['auto_prepend'] );
+ $options .= " -dauto_prepend_file={$prependFile}";
+ }
+
+ $php = getenv( 'TEST_PHP_EXECUTABLE' );
+ $cmd = "{$php} $options {$filename} >{$this->tmpDir}php-stdout.txt 2>{$this->tmpDir}php-stderr.txt";
+ if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
+ $cmd = "exec {$cmd}";
+ }
+ $cwd = dirname( __FILE__ );
+
+ $process = proc_open( $cmd, $descriptorspec, $pipes, $cwd, $env_vars );
+ return $process;
+ }
+
+ function fixFilePath( $m )
+ {
+ preg_match( '@.*/(.*\.inc)@', $m[2], $fm );
+ if ( !isset( $fm[1] ) )
+ {
+ $fm[1] = '';
+ }
+ return " {$m[1]}=\"file://{$fm[1]}\"";
+ }
+
+ function doRead( $conn, ?string $transaction_id = null )
+ {
+ stream_set_timeout( $conn, 3 );
+ do {
+ $trans_id = null;
+ $length = 0;
+ while ( "\0" !== ( $char = fgetc($conn) ) ) {
+ if ( $char === false ) {
+ echo "read a false for $transaction_id" . PHP_EOL;
+ return null;
+ }
+ if ( !is_numeric($char) ) {
+ echo "read a non-number for $transaction_id" . PHP_EOL;
+ return null;
+ }
+ $length = $length * 10 + (int)$char;
+ }
+
+ $read = '';
+ while ( 0 < $length ) {
+ $data = fread( $conn, $length );
+ if ( $data === false )
+ {
+ echo "read a false for $transaction_id" . PHP_EOL;
+ return null;
+ }
+ $length -= strlen( $data );
+ $read .= $data;
+ }
+ $char = fgetc( $conn );
+ if ( $char !== "\0" )
+ {
+ echo 'must end with \0' . PHP_EOL;
+ }
+
+ // sanitize
+ $read = preg_replace( '@(?\d+)"@', $read, $matches ) )
+ {
+ $trans_id = $matches['transaction_id'] ?? null;
+ }
+ } while ( $trans_id !== $transaction_id );
+ }
+
+ function start( $filename, array $ini_options = [], array $options = [])
+ {
+ $filename = realpath( $filename );
+
+ $this->socket = $this->open( $errno, $errstr );
+ if ( $this->socket === false )
+ {
+ echo "Could not create socket server - already in use?\n";
+ echo "Error: {$errstr}, errno: {$errno}\n";
+ echo "Address: {$this->getAddress()}\n";
+ return false;
+ }
+ $this->php = $this->launchPhp( $this->ppipes, $filename, $ini_options, $options );
+ $conn = @stream_socket_accept( $this->socket, isset( $options['timeout'] ) ? $options['timeout'] : 5 );
+
+ if ( $conn === false )
+ {
+ echo @file_get_contents( $this->tmpDir . 'php-stdout.txt' ), "\n";
+ echo @file_get_contents( $this->tmpDir . 'php-stderr.txt' ), "\n";
+ echo @file_get_contents( $this->tmpDir . 'error-output.txt' ), "\n";
+ echo @file_get_contents( $this->tmpDir . 'remote_log.txt' ), "\n";
+ proc_close( $this->php );
+ return false;
+ }
+ return $conn;
+ }
+
+ function stop( $conn, array $options = [] )
+ {
+ fclose( $conn );
+ fclose( $this->ppipes[0] );
+ fclose( $this->ppipes[1] );
+ fclose( $this->socket );
+ proc_close( $this->php );
+
+ if ( array_key_exists( 'show-stdout', $options ) && $options['show-stdout'] )
+ {
+ echo @file_get_contents( $this->tmpDir . 'php-stdout.txt' ), "\n";
+ }
+ // echo @file_get_contents( $this->tmpDir . 'php-stderr.txt' ), "\n";
+ // echo @file_get_contents( $this->tmpDir . 'error-output.txt' ), "\n";
+ }
+
+ function sendCommand( $conn, $command, $transaction_id )
+ {
+ // inject identifier
+ $parts = explode( ' ', $command, 2 );
+ if ( count($parts) == 1 )
+ {
+ $command = $parts[0] . " -i $transaction_id";
+ }
+ else
+ {
+ $command = $parts[0] . " -i $transaction_id " . $parts[1];
+ }
+
+ /* Replace PID macro */
+ $command = str_replace( "{{PID}}", $this->pid & 0x1ffff, $command );
+
+ $sanitised = $command;
+ $sanitised = preg_replace( '@\sfile://.*[/\\\\](.*\.inc)\s@', ' file://\\1 ', $sanitised );
+
+ echo "-> ", $sanitised, "\n";
+ fwrite( $conn, $command . "\0" );
+ }
+
+ function runTest( $filename, array $commands, array $ini_options = [], array $options = [] )
+ {
+ $conn = $this->start( $filename, $ini_options, $options );
+ if ( $conn === false )
+ {
+ return;
+ }
+ $i = 1;
+ $procInfo = proc_get_status( $this->php );
+ $this->pid = $procInfo['pid'];
+
+ // read header
+ $this->doRead( $conn );
+ foreach ( $commands as $command )
+ {
+ $this->sendCommand( $conn, $command, $i );
+ $this->doRead( $conn, (string)$i );
+
+ $i++;
+ }
+ $this->stop( $conn, $options );
+ }
+}
+
+class DebugClientIPv6 extends DebugClient
+{
+ protected function getIPAddress()
+ {
+ return "::1";
+ }
+
+ protected function getAddress()
+ {
+ return "tcp://[" . $this->getIPAddress() . "]:" . $this->getPort();
+ }
+
+ public static function isSupported( &$errno, &$errstr )
+ {
+ $socket = @stream_socket_server( "tcp://[::1]:0", $errno, $errstr );
+
+ if ( $socket === false )
+ {
+ return false;
+ }
+
+ fclose( $socket );
+ return true;
+ }
+}
+
+function dbgpRunFile( $data, $commands, array $ini_options = [], array $options = [] )
+{
+ if ( isset( $options['ipv'] ) && $options['ipv'] == 6 )
+ {
+ $t = new DebugClientIPv6();
+ }
+ else
+ {
+ $t = new DebugClient();
+ }
+
+ $t->runTest( $data, $commands, $ini_options, $options );
+}
+?>
\ No newline at end of file