diff --git a/src/FrameBuilder.php b/src/FrameBuilder.php index b2adba6de..5cd61bae6 100644 --- a/src/FrameBuilder.php +++ b/src/FrameBuilder.php @@ -5,6 +5,7 @@ namespace Sentry; use Sentry\Serializer\RepresentationSerializerInterface; +use Sentry\Util\PrefixStripper; /** * This class builds a {@see Frame} object out of a backtrace's raw frame. @@ -22,6 +23,8 @@ */ final class FrameBuilder { + use PrefixStripper; + /** * @var Options The SDK client options */ @@ -66,13 +69,13 @@ public function buildFromBacktraceFrame(string $file, int $line, array $backtrac $functionName = null; $rawFunctionName = null; - $strippedFilePath = $this->stripPrefixFromFilePath($file); + $strippedFilePath = $this->stripPrefixFromFilePath($this->options, $file); if (isset($backtraceFrame['class']) && isset($backtraceFrame['function'])) { $functionName = $backtraceFrame['class']; if (str_starts_with($functionName, Frame::ANONYMOUS_CLASS_PREFIX)) { - $functionName = Frame::ANONYMOUS_CLASS_PREFIX . $this->stripPrefixFromFilePath(substr($backtraceFrame['class'], \strlen(Frame::ANONYMOUS_CLASS_PREFIX))); + $functionName = Frame::ANONYMOUS_CLASS_PREFIX . $this->stripPrefixFromFilePath($this->options, substr($backtraceFrame['class'], \strlen(Frame::ANONYMOUS_CLASS_PREFIX))); } $rawFunctionName = sprintf('%s::%s', $backtraceFrame['class'], $backtraceFrame['function']); @@ -92,22 +95,6 @@ public function buildFromBacktraceFrame(string $file, int $line, array $backtrac ); } - /** - * Removes from the given file path the specified prefixes. - * - * @param string $filePath The path to the file - */ - private function stripPrefixFromFilePath(string $filePath): string - { - foreach ($this->options->getPrefixes() as $prefix) { - if (str_starts_with($filePath, $prefix)) { - return mb_substr($filePath, mb_strlen($prefix)); - } - } - - return $filePath; - } - /** * Checks whether a certain frame should be marked as "in app" or not. * diff --git a/src/Profiling/Profile.php b/src/Profiling/Profile.php index b6e974d37..63acbcd04 100644 --- a/src/Profiling/Profile.php +++ b/src/Profiling/Profile.php @@ -8,6 +8,8 @@ use Sentry\Context\RuntimeContext; use Sentry\Event; use Sentry\EventId; +use Sentry\Options; +use Sentry\Util\PrefixStripper; use Sentry\Util\SentryUid; /** @@ -73,6 +75,8 @@ */ final class Profile { + use PrefixStripper; + /** * @var string The version of the profile format */ @@ -108,6 +112,16 @@ final class Profile */ private $eventId; + /** + * @var Options|null + */ + private $options; + + public function __construct(?Options $options = null) + { + $this->options = $options; + } + public function setStartTimeStamp(float $startTimeStamp): void { $this->startTimeStamp = $startTimeStamp; @@ -160,8 +174,7 @@ public function getFormattedData(Event $event): ?array foreach ($loggedStacks as $stackId => $stack) { foreach ($stack['trace'] as $frame) { $absolutePath = (string) $frame['file']; - // TODO(michi) Strip the file path based on the `prefixes` option - $file = $absolutePath; + $file = $this->stripPrefixFromFilePath($this->options, $absolutePath); $module = null; if (isset($frame['class'], $frame['function'])) { diff --git a/src/Profiling/Profiler.php b/src/Profiling/Profiler.php index 463112fef..956c213d0 100644 --- a/src/Profiling/Profiler.php +++ b/src/Profiling/Profiler.php @@ -4,6 +4,8 @@ namespace Sentry\Profiling; +use Sentry\Options; + /** * @internal */ @@ -29,9 +31,9 @@ final class Profiler */ private const MAX_STACK_DEPTH = 128; - public function __construct() + public function __construct(?Options $options = null) { - $this->profile = new Profile(); + $this->profile = new Profile($options); $this->initProfiler(); } diff --git a/src/Tracing/Transaction.php b/src/Tracing/Transaction.php index ab9bbe485..b4dcc09c1 100644 --- a/src/Tracing/Transaction.php +++ b/src/Tracing/Transaction.php @@ -121,7 +121,10 @@ public function detachSpanRecorder(): void public function initProfiler(): void { if (null === $this->profiler) { - $this->profiler = new Profiler(); + $client = $this->hub->getClient(); + $options = null !== $client ? $client->getOptions() : null; + + $this->profiler = new Profiler($options); } } diff --git a/src/Util/PrefixStripper.php b/src/Util/PrefixStripper.php new file mode 100644 index 000000000..35c261312 --- /dev/null +++ b/src/Util/PrefixStripper.php @@ -0,0 +1,28 @@ +getPrefixes() as $prefix) { + if (str_starts_with($filePath, $prefix)) { + return mb_substr($filePath, mb_strlen($prefix)); + } + } + + return $filePath; + } +} diff --git a/tests/Profiling/ProfileTest.php b/tests/Profiling/ProfileTest.php index dab6e62d5..184cecdd6 100644 --- a/tests/Profiling/ProfileTest.php +++ b/tests/Profiling/ProfileTest.php @@ -9,6 +9,7 @@ use Sentry\Context\RuntimeContext; use Sentry\Event; use Sentry\EventId; +use Sentry\Options; use Sentry\Profiling\Profile; final class ProfileTest extends TestCase @@ -16,9 +17,9 @@ final class ProfileTest extends TestCase /** * @dataProvider formattedDataDataProvider */ - public function testGetFormattedData(Event $event, array $excimerLog, $expectedData): void + public function testGetFormattedData(Event $event, array $excimerLog, $expectedData, ?Options $options = null): void { - $profile = new Profile(); + $profile = new Profile($options); // 2022-02-28T09:41:00Z $profile->setStartTimeStamp(1677573660.0000); @@ -50,60 +51,178 @@ public static function formattedDataDataProvider(): \Generator 'aarch64' )); + $excimerLog = [ + [ + 'trace' => [ + [ + 'file' => '/var/www/html/index.php', + 'line' => 42, + ], + ], + 'timestamp' => 0.001, + ], + [ + 'trace' => [ + [ + 'file' => '/var/www/html/index.php', + 'line' => 42, + ], + [ + 'class' => 'Function', + 'function' => 'doStuff', + 'file' => '/var/www/html/function.php', + 'line' => 84, + ], + ], + 'timestamp' => 0.002, + ], + [ + 'trace' => [ + [ + 'file' => '/var/www/html/index.php', + 'line' => 42, + ], + [ + 'class' => 'Function', + 'function' => 'doStuff', + 'file' => '/var/www/html/function.php', + 'line' => 84, + ], + [ + 'class' => 'Class\Something', + 'function' => 'run', + 'file' => '/var/www/html/class.php', + 'line' => 42, + ], + [ + 'function' => '{closure}', + 'file' => '/var/www/html/index.php', + 'line' => 126, + ], + ], + 'timestamp' => 0.003, + ], + ]; + yield [ $event, + $excimerLog, [ - [ - 'trace' => [ + 'device' => [ + 'architecture' => 'aarch64', + ], + 'event_id' => '815e57b4bb134056ab1840919834689d', + 'os' => [ + 'name' => 'macOS', + 'version' => '13.2.1', + 'build_number' => '22D68', + ], + 'platform' => 'php', + 'release' => '1.0.0', + 'environment' => 'dev', + 'runtime' => [ + 'name' => 'php', + 'version' => '8.2.3', + ], + 'timestamp' => '2023-02-28T08:41:00.000+00:00', + 'transaction' => [ + 'id' => 'fc9442f5aef34234bb22b9a615e30ccd', + 'name' => 'GET /', + 'trace_id' => '566e3688a61d4bc888951642d6f14a19', + 'active_thread_id' => '0', + ], + 'version' => '1', + 'profile' => [ + 'frames' => [ [ - 'file' => '/var/www/html/index.php', - 'line' => 42, + 'filename' => '/var/www/html/index.php', + 'abs_path' => '/var/www/html/index.php', + 'module' => null, + 'function' => '/var/www/html/index.php', + 'lineno' => 42, ], - ], - 'timestamp' => 0.001, - ], - [ - 'trace' => [ [ - 'file' => '/var/www/html/index.php', - 'line' => 42, + 'filename' => '/var/www/html/index.php', + 'abs_path' => '/var/www/html/index.php', + 'module' => null, + 'function' => '/var/www/html/index.php', + 'lineno' => 42, ], [ - 'class' => 'Function', - 'function' => 'doStuff', - 'file' => '/var/www/html/function.php', - 'line' => 84, + 'filename' => '/var/www/html/function.php', + 'abs_path' => '/var/www/html/function.php', + 'module' => 'Function', + 'function' => 'Function::doStuff', + 'lineno' => 84, ], - ], - 'timestamp' => 0.002, - ], - [ - 'trace' => [ [ - 'file' => '/var/www/html/index.php', - 'line' => 42, + 'filename' => '/var/www/html/index.php', + 'abs_path' => '/var/www/html/index.php', + 'module' => null, + 'function' => '/var/www/html/index.php', + 'lineno' => 42, ], [ - 'class' => 'Function', - 'function' => 'doStuff', - 'file' => '/var/www/html/function.php', - 'line' => 84, + 'filename' => '/var/www/html/function.php', + 'abs_path' => '/var/www/html/function.php', + 'module' => 'Function', + 'function' => 'Function::doStuff', + 'lineno' => 84, ], [ - 'class' => 'Class\Something', - 'function' => 'run', - 'file' => '/var/www/html/class.php', - 'line' => 42, + 'filename' => '/var/www/html/class.php', + 'abs_path' => '/var/www/html/class.php', + 'module' => 'Class\Something', + 'function' => 'Class\Something::run', + 'lineno' => 42, ], [ + 'filename' => '/var/www/html/index.php', + 'abs_path' => '/var/www/html/index.php', + 'module' => null, 'function' => '{closure}', - 'file' => '/var/www/html/index.php', - 'line' => 126, + 'lineno' => 126, + ], + ], + 'samples' => [ + [ + 'elapsed_since_start_ns' => 1000000, + 'stack_id' => 0, + 'thread_id' => '0', + ], + [ + 'elapsed_since_start_ns' => 2000000, + 'stack_id' => 1, + 'thread_id' => '0', + ], + [ + 'elapsed_since_start_ns' => 3000000, + 'stack_id' => 2, + 'thread_id' => '0', + ], + ], + 'stacks' => [ + [ + 0, + ], + [ + 1, + 2, + ], + [ + 3, + 4, + 5, + 6, ], ], - 'timestamp' => 0.003, ], ], + ]; + + yield [ + $event, + $excimerLog, [ 'device' => [ 'architecture' => 'aarch64', @@ -132,49 +251,49 @@ public static function formattedDataDataProvider(): \Generator 'profile' => [ 'frames' => [ [ - 'filename' => '/var/www/html/index.php', + 'filename' => '/index.php', 'abs_path' => '/var/www/html/index.php', 'module' => null, - 'function' => '/var/www/html/index.php', + 'function' => '/index.php', 'lineno' => 42, ], [ - 'filename' => '/var/www/html/index.php', + 'filename' => '/index.php', 'abs_path' => '/var/www/html/index.php', 'module' => null, - 'function' => '/var/www/html/index.php', + 'function' => '/index.php', 'lineno' => 42, ], [ - 'filename' => '/var/www/html/function.php', + 'filename' => '/function.php', 'abs_path' => '/var/www/html/function.php', 'module' => 'Function', 'function' => 'Function::doStuff', 'lineno' => 84, ], [ - 'filename' => '/var/www/html/index.php', + 'filename' => '/index.php', 'abs_path' => '/var/www/html/index.php', 'module' => null, - 'function' => '/var/www/html/index.php', + 'function' => '/index.php', 'lineno' => 42, ], [ - 'filename' => '/var/www/html/function.php', + 'filename' => '/function.php', 'abs_path' => '/var/www/html/function.php', 'module' => 'Function', 'function' => 'Function::doStuff', 'lineno' => 84, ], [ - 'filename' => '/var/www/html/class.php', + 'filename' => '/class.php', 'abs_path' => '/var/www/html/class.php', 'module' => 'Class\Something', 'function' => 'Class\Something::run', 'lineno' => 42, ], [ - 'filename' => '/var/www/html/index.php', + 'filename' => '/index.php', 'abs_path' => '/var/www/html/index.php', 'module' => null, 'function' => '{closure}', @@ -215,6 +334,9 @@ public static function formattedDataDataProvider(): \Generator ], ], ], + new Options([ + 'prefixes' => ['/var/www/html'], + ]), ]; yield 'Too little samples' => [