From 1ecf616f1c88b273a272c1d1a21e250efe96c3f8 Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Fri, 9 Feb 2018 13:00:07 -0800 Subject: [PATCH 1/8] Re-introduce the google-cloud-debugger binary script. The script is required in order to run on systems without sysv extensions. If the system cannot run sysv extensions, then the Agent will not attempt to register the Daemon with the BatchRunner. --- src/Debugger/Agent.php | 17 ++++-- src/Debugger/Daemon.php | 16 +++-- src/Debugger/bin/google-cloud-debugger | 85 ++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 9 deletions(-) create mode 100755 src/Debugger/bin/google-cloud-debugger diff --git a/src/Debugger/Agent.php b/src/Debugger/Agent.php index 1536d296869d..15a5b006dbce 100644 --- a/src/Debugger/Agent.php +++ b/src/Debugger/Agent.php @@ -43,6 +43,7 @@ class Agent { use BatchTrait; + use BatchDaemonTrait; use SysvTrait; /** @@ -99,10 +100,13 @@ public function __construct(array $options = []) ? $options['sourceRoot'] : dirname(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['file']); - $daemon = new Daemon([ - 'sourceRoot' => $this->sourceRoot, - 'storage' => $storage - ]); + if ($this->shouldStartDaemon()) { + $daemon = new Daemon([ + 'sourceRoot' => $this->sourceRoot, + 'storage' => $storage, + 'register' => true + ]); + } list($this->debuggeeId, $breakpoints) = $storage->load(); @@ -252,4 +256,9 @@ private function invalidateOpcache($breakpoint) return opcache_invalidate($this->sourceRoot . DIRECTORY_SEPARATOR . $breakpoint->location()->path(), true); } + + private function shouldStartDaemon() + { + return $this->isDaemonRunning() && $this->isSysvIPCLoaded(); + } } diff --git a/src/Debugger/Daemon.php b/src/Debugger/Daemon.php index 2cf4867c087e..f8ead0b4510b 100644 --- a/src/Debugger/Daemon.php +++ b/src/Debugger/Daemon.php @@ -88,7 +88,8 @@ class Daemon * @param array $options [optional] { * Configuration options. * - * @type string $sourceRoot The full path to the source root + * @type string $sourceRoot The full path to the source root. + * **Defaults to** the current working directory. * @type array $clientConfig The options to instantiate the default * DebuggerClient. * {@see Google\Cloud\Debugger\DebuggerClient::__construct()} @@ -118,6 +119,8 @@ class Daemon * batch daemon. **Defaults to** * {@see Google\Cloud\Core\Batch\OpisClosureSerializer} if the * `opis/closure` library is installed. + * @type bool $register Whether to start the worker in the background + * using the BatchRunner. **Defaults to** false. * } */ public function __construct(array $options = []) @@ -130,7 +133,8 @@ public function __construct(array $options = []) 'description' => null, 'debuggee' => null, 'labels' => null, - 'metadataProvider' => null + 'metadataProvider' => null, + 'register' => false ]; $this->setSerializableClientOptions($options); @@ -150,9 +154,11 @@ public function __construct(array $options = []) ? $options['storage'] : $this->defaultStorage(); - $this->setSimpleJobProperties($options + [ - 'identifier' => 'debugger-daemon' - ]); + if ($options['register']) { + $this->setSimpleJobProperties($options + [ + 'identifier' => 'debugger-daemon' + ]); + } } /** diff --git a/src/Debugger/bin/google-cloud-debugger b/src/Debugger/bin/google-cloud-debugger new file mode 100755 index 000000000000..29f8e5e6791e --- /dev/null +++ b/src/Debugger/bin/google-cloud-debugger @@ -0,0 +1,85 @@ +#!/usr/bin/env php + + +Options: + -c, --config=CONFIG_FILE If specified, load this file which should initialize and return a Debugger\Daemon instance. + -s, --source-root=SOURCE_ROOT Sets the source root for the Daemon. Ignored if --config is specified. + + +EOS +); + die(); +} + +if (count($argv) < 2) { + showUsageAndDie(); +} + +$options = getopt('c:s:', ['config:', 'source-root:'], $optind) + [ + 'c' => null, + 'config' => null, + 's' => null, + 'source-root' => null +]; +$config = $options['c'] ?: $options['config']; +if ($config) { + if (file_exists($config)) { + $daemon = require_once $config; + var_dump($daemon); + if (empty($daemon) || get_class($daemon) !== Daemon::class) { + fwrite(STDERR, 'Config file does not return a Daemon instance' . PHP_EOL); + showUsageAndDie(); + } + } else { + showUsageAndDie(); + } +} else { + $sourceRoot = $options['s'] ?: $options['source-root'] ?: $argv[1]; + if (realpath($sourceRoot) !== false) { + $daemon = new Daemon([ + 'sourceRoot' => $sourceRoot + ]); + } else { + showUsageAndDie(); + } +} + +$daemon->run(); From 72ed0162f8a47694f9e726ea4873e0c9880055e6 Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Fri, 9 Feb 2018 13:04:08 -0800 Subject: [PATCH 2/8] put the binary back in the composer.json files --- composer.json | 3 ++- src/Debugger/composer.json | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 2f6e7ca5eb33..a5a68f8cb9f7 100644 --- a/composer.json +++ b/composer.json @@ -114,7 +114,8 @@ "google-cloud": "dev/google-cloud" }, "bin": [ - "src/Core/bin/google-cloud-batch" + "src/Core/bin/google-cloud-batch", + "src/Debugger/bin/google-cloud-debugger" ], "extra": { "component": { diff --git a/src/Debugger/composer.json b/src/Debugger/composer.json index 46dacc442cf3..539ed5aee794 100644 --- a/src/Debugger/composer.json +++ b/src/Debugger/composer.json @@ -24,5 +24,8 @@ }, "archive": { "exclude": ["/ext"] - } + }, + "bin": [ + "bin/google-cloud-debugger" + ] } From 400f5d4318bdf4e887eb7d8d9a3d1a9836b98e30 Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Fri, 9 Feb 2018 13:15:30 -0800 Subject: [PATCH 3/8] Fix missing use --- src/Debugger/Agent.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Debugger/Agent.php b/src/Debugger/Agent.php index 15a5b006dbce..1307b446f639 100644 --- a/src/Debugger/Agent.php +++ b/src/Debugger/Agent.php @@ -17,6 +17,7 @@ namespace Google\Cloud\Debugger; +use Google\Cloud\Core\Batch\BatchDaemonTrait; use Google\Cloud\Core\Batch\BatchRunner; use Google\Cloud\Core\Batch\BatchTrait; use Google\Cloud\Core\ExponentialBackoff; From 5c9c40531c2abb8be396e7fb6eff3d60f340f272 Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Fri, 9 Feb 2018 14:12:13 -0800 Subject: [PATCH 4/8] Update README with installation instructions for windows --- src/Debugger/README.md | 59 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/src/Debugger/README.md b/src/Debugger/README.md index c9a6b36f51b5..bac6d4f09f1b 100644 --- a/src/Debugger/README.md +++ b/src/Debugger/README.md @@ -11,21 +11,57 @@ that project. ## Installation +1. Install the PHP extension from PECL. + +```bash +$ pecl install stackdriver_debugger-alpha +``` + +On Windows, you can download pre-built .dll files [from PECL][pecl-debugger]. + +You may also need to enable the extension in your `php.ini` file: + +``` +# on Unix +extension=stackdriver_debugger.so + +# on Windows +extension=php_stackdriver_debugger.dll +``` + 1. Install with `composer` or add to your `composer.json`. ```bash $ composer require google/cloud-debugger ``` -2. Run the debugger daemon script in the background. +1. Run the batch daemon script in the background. + +On Unix-based systems that have +[semaphore extensions][semaphore-extensions] installed, run the +[BatchDaemon][batch-daemon]: + +```bash +$ vendor/bin/google-cloud-batch daemon +``` + +On Windows or systems that do not have +[semaphore extensions][semaphore-extensions] installed, run the Debugger +[Daemon][debugger-daemon]: ```bash -$ vendor/bin/google-cloud-debugger +$ vendor/bin/google-cloud-debugger -s ``` The `SOURCE_ROOT` is the base location of your deployed application. -3. Include and start the debugger `Agent` as the first action in your +Alternatively, you can provide a configuration script: + +```bash +$ vendor/bin/google-cloud-debugger -c +``` + +1. Include and start the debugger `Agent` as the first action in your application: ```php @@ -48,10 +84,10 @@ $agent = new Google\Cloud\Debugger\Agent([ Debugger snapshots allow you to capture and inspect the call stack and local variables in your application without stopping or slowing it down. In general, you will set breakpoints via the Stackdriver Debugger UI in the -[Cloud Platform Console](https://console.cloud.google.com/debug). +[Cloud Platform Console][debugger-console]. -See [Using Debug Snapshots](https://cloud.google.com/debugger/docs/debugging) -for more information on snapshots. +See [Using Debug Snapshots][using-debug-snapshots] for more information on +snapshots. ### Logpoints @@ -69,5 +105,12 @@ $agent = new Google\Cloud\Debugger\Agent([ 'logger' => new Monolog\Logger('name') ]); ``` -See [Using Debug Logpoints](https://cloud.google.com/debugger/docs/logpoints) -for more information on logpoints. +See [Using Debug Logpoints][using-debug-logpoints] for more information on +logpoints. + +[semaphore-extensions]: http://php.net/manual/en/book.sem.php +[batch-daemon]: https://github.com/GoogleCloudPlatform/google-cloud-php/blob/master/src/Core/Batch/BatchDaemon.php +[pecl-debugger]: https://pecl.php.net/package/stackdriver_debugger +[debugger-console]: https://console.cloud.google.com/debug +[using-debug-snapshots]: https://cloud.google.com/debugger/docs/debugging +[using-debug-logpoints]: https://cloud.google.com/debugger/docs/logpoints From 6ec6c17f819258cfbf31802617a318be4489c738 Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Fri, 9 Feb 2018 14:14:40 -0800 Subject: [PATCH 5/8] Fix indentation for docs --- src/Debugger/README.md | 83 +++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/src/Debugger/README.md b/src/Debugger/README.md index bac6d4f09f1b..1142b0080860 100644 --- a/src/Debugger/README.md +++ b/src/Debugger/README.md @@ -13,69 +13,69 @@ that project. 1. Install the PHP extension from PECL. -```bash -$ pecl install stackdriver_debugger-alpha -``` + ```bash + $ pecl install stackdriver_debugger-alpha + ``` -On Windows, you can download pre-built .dll files [from PECL][pecl-debugger]. + On Windows, you can download pre-built .dll files [from PECL][pecl-debugger]. -You may also need to enable the extension in your `php.ini` file: + You may also need to enable the extension in your `php.ini` file: -``` -# on Unix -extension=stackdriver_debugger.so + ``` + # on Unix + extension=stackdriver_debugger.so -# on Windows -extension=php_stackdriver_debugger.dll -``` + # on Windows + extension=php_stackdriver_debugger.dll + ``` 1. Install with `composer` or add to your `composer.json`. -```bash -$ composer require google/cloud-debugger -``` + ```bash + $ composer require google/cloud-debugger + ``` 1. Run the batch daemon script in the background. -On Unix-based systems that have -[semaphore extensions][semaphore-extensions] installed, run the -[BatchDaemon][batch-daemon]: + On Unix-based systems that have + [semaphore extensions][semaphore-extensions] installed, run the + [BatchDaemon][batch-daemon]: -```bash -$ vendor/bin/google-cloud-batch daemon -``` + ```bash + $ vendor/bin/google-cloud-batch daemon + ``` -On Windows or systems that do not have -[semaphore extensions][semaphore-extensions] installed, run the Debugger -[Daemon][debugger-daemon]: + On Windows or systems that do not have + [semaphore extensions][semaphore-extensions] installed, run the Debugger + [Daemon][debugger-daemon]: -```bash -$ vendor/bin/google-cloud-debugger -s -``` + ```bash + $ vendor/bin/google-cloud-debugger -s + ``` -The `SOURCE_ROOT` is the base location of your deployed application. + The `SOURCE_ROOT` is the base location of your deployed application. -Alternatively, you can provide a configuration script: + Alternatively, you can provide a configuration script: -```bash -$ vendor/bin/google-cloud-debugger -c -``` + ```bash + $ vendor/bin/google-cloud-debugger -c + ``` 1. Include and start the debugger `Agent` as the first action in your application: -```php -$agent = new Google\Cloud\Debugger\Agent(); -``` + ```php + $agent = new Google\Cloud\Debugger\Agent(); + ``` -If this file is not in your source root, you will need to provide the path to -your application's source root as an optional parameter: + If this file is not in your source root, you will need to provide the path to + your application's source root as an optional parameter: -```php -$agent = new Google\Cloud\Debugger\Agent([ - 'sourceRoot' => '/path/to/source/root' -]); -``` + ```php + $agent = new Google\Cloud\Debugger\Agent([ + 'sourceRoot' => '/path/to/source/root' + ]); + ``` ## Configuration @@ -110,6 +110,7 @@ logpoints. [semaphore-extensions]: http://php.net/manual/en/book.sem.php [batch-daemon]: https://github.com/GoogleCloudPlatform/google-cloud-php/blob/master/src/Core/Batch/BatchDaemon.php +[debugger-daemon]: http://googlecloudplatform.github.io/google-cloud-php/#/docs/cloud-debugger/master/debugger/daemon [pecl-debugger]: https://pecl.php.net/package/stackdriver_debugger [debugger-console]: https://console.cloud.google.com/debug [using-debug-snapshots]: https://cloud.google.com/debugger/docs/debugging From 82698611cd0089a8d5138b454e1c5fd9bdd045bd Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Fri, 9 Feb 2018 14:33:55 -0800 Subject: [PATCH 6/8] Remove debug statement. ini syntax highlighting for the php.ini configuration --- src/Debugger/README.md | 2 +- src/Debugger/bin/google-cloud-debugger | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Debugger/README.md b/src/Debugger/README.md index 1142b0080860..3c069984d9ce 100644 --- a/src/Debugger/README.md +++ b/src/Debugger/README.md @@ -21,7 +21,7 @@ that project. You may also need to enable the extension in your `php.ini` file: - ``` + ```ini # on Unix extension=stackdriver_debugger.so diff --git a/src/Debugger/bin/google-cloud-debugger b/src/Debugger/bin/google-cloud-debugger index 29f8e5e6791e..fc8784febca5 100755 --- a/src/Debugger/bin/google-cloud-debugger +++ b/src/Debugger/bin/google-cloud-debugger @@ -63,7 +63,6 @@ $config = $options['c'] ?: $options['config']; if ($config) { if (file_exists($config)) { $daemon = require_once $config; - var_dump($daemon); if (empty($daemon) || get_class($daemon) !== Daemon::class) { fwrite(STDERR, 'Config file does not return a Daemon instance' . PHP_EOL); showUsageAndDie(); From b02746d64cdacfa2038b342109eadba5c562466c Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Fri, 9 Feb 2018 15:14:06 -0800 Subject: [PATCH 7/8] Move CLI logic into a CliDaemon class --- src/Debugger/CliDaemon.php | 88 ++++++++++++++++++++++++++ src/Debugger/bin/google-cloud-debugger | 31 +++------ 2 files changed, 97 insertions(+), 22 deletions(-) create mode 100644 src/Debugger/CliDaemon.php diff --git a/src/Debugger/CliDaemon.php b/src/Debugger/CliDaemon.php new file mode 100644 index 000000000000..db83480cf39c --- /dev/null +++ b/src/Debugger/CliDaemon.php @@ -0,0 +1,88 @@ + null, + 'sourceRoot' => null + ]; + $config = $options['config']; + $sourceRoot = $options['sourceRoot']; + + if ($config && file_exists($config)) { + // Load the config file. The config file should return a configured + // Daemon instance. + $this->daemon = require_once $config; + + if (!is_object($this->daemon) || get_class($this->daemon) !== Daemon::class) { + throw new \UnexpectedValueException('Config file does not return a Daemon instance.'); + } + } else { + if (!file_exists($sourceRoot)) { + throw new \UnexpectedValueException("Source root '$sourceRoot' does not exist."); + } + $this->daemon = new Daemon([ + 'sourceRoot' => $sourceRoot + ]); + } + + // If the Daemon would be started by the BatchRunner, then don't run it here. + if ($this->isDaemonRunning() && $this->isSysvIPCLoaded()) { + throw new \RuntimeException('Daemon should already be running via BatchDaemon'); + } + } + + /** + * Start the Daemon. This is expected to run indefinitely. + */ + public function run() + { + $this->daemon->run(); + } +} diff --git a/src/Debugger/bin/google-cloud-debugger b/src/Debugger/bin/google-cloud-debugger index fc8784febca5..2554e3a7d3be 100755 --- a/src/Debugger/bin/google-cloud-debugger +++ b/src/Debugger/bin/google-cloud-debugger @@ -59,26 +59,13 @@ $options = getopt('c:s:', ['config:', 'source-root:'], $optind) + [ 's' => null, 'source-root' => null ]; -$config = $options['c'] ?: $options['config']; -if ($config) { - if (file_exists($config)) { - $daemon = require_once $config; - if (empty($daemon) || get_class($daemon) !== Daemon::class) { - fwrite(STDERR, 'Config file does not return a Daemon instance' . PHP_EOL); - showUsageAndDie(); - } - } else { - showUsageAndDie(); - } -} else { - $sourceRoot = $options['s'] ?: $options['source-root'] ?: $argv[1]; - if (realpath($sourceRoot) !== false) { - $daemon = new Daemon([ - 'sourceRoot' => $sourceRoot - ]); - } else { - showUsageAndDie(); - } +try { + $cli = new CliDaemon([ + 'config' => $options['c'] ?: $options['config'], + 'sourceRoot' => $options['s'] ?: $options['source-root'] ?: $argv[1] + ]); +} catch (\Exception $e) { + fwrite(STDERR, $e->getMessage() . PHP_EOL); + showUsageAndDie(); } - -$daemon->run(); +$cli->run(); From 88e9af2df7eae7776ef2d2e876cd46bfb41d96a4 Mon Sep 17 00:00:00 2001 From: Jeff Ching Date: Fri, 9 Feb 2018 15:27:40 -0800 Subject: [PATCH 8/8] Add tests for CliDaemon --- src/Debugger/CliDaemon.php | 12 ++- tests/unit/Debugger/CliDaemonTest.php | 89 +++++++++++++++++++ tests/unit/Debugger/data/daemon_config.php | 5 ++ .../data/daemon_config_wrong_return.php | 3 + 4 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 tests/unit/Debugger/CliDaemonTest.php create mode 100644 tests/unit/Debugger/data/daemon_config.php create mode 100644 tests/unit/Debugger/data/daemon_config_wrong_return.php diff --git a/src/Debugger/CliDaemon.php b/src/Debugger/CliDaemon.php index db83480cf39c..91e7e4cb6e63 100644 --- a/src/Debugger/CliDaemon.php +++ b/src/Debugger/CliDaemon.php @@ -55,7 +55,10 @@ public function __construct(array $options = []) $config = $options['config']; $sourceRoot = $options['sourceRoot']; - if ($config && file_exists($config)) { + if ($config) { + if (!file_exists($config)) { + throw new \UnexpectedValueException("Config file '$config' does not exist."); + } // Load the config file. The config file should return a configured // Daemon instance. $this->daemon = require_once $config; @@ -63,13 +66,18 @@ public function __construct(array $options = []) if (!is_object($this->daemon) || get_class($this->daemon) !== Daemon::class) { throw new \UnexpectedValueException('Config file does not return a Daemon instance.'); } - } else { + } elseif ($sourceRoot) { if (!file_exists($sourceRoot)) { throw new \UnexpectedValueException("Source root '$sourceRoot' does not exist."); } + if (!is_dir($sourceRoot)) { + throw new \UnexpectedValueException("Source root '$sourceRoot' is not a directory."); + } $this->daemon = new Daemon([ 'sourceRoot' => $sourceRoot ]); + } else { + throw new \InvalidArgumentException('Must specify either config or sourceRoot'); } // If the Daemon would be started by the BatchRunner, then don't run it here. diff --git a/tests/unit/Debugger/CliDaemonTest.php b/tests/unit/Debugger/CliDaemonTest.php new file mode 100644 index 000000000000..e93aef27956a --- /dev/null +++ b/tests/unit/Debugger/CliDaemonTest.php @@ -0,0 +1,89 @@ + implode(DIRECTORY_SEPARATOR, [dirname(__FILE__), 'data', 'daemon_config.php']) + ]); + } + + /** + * @expectedException UnexpectedValueException + */ + public function testClientConfigMissing() + { + new CliDaemon([ + 'config' => 'non-existent-file' + ]); + } + + /** + * @expectedException UnexpectedValueException + */ + public function testClientConfigWrongReturn() + { + new CliDaemon([ + 'config' => implode(DIRECTORY_SEPARATOR, [dirname(__FILE__), 'data', 'daemon_config_wrong_return.php']) + ]); + } + + public function testSourceRoot() + { + new CliDaemon([ + 'sourceRoot' => '.' + ]); + } + + /** + * @expectedException UnexpectedValueException + */ + public function testSourceRootMissing() + { + new CliDaemon([ + 'sourceRoot' => 'non-existent-directory' + ]); + } + + /** + * @expectedException UnexpectedValueException + */ + public function testSourceRootInvalid() + { + new CliDaemon([ + 'sourceRoot' => __FILE__ + ]); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testDefaults() + { + new CliDaemon(); + } +} diff --git a/tests/unit/Debugger/data/daemon_config.php b/tests/unit/Debugger/data/daemon_config.php new file mode 100644 index 000000000000..555f75f12663 --- /dev/null +++ b/tests/unit/Debugger/data/daemon_config.php @@ -0,0 +1,5 @@ +