X-Git-Url: http://www.aleph1.co.uk/gitweb/?a=blobdiff_plain;f=web%2Fmodules%2Fcontrib%2Fdevel%2Fwebprofiler%2Fsrc%2FCommand%2FBenchmarkCommand.php;fp=web%2Fmodules%2Fcontrib%2Fdevel%2Fwebprofiler%2Fsrc%2FCommand%2FBenchmarkCommand.php;h=c33905b8b29b20bc37a6bcd269537a50e1726631;hb=a2bd1bf0c2c1f1a17d188f4dc0726a45494cefae;hp=0000000000000000000000000000000000000000;hpb=57c063afa3f66b07c4bbddc2d6129a96d90f0aad;p=yaffs-website diff --git a/web/modules/contrib/devel/webprofiler/src/Command/BenchmarkCommand.php b/web/modules/contrib/devel/webprofiler/src/Command/BenchmarkCommand.php new file mode 100644 index 000000000..c33905b8b --- /dev/null +++ b/web/modules/contrib/devel/webprofiler/src/Command/BenchmarkCommand.php @@ -0,0 +1,311 @@ +setName('webprofiler:benchmark') + ->setDescription($this->trans('commands.webprofiler.benchmark.description')) + ->addArgument('url', InputArgument::REQUIRED, $this->trans('commands.webprofiler.benchmark.arguments.url')) + ->addOption('runs', NULL, InputOption::VALUE_REQUIRED, $this->trans('commands.webprofiler.benchmark.options.runs'), 100) + ->addOption('file', NULL, InputOption::VALUE_REQUIRED, $this->trans('commands.webprofiler.benchmark.options.file')) + ->addOption('cache-rebuild', 'cr', InputOption::VALUE_NONE, $this->trans('commands.webprofiler.benchmark.options.cache_rebuild')); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $runs = $input->getOption('runs'); + $file = $input->getOption('file'); + $cache_rebuild = $input->getOption('cache-rebuild'); + + // http://username:password@hostname/ + $url = $input->getArgument('url'); + $url_components = parse_url($url); + $login = isset($url_components['user']) && isset($url_components['pass']); + + $steps = 3; + + if ($cache_rebuild) { + $steps++; + } + + if ($login) { + $steps++; + } + + /** @var \Drupal\Core\Http\Client $client */ + $client = $this->container->get('http_client'); + + $progress = new ProgressBar($output, $runs + $steps); + $progress->setFormat(' %current%/%max% [%bar%] %percent:3s%% %message%'); + + if ($cache_rebuild) { + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.cache_rebuild')); + $this->RebuildCache(); + $progress->advance(); + } + + if ($login) { + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.login')); + $login_url = "{$url_components['scheme']}://{$url_components['host']}/user/login"; + + // Enable cookies storage. + $cookieJar = new CookieJar(); + $client->setDefaultOption('cookies', $cookieJar); + + // Retrieve a form_build_id using the DomCrawler component. + $response = $client->get($login_url)->getBody()->getContents(); + $crawler = new Crawler($response); + $form_build_id = $crawler->filter('#user-login-form input[name=form_build_id]') + ->attr('value'); + $op = $crawler->filter('#user-login-form input[name=op]')->attr('value'); + + // Login a user. + $response = $client->post($login_url, [ + 'body' => [ + 'name' => $url_components['user'], + 'pass' => $url_components['pass'], + 'form_build_id' => $form_build_id, + 'form_id' => 'user_login_form', + 'op' => $op, + ] + ]); + $progress->advance(); + + if ($response->getStatusCode() != 200) { + throw new \Exception($this->trans('commands.webprofiler.benchmark.messages.error_login')); + } + } + + $datas = []; + for ($i = 0; $i < $runs; $i++) { + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.get')); + $datas[] = $this->getData($client, $url); + $progress->advance(); + } + + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.compute_avg')); + $avg = $this->computeAvg($datas); + $progress->advance(); + + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.compute_median')); + $median = $this->computePercentile($datas, 50); + $progress->advance(); + + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.compute_95percentile')); + $percentile95 = $this->computePercentile($datas, 95); + $progress->advance(); + + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.git_hash')); + $gitHash = $this->getGitHash(); + $progress->advance(); + + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.yaml')); + $yaml = $this->generateYaml($gitHash, $runs, $url, $avg, $median, $percentile95); + $progress->advance(); + + $progress->setMessage($this->trans('commands.webprofiler.benchmark.progress.done')); + $progress->finish(); + $output->writeln(''); + + if ($file) { + file_put_contents($file, $yaml); + } + else { + $output->writeln($yaml); + } + + } + + /** + * @param \GuzzleHttp\ClientInterface $client + * @param $url + * + * @return array + */ + private function getData(ClientInterface $client, $url) { + /** @var \GuzzleHttp\Message\ResponseInterface $response */ + $response = $client->get($url); + + $token = $response->getHeader('X-Debug-Token'); + + /** @var \Drupal\webprofiler\Profiler\Profiler $profiler */ + $profiler = $this->container->get('profiler'); + + /** @var \Symfony\Component\HttpKernel\Profiler\Profile $profile */ + $profile = $profiler->loadProfile($token); + + /** @var \Drupal\webprofiler\DataCollector\TimeDataCollector $timeDataCollector */ + $timeDataCollector = $profile->getCollector('time'); + + return new BenchmarkData( + $token, + $timeDataCollector->getMemory(), + $timeDataCollector->getDuration()); + } + + /** + * @param \Drupal\webprofiler\Command\BenchmarkData[] $datas + * + * @return \Drupal\webprofiler\Command\BenchmarkData + */ + private function computeAvg($datas) { + $profiles = count($datas); + $totalTime = 0; + $totalMemory = 0; + + foreach ($datas as $data) { + $totalTime += $data->getTime(); + $totalMemory += $data->getMemory(); + } + + return new BenchmarkData(NULL, $totalMemory / $profiles, $totalTime / $profiles); + } + + /** + * Computes percentile using The Nearest Rank method. + * + * @param \Drupal\webprofiler\Command\BenchmarkData[] $datas + * @param $percentile + * + * @return \Drupal\webprofiler\Command\BenchmarkData + * + * @throws \Exception + */ + private function computePercentile($datas, $percentile) { + if ($percentile < 0 || $percentile > 100) { + throw new \Exception('Percentile has to be between 0 and 100'); + } + + $profiles = count($datas); + + $n = ceil((($percentile / 100) * $profiles)); + $index = (int) $n - 1; + + $orderedTime = $datas; + $this->getOrderedDatas($orderedTime, 'Time'); + + $orderedMemory = $datas; + $this->getOrderedDatas($orderedMemory, 'Memory'); + + return new BenchmarkData(NULL, $orderedMemory[$index]->getMemory(), $orderedTime[$index]->getTime()); + } + + /** + * @return string + */ + private function getGitHash() { + try { + $process = new Process('git rev-parse HEAD'); + $process->setTimeout(3600); + $process->run(); + $git_hash = $process->getOutput(); + } catch (\Exception $e) { + $git_hash = $this->trans('commands.webprofiler.benchmark.messages.not_git'); + } + + return $git_hash; + } + + /** + * @param \Drupal\webprofiler\Command\BenchmarkData[] $datas + * @param $string + * + * @return array + */ + private function getOrderedDatas(&$datas, $string) { + usort($datas, function ($a, $b) use ($string) { + $method = 'get' . $string; + if ($a->{$method} > $b->{$method}) { + return 1; + } + if ($a->{$method} < $b->{$method}) { + return -1; + } + return 0; + }); + } + + /** + * Rebuilds Drupal cache. + */ + protected function RebuildCache() { + require_once DRUPAL_ROOT . '/core/includes/utility.inc'; + $kernelHelper = $this->getHelper('kernel'); + $classLoader = $kernelHelper->getClassLoader(); + $request = $kernelHelper->getRequest(); + drupal_rebuild($classLoader, $request); + } + + /** + * @param $gitHash + * @param $runs + * @param $url + * @param \Drupal\webprofiler\Command\BenchmarkData $avg + * @param \Drupal\webprofiler\Command\BenchmarkData $median + * @param \Drupal\webprofiler\Command\BenchmarkData $percentile95 + * + * @return string + */ + private function generateYaml($gitHash, $runs, $url, BenchmarkData $avg, BenchmarkData $median, BenchmarkData $percentile95) { + $yaml = Yaml::dump([ + 'date' => date($this->trans('commands.webprofiler.list.rows.time'), time()), + 'git_commit' => $gitHash, + 'number_of_runs' => $runs, + 'url' => $url, + 'results' => [ + 'average' => [ + 'time' => sprintf('%.0f ms', $avg->getTime()), + 'memory' => sprintf('%.1f MB', $avg->getMemory() / 1024 / 1024), + ], + 'median' => [ + 'time' => sprintf('%.0f ms', $median->getTime()), + 'memory' => sprintf('%.1f MB', $median->getMemory() / 1024 / 1024), + ], + '95_percentile' => [ + 'time' => sprintf('%.0f ms', $percentile95->getTime()), + 'memory' => sprintf('%.1f MB', $percentile95->getMemory() / 1024 / 1024), + ], + ], + ]); + return $yaml; + } + + /** + * {@inheritdoc} + */ + public function showMessage($output, $message, $type = 'info') { + } + +}