Skip to content

Commit

Permalink
Merge c30fb2b into 839ca54
Browse files Browse the repository at this point in the history
  • Loading branch information
sj-i committed Oct 8, 2021
2 parents 839ca54 + c30fb2b commit c3312f9
Showing 1 changed file with 124 additions and 0 deletions.
124 changes: 124 additions & 0 deletions src/Command/Converter/SpeedscopeCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php

/**
* This file is part of the sj-i/php-profiler package.
*
* (c) sji <sji@sj-i.dev>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace PhpProfiler\Command\Converter;

use PhpCast\Cast;
use PhpProfiler\Lib\PhpInternals\Opcodes\OpcodeV80;
use PhpProfiler\Lib\PhpInternals\Types\Zend\Opline;
use PhpProfiler\Lib\PhpProcessReader\CallFrame;
use PhpProfiler\Lib\PhpProcessReader\CallTrace;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

final class SpeedscopeCommand extends Command
{
public function configure(): void
{
$this->setName('converter:speedscope')
->setDescription('convert traces to the speedscope file format')
;
}

public function execute(InputInterface $input, OutputInterface $output): int
{
$output->write(
json_encode(
$this->collectFrames(
$this->getTraceIterator(STDIN)
)
)
);
return 0;
}

/**
* @param resource $fp
* @return iterable<int, CallTrace>
*/
private function getTraceIterator($fp): iterable
{
$buffer = [];
while (($line = fgets($fp)) !== false) {
$line = trim($line);
if ($line !== '') {
$buffer[] = $line;
continue;
}
yield $this->parsePhpSpyCompatible($buffer);
$buffer = [];
}
}

/** @param string[] $buffer */
private function parsePhpSpyCompatible(array $buffer): CallTrace
{
$frames = [];
foreach ($buffer as $line_buffer) {
$result = explode(' ', $line_buffer);
[$_depth, $name, $file_line] = $result;
[$file, $line] = explode(':', $file_line);
$frames[] = new CallFrame(
'',
$name,
$file,
new Opline(0, 0, 0, 0, Cast::toInt($line), new OpcodeV80(0), 0, 0, 0),
);
}
return new CallTrace(...$frames);
}

/** @param iterable<CallTrace> $call_frames */
private function collectFrames(iterable $call_frames): array
{
$mapper = fn (array $value): string => \json_encode($value);
$trace_map = [];
$result_frames = [];
$sampled_stacks = [];
$counter = 0;
foreach ($call_frames as $frames) {
$sampled_stack = [];
foreach ($frames->call_frames as $call_frame) {
$frame = [
'name' => $call_frame->getFullyQualifiedFunctionName(),
'file' => $call_frame->file_name,
'line' => $call_frame->getLineno(),
];
$mapper_key = $mapper($frame);
if (!isset($trace_map[$mapper_key])) {
$result_frames[] = $frame;
$trace_map[$mapper_key] = array_key_last($result_frames);
}
$sampled_stack[] = $trace_map[$mapper_key];
}
$sampled_stacks[] = $sampled_stack;
$counter++;
}
return [
"\$schema" => "https://www.speedscope.app/file-format-schema.json",
'shared' => [
'frames' => $result_frames,
],
'profiles' => [[
'type' => 'sampled',
'name' => 'test',
'unit' => 'none',
'startValue' => 0,
'endValue' => $counter,
'samples' => $sampled_stacks,
'weights' => array_fill(0, count($sampled_stacks), 1),
]]
];
}
}

0 comments on commit c3312f9

Please sign in to comment.