Skip to content

Commit

Permalink
Merge pull request #30823 from nextcloud/work/profiler
Browse files Browse the repository at this point in the history
Built-in profiler

This adds the required API for collecting information about requests. This information
can then be displayed with the new 'profiler' app.
  • Loading branch information
CarlSchwan authored Apr 4, 2022
2 parents 498d3ae + 7d272c5 commit 135bdb3
Show file tree
Hide file tree
Showing 43 changed files with 2,001 additions and 190 deletions.
1 change: 1 addition & 0 deletions apps/dav/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@
'OCA\\DAV\\Migration\\Version1016Date20201109085907' => $baseDir . '/../lib/Migration/Version1016Date20201109085907.php',
'OCA\\DAV\\Migration\\Version1017Date20210216083742' => $baseDir . '/../lib/Migration/Version1017Date20210216083742.php',
'OCA\\DAV\\Migration\\Version1018Date20210312100735' => $baseDir . '/../lib/Migration/Version1018Date20210312100735.php',
'OCA\\DAV\\Profiler\\ProfilerPlugin' => $baseDir . '/../lib/Profiler/ProfilerPlugin.php',
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningNode.php',
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php',
'OCA\\DAV\\RootCollection' => $baseDir . '/../lib/RootCollection.php',
Expand Down
1 change: 1 addition & 0 deletions apps/dav/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Migration\\Version1016Date20201109085907' => __DIR__ . '/..' . '/../lib/Migration/Version1016Date20201109085907.php',
'OCA\\DAV\\Migration\\Version1017Date20210216083742' => __DIR__ . '/..' . '/../lib/Migration/Version1017Date20210216083742.php',
'OCA\\DAV\\Migration\\Version1018Date20210312100735' => __DIR__ . '/..' . '/../lib/Migration/Version1018Date20210312100735.php',
'OCA\\DAV\\Profiler\\ProfilerPlugin' => __DIR__ . '/..' . '/../lib/Profiler/ProfilerPlugin.php',
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningNode.php',
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php',
'OCA\\DAV\\RootCollection' => __DIR__ . '/..' . '/../lib/RootCollection.php',
Expand Down
47 changes: 47 additions & 0 deletions apps/dav/lib/Profiler/ProfilerPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php declare(strict_types = 1);
/**
* @copyright 2021 Carl Schwan <carl@carlschwan.eu>
*
* @author Carl Schwan <carl@carlschwan.eu>
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\DAV\Profiler;

use OCP\IRequest;
use Sabre\DAV\Server;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;

class ProfilerPlugin extends \Sabre\DAV\ServerPlugin {
private IRequest $request;

public function __construct(IRequest $request) {
$this->request = $request;
}

/** @return void */
public function initialize(Server $server) {
$server->on('afterMethod:*', [$this, 'afterMethod']);
}

/** @return void */
public function afterMethod(RequestInterface $request, ResponseInterface $response) {
$response->addHeader('X-Debug-Token', $this->request->getId());
}
}
31 changes: 21 additions & 10 deletions apps/dav/lib/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@

use OCA\DAV\Connector\Sabre\RequestIdHeaderPlugin;
use OCP\Diagnostics\IEventLogger;
use OCP\Profiler\IProfiler;
use OCA\DAV\Profiler\ProfilerPlugin;
use OCP\AppFramework\Http\Response;
use Psr\Log\LoggerInterface;
use OCA\DAV\AppInfo\PluginManager;
use OCA\DAV\CalDAV\BirthdayService;
Expand Down Expand Up @@ -78,17 +81,19 @@
use SearchDAV\DAV\SearchPlugin;

class Server {
private IRequest $request;
private string $baseUri;
public Connector\Sabre\Server $server;
private IProfiler $profiler;

public function __construct(IRequest $request, string $baseUri) {
$this->profiler = \OC::$server->get(IProfiler::class);
if ($this->profiler->isEnabled()) {
/** @var IEventLogger $eventLogger */
$eventLogger = \OC::$server->get(IEventLogger::class);
$eventLogger->start('runtime', 'DAV Runtime');
}

/** @var IRequest */
private $request;

/** @var string */
private $baseUri;

/** @var Connector\Sabre\Server */
public $server;

public function __construct(IRequest $request, $baseUri) {
$this->request = $request;
$this->baseUri = $baseUri;
$logger = \OC::$server->getLogger();
Expand All @@ -115,6 +120,7 @@ public function __construct(IRequest $request, $baseUri) {
$this->server->httpRequest->setUrl($this->request->getRequestUri());
$this->server->setBaseUri($this->baseUri);

$this->server->addPlugin(new ProfilerPlugin($this->request));
$this->server->addPlugin(new BlockLegacyClientPlugin(\OC::$server->getConfig()));
$this->server->addPlugin(new AnonymousOptionsPlugin());
$authPlugin = new Plugin();
Expand Down Expand Up @@ -343,6 +349,11 @@ public function exec() {
$eventLogger->start('dav_server_exec', '');
$this->server->exec();
$eventLogger->end('dav_server_exec');
if ($this->profiler->isEnabled()) {
$eventLogger->end('runtime');
$profile = $this->profiler->collect(\OC::$server->get(IRequest::class), new Response());
$this->profiler->saveProfile($profile);
}
}

private function requestIsForSubtree(array $subTrees): bool {
Expand Down
1 change: 1 addition & 0 deletions apps/user_ldap/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
'OCA\\User_LDAP\\ConnectionFactory' => $baseDir . '/../lib/ConnectionFactory.php',
'OCA\\User_LDAP\\Controller\\ConfigAPIController' => $baseDir . '/../lib/Controller/ConfigAPIController.php',
'OCA\\User_LDAP\\Controller\\RenewPasswordController' => $baseDir . '/../lib/Controller/RenewPasswordController.php',
'OCA\\User_LDAP\\DataCollector\\LdapDataCollector' => $baseDir . '/../lib/DataCollector/LdapDataCollector.php',
'OCA\\User_LDAP\\Events\\GroupBackendRegistered' => $baseDir . '/../lib/Events/GroupBackendRegistered.php',
'OCA\\User_LDAP\\Events\\UserBackendRegistered' => $baseDir . '/../lib/Events/UserBackendRegistered.php',
'OCA\\User_LDAP\\Exceptions\\AttributeNotSet' => $baseDir . '/../lib/Exceptions/AttributeNotSet.php',
Expand Down
1 change: 1 addition & 0 deletions apps/user_ldap/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class ComposerStaticInitUser_LDAP
'OCA\\User_LDAP\\ConnectionFactory' => __DIR__ . '/..' . '/../lib/ConnectionFactory.php',
'OCA\\User_LDAP\\Controller\\ConfigAPIController' => __DIR__ . '/..' . '/../lib/Controller/ConfigAPIController.php',
'OCA\\User_LDAP\\Controller\\RenewPasswordController' => __DIR__ . '/..' . '/../lib/Controller/RenewPasswordController.php',
'OCA\\User_LDAP\\DataCollector\\LdapDataCollector' => __DIR__ . '/..' . '/../lib/DataCollector/LdapDataCollector.php',
'OCA\\User_LDAP\\Events\\GroupBackendRegistered' => __DIR__ . '/..' . '/../lib/Events/GroupBackendRegistered.php',
'OCA\\User_LDAP\\Events\\UserBackendRegistered' => __DIR__ . '/..' . '/../lib/Events/UserBackendRegistered.php',
'OCA\\User_LDAP\\Exceptions\\AttributeNotSet' => __DIR__ . '/..' . '/../lib/Exceptions/AttributeNotSet.php',
Expand Down
50 changes: 50 additions & 0 deletions apps/user_ldap/lib/DataCollector/LdapDataCollector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php declare(strict_types = 1);
/**
* @copyright 2022 Carl Schwan <carl@carlschwan.eu>
*
* @author Carl Schwan <carl@carlschwan.eu>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\User_LDAP\DataCollector;

use OC\AppFramework\Http\Request;
use OCP\AppFramework\Http\Response;
use OCP\DataCollector\AbstractDataCollector;

class LdapDataCollector extends AbstractDataCollector {
public function startLdapRequest(string $query, array $args): void {
$this->data[] = [
'start' => microtime(true),
'query' => $query,
'args' => $args,
'end' => microtime(true),
];
}

public function stopLastLdapRequest(): void {
$this->data[count($this->data) - 1]['end'] = microtime(true);
}

public function getName(): string {
return 'ldap';
}

public function collect(Request $request, Response $response, \Throwable $exception = null): void {
}
}
32 changes: 23 additions & 9 deletions apps/user_ldap/lib/LDAP.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* @author Robin McCorkell <robin@mccorkell.me.uk>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Roger Szabo <roger.szabo@web.de>
* @author Carl Schwan <carl@carlschwan.eu>
*
* @license AGPL-3.0
*
Expand All @@ -32,7 +33,9 @@
*/
namespace OCA\User_LDAP;

use OCP\Profiler\IProfiler;
use OC\ServerNotAvailableException;
use OCA\User_LDAP\DataCollector\LdapDataCollector;
use OCA\User_LDAP\Exceptions\ConstraintViolationException;
use OCA\User_LDAP\PagedResults\IAdapter;
use OCA\User_LDAP\PagedResults\Php73;
Expand All @@ -45,9 +48,18 @@ class LDAP implements ILDAPWrapper {
/** @var IAdapter */
protected $pagedResultsAdapter;

private ?LdapDataCollector $dataCollector = null;

public function __construct(string $logFile = '') {
$this->pagedResultsAdapter = new Php73();
$this->logFile = $logFile;

/** @var IProfiler $profiler */
$profiler = \OC::$server->get(IProfiler::class);
if ($profiler->isEnabled()) {
$this->dataCollector = new LdapDataCollector();
$profiler->add($this->dataCollector);
}
}

/**
Expand Down Expand Up @@ -295,24 +307,26 @@ protected function invokeLDAPMethod() {
if ($this->isResultFalse($result)) {
$this->postFunctionCall();
}
if ($this->dataCollector !== null) {
$this->dataCollector->stopLastLdapRequest();
}
return $result;
}
return null;
}

/**
* @param string $functionName
* @param array $args
*/
private function preFunctionCall($functionName, $args) {
private function preFunctionCall(string $functionName, array $args): void {
$this->curFunc = $functionName;
$this->curArgs = $args;

if ($this->dataCollector !== null) {
$args = array_map(fn ($item) => (!$this->isResource($item) ? $item : '(resource)'), $this->curArgs);

$this->dataCollector->startLdapRequest($this->curFunc, $args);
}

if ($this->logFile !== '' && is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
$args = array_reduce($this->curArgs, static function (array $carry, $item): array {
$carry[] = !is_resource($item) ? $item : '(resource)';
return $carry;
}, []);
$args = array_map(fn ($item) => (!$this->isResource($item) ? $item : '(resource)'), $this->curArgs);
file_put_contents(
$this->logFile,
$this->curFunc . '::' . json_encode($args) . "\n",
Expand Down
6 changes: 0 additions & 6 deletions build/psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,6 @@
</RedundantCondition>
<TypeDoesNotContainType occurrences="2">
<code>get_class($res) === 'OpenSSLAsymmetricKey'</code>
<code>is_object($res)</code>
</TypeDoesNotContainType>
</file>
<file src="apps/encryption/lib/Crypto/EncryptAll.php">
Expand Down Expand Up @@ -2638,11 +2637,6 @@
<code>$default</code>
</MoreSpecificImplementedParamType>
</file>
<file src="lib/private/AppFramework/Utility/SimpleContainer.php">
<UndefinedMethod occurrences="1">
<code>getName</code>
</UndefinedMethod>
</file>
<file src="lib/private/Archive/TAR.php">
<UndefinedDocblockClass occurrences="1">
<code>$this-&gt;tar-&gt;extractInString($path)</code>
Expand Down
8 changes: 8 additions & 0 deletions config/config.sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,14 @@
*/
'log_rotate_size' => 100 * 1024 * 1024,

/**
* Enable built-in profiler. Helpful when trying to debug performance
* issues.
*
* Note that this has a performance impact and shouldn't be enabled
* on production.
*/
'profiler' => false,

/**
* Alternate Code Locations
Expand Down
2 changes: 1 addition & 1 deletion core/templates/layout.user.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,6 @@
<div id="content" class="app-<?php p($_['appid']) ?>" role="main">
<?php print_unescaped($_['content']); ?>
</div>

<div id="profiler-toolbar"></div>
</body>
</html>
5 changes: 3 additions & 2 deletions lib/autoloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

use \OCP\AutoloadNotAllowedException;
use OCP\ILogger;
use OCP\ICache;

class Autoloader {
/** @var bool */
Expand Down Expand Up @@ -182,9 +183,9 @@ public function load(string $class): bool {
/**
* Sets the optional low-latency cache for class to path mapping.
*
* @param \OC\Memcache\Cache $memoryCache Instance of memory cache.
* @param ICache $memoryCache Instance of memory cache.
*/
public function setMemoryCache(\OC\Memcache\Cache $memoryCache = null): void {
public function setMemoryCache(ICache $memoryCache = null): void {
$this->memoryCache = $memoryCache;
}
}
1 change: 1 addition & 0 deletions lib/base.php
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ public static function init() {
$eventLogger->end('request');
});
$eventLogger->start('boot', 'Initialize');
$eventLogger->start('runtime', 'Runtime (total - autoloader)');

// Override php.ini and log everything if we're troubleshooting
if (self::$config->getValue('loglevel') === ILogger::DEBUG) {
Expand Down
12 changes: 12 additions & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@
'OCP\\Dashboard\\RegisterWidgetEvent' => $baseDir . '/lib/public/Dashboard/RegisterWidgetEvent.php',
'OCP\\Dashboard\\Service\\IEventsService' => $baseDir . '/lib/public/Dashboard/Service/IEventsService.php',
'OCP\\Dashboard\\Service\\IWidgetsService' => $baseDir . '/lib/public/Dashboard/Service/IWidgetsService.php',
'OCP\\DataCollector\\AbstractDataCollector' => $baseDir . '/lib/public/DataCollector/AbstractDataCollector.php',
'OCP\\DataCollector\\IDataCollector' => $baseDir . '/lib/public/DataCollector/IDataCollector.php',
'OCP\\Defaults' => $baseDir . '/lib/public/Defaults.php',
'OCP\\Diagnostics\\IEvent' => $baseDir . '/lib/public/Diagnostics/IEvent.php',
'OCP\\Diagnostics\\IEventLogger' => $baseDir . '/lib/public/Diagnostics/IEventLogger.php',
Expand Down Expand Up @@ -467,6 +469,8 @@
'OCP\\Preview\\IVersionedPreviewFile' => $baseDir . '/lib/public/Preview/IVersionedPreviewFile.php',
'OCP\\Profile\\ILinkAction' => $baseDir . '/lib/public/Profile/ILinkAction.php',
'OCP\\Profile\\ParameterDoesNotExistException' => $baseDir . '/lib/public/Profile/ParameterDoesNotExistException.php',
'OCP\\Profiler\\IProfile' => $baseDir . '/lib/public/Profiler/IProfile.php',
'OCP\\Profiler\\IProfiler' => $baseDir . '/lib/public/Profiler/IProfiler.php',
'OCP\\Remote\\Api\\IApiCollection' => $baseDir . '/lib/public/Remote/Api/IApiCollection.php',
'OCP\\Remote\\Api\\IApiFactory' => $baseDir . '/lib/public/Remote/Api/IApiFactory.php',
'OCP\\Remote\\Api\\ICapabilitiesApi' => $baseDir . '/lib/public/Remote/Api/ICapabilitiesApi.php',
Expand Down Expand Up @@ -1025,6 +1029,7 @@
'OC\\DB\\Connection' => $baseDir . '/lib/private/DB/Connection.php',
'OC\\DB\\ConnectionAdapter' => $baseDir . '/lib/private/DB/ConnectionAdapter.php',
'OC\\DB\\ConnectionFactory' => $baseDir . '/lib/private/DB/ConnectionFactory.php',
'OC\\DB\\DbDataCollector' => $baseDir . '/lib/private/DB/DbDataCollector.php',
'OC\\DB\\Exceptions\\DbalException' => $baseDir . '/lib/private/DB/Exceptions/DbalException.php',
'OC\\DB\\MigrationException' => $baseDir . '/lib/private/DB/MigrationException.php',
'OC\\DB\\MigrationService' => $baseDir . '/lib/private/DB/MigrationService.php',
Expand All @@ -1035,6 +1040,7 @@
'OC\\DB\\MySQLMigrator' => $baseDir . '/lib/private/DB/MySQLMigrator.php',
'OC\\DB\\MySqlTools' => $baseDir . '/lib/private/DB/MySqlTools.php',
'OC\\DB\\OCSqlitePlatform' => $baseDir . '/lib/private/DB/OCSqlitePlatform.php',
'OC\\DB\\ObjectParameter' => $baseDir . '/lib/private/DB/ObjectParameter.php',
'OC\\DB\\OracleConnection' => $baseDir . '/lib/private/DB/OracleConnection.php',
'OC\\DB\\OracleMigrator' => $baseDir . '/lib/private/DB/OracleMigrator.php',
'OC\\DB\\PgSqlTools' => $baseDir . '/lib/private/DB/PgSqlTools.php',
Expand Down Expand Up @@ -1279,8 +1285,10 @@
'OC\\Memcache\\CASTrait' => $baseDir . '/lib/private/Memcache/CASTrait.php',
'OC\\Memcache\\Cache' => $baseDir . '/lib/private/Memcache/Cache.php',
'OC\\Memcache\\Factory' => $baseDir . '/lib/private/Memcache/Factory.php',
'OC\\Memcache\\LoggerWrapperCache' => $baseDir . '/lib/private/Memcache/LoggerWrapperCache.php',
'OC\\Memcache\\Memcached' => $baseDir . '/lib/private/Memcache/Memcached.php',
'OC\\Memcache\\NullCache' => $baseDir . '/lib/private/Memcache/NullCache.php',
'OC\\Memcache\\ProfilerWrapperCache' => $baseDir . '/lib/private/Memcache/ProfilerWrapperCache.php',
'OC\\Memcache\\Redis' => $baseDir . '/lib/private/Memcache/Redis.php',
'OC\\MemoryInfo' => $baseDir . '/lib/private/MemoryInfo.php',
'OC\\Migration\\BackgroundRepair' => $baseDir . '/lib/private/Migration/BackgroundRepair.php',
Expand Down Expand Up @@ -1347,6 +1355,10 @@
'OC\\Profile\\Actions\\WebsiteAction' => $baseDir . '/lib/private/Profile/Actions/WebsiteAction.php',
'OC\\Profile\\ProfileManager' => $baseDir . '/lib/private/Profile/ProfileManager.php',
'OC\\Profile\\TProfileHelper' => $baseDir . '/lib/private/Profile/TProfileHelper.php',
'OC\\Profiler\\FileProfilerStorage' => $baseDir . '/lib/private/Profiler/FileProfilerStorage.php',
'OC\\Profiler\\Profile' => $baseDir . '/lib/private/Profiler/Profile.php',
'OC\\Profiler\\Profiler' => $baseDir . '/lib/private/Profiler/Profiler.php',
'OC\\Profiler\\RoutingDataCollector' => $baseDir . '/lib/private/Profiler/RoutingDataCollector.php',
'OC\\RedisFactory' => $baseDir . '/lib/private/RedisFactory.php',
'OC\\Remote\\Api\\ApiBase' => $baseDir . '/lib/private/Remote/Api/ApiBase.php',
'OC\\Remote\\Api\\ApiCollection' => $baseDir . '/lib/private/Remote/Api/ApiCollection.php',
Expand Down
Loading

0 comments on commit 135bdb3

Please sign in to comment.