Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide experimental access to V8's Inspector API #445

Open
wants to merge 6 commits into
base: php7
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,13 @@ class V8Js
public function setAverageObjectSize($average_object_size)
{}

/**
* Install a V8 inspector (client) on this V8Js object.
* This is an experimental feature.
* @return V8Inspector
*/
public function connectInspector();

/**
* Returns uncaught pending exception or null if there is no pending exception.
* @return V8JsScriptException|null
Expand Down Expand Up @@ -206,6 +213,30 @@ class V8Js
{}
}

final class V8Inspector
{
/**
* Send a message to V8's inspector backend.
* @param string $message
*/
public function send($message)
{}

/**
* Register a callback handler for responses from V8's inspector backend.
* @param callable $handler
*/
public function setResponseHandler($handler)
{}

/**
* Register a callback handler for notifications from V8's inspector backend.
* @param callable $handler
*/
public function setNotificationHandler($handler)
{}
}

final class V8JsScriptException extends Exception
{
/**
Expand Down Expand Up @@ -401,3 +432,56 @@ objects obeying the above rules and re-thrown in JavaScript context. If they
are not caught by JavaScript code the execution stops and a
`V8JsScriptException` is thrown, which has the original PHP exception accessible
via `getPrevious` method.

Inspector Client
================

Keep in mind that this is an experimental feature of php-v8js.

The inspector client API, i.e. calling `V8Js::connectInspector()`, provides
low-level access to [V8's Inspector Protocol](https://v8.dev/docs/inspector).
This may be used to access V8's profiler.

Theoretically this also provides access to V8's debugger. Yet keep in mind
that setting a breakpoint in the JavaScript code will also block PHP code
execution (due to php-v8js' call model).

See [Chrome DevTool Protocol API documentation](https://chromedevtools.github.io/devtools-protocol/1-3/Profiler/)
for details on how to use the profiler.

Usage example to collect call count information on function level:

```php
<?php

$v8 = new V8Js;
$i = $v8->connectInspector();

$i->setResponseHandler(function($res) {
$res = \json_decode($res);

if ($res->id === 3) {
foreach($res->result->result[0]->functions as $info) {
printf("function '%s' was called %d times.\n", $info->functionName ?: '<root scope>', $info->ranges[0]->count);
}
}
});

$i->send(json_encode([ 'id' => 1, 'method' => "Profiler.enable" ]));
$i->send(json_encode([ 'id' => 2, 'method' => "Profiler.startPreciseCoverage", 'params' => [ 'callCount' => true ] ]));

$fn = $v8->executeString('(function foo() { const blarg = 42; })', 'multi-call-lambda');

$fn();
$fn();
$fn();

$i->send(json_encode([ 'id' => 3, 'method' => "Profiler.takePreciseCoverage" ]));
```

yields

```
function '<root scope>' was called 1 times.
function 'foo' was called 3 times.
```
1 change: 1 addition & 0 deletions config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ int main ()
v8js_timer.cc \
v8js_v8.cc \
v8js_v8object_class.cc \
v8js_v8inspector_class.cc \
v8js_variables.cc \
], $ext_shared, , "$ac_cv_v8_narrowing -std="$ac_cv_v8_cstd)

Expand Down
21 changes: 21 additions & 0 deletions v8js_class.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "v8js_v8object_class.h"
#include "v8js_object_export.h"
#include "v8js_timer.h"
#include "v8js_v8inspector_class.h"

extern "C" {
#include "php.h"
Expand Down Expand Up @@ -848,6 +849,22 @@ static PHP_METHOD(V8Js, clearPendingException)
}
/* }}} */

/* {{{ proto void V8Js::connectInspector()
*/
static PHP_METHOD(V8Js, connectInspector)
{
v8js_ctx *c;

if (zend_parse_parameters_none() == FAILURE) {
return;
}

c = Z_V8JS_CTX_OBJ_P(getThis());
v8js_v8inspector_create(return_value, c);
}
/* }}} */


/* {{{ proto void V8Js::setModuleNormaliser(string base, string module_id)
*/
static PHP_METHOD(V8Js, setModuleNormaliser)
Expand Down Expand Up @@ -1245,6 +1262,9 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_v8js_clearpendingexception, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_v8js_connectinspector, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_setmodulenormaliser, 0, 0, 2)
ZEND_ARG_INFO(0, base)
ZEND_ARG_INFO(0, module_id)
Expand Down Expand Up @@ -1291,6 +1311,7 @@ const zend_function_entry v8js_methods[] = { /* {{{ */
PHP_ME(V8Js, checkString, arginfo_v8js_checkstring, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
PHP_ME(V8Js, getPendingException, arginfo_v8js_getpendingexception, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
PHP_ME(V8Js, clearPendingException, arginfo_v8js_clearpendingexception, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
PHP_ME(V8Js, connectInspector, arginfo_v8js_connectinspector, ZEND_ACC_PUBLIC)
PHP_ME(V8Js, setModuleNormaliser, arginfo_v8js_setmodulenormaliser, ZEND_ACC_PUBLIC)
PHP_ME(V8Js, setModuleLoader, arginfo_v8js_setmoduleloader, ZEND_ACC_PUBLIC)
PHP_ME(V8Js, setTimeLimit, arginfo_v8js_settimelimit, ZEND_ACC_PUBLIC)
Expand Down
2 changes: 2 additions & 0 deletions v8js_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ extern "C" {
#include "v8js_class.h"
#include "v8js_exceptions.h"
#include "v8js_v8object_class.h"
#include "v8js_v8inspector_class.h"

ZEND_DECLARE_MODULE_GLOBALS(v8js)
struct _v8js_process_globals v8js_process_globals;
Expand Down Expand Up @@ -139,6 +140,7 @@ PHP_MINIT_FUNCTION(v8js)
PHP_MINIT(v8js_class)(INIT_FUNC_ARGS_PASSTHRU);
PHP_MINIT(v8js_exceptions)(INIT_FUNC_ARGS_PASSTHRU);
PHP_MINIT(v8js_v8object_class)(INIT_FUNC_ARGS_PASSTHRU);
PHP_MINIT(v8js_v8inspector_class)(INIT_FUNC_ARGS_PASSTHRU);

REGISTER_INI_ENTRIES();

Expand Down
Loading