src,test: ensure that V8 fast APIs are called
Adds a debug-only macro that can be used to track when a V8 fast API is called. A map of counters is maintained in in thread-local storage and an internal API can be called to get the total count associated with a call id. Specific tests are added and `crypto.timingSafeEqual` as well as internal documentation are updated to show how to use the macro and test fast API calls without running long loops. PR-URL: https://github.com/nodejs/node/pull/54317 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
This commit is contained in:
parent
9e8cc2933e
commit
1d35a066e7
@ -29,6 +29,12 @@ for example, they may not trigger garbage collection.
|
|||||||
* To test fast APIs, make sure to run the tests in a loop with a decent
|
* To test fast APIs, make sure to run the tests in a loop with a decent
|
||||||
iterations count to trigger relevant optimizations that prefer the fast API
|
iterations count to trigger relevant optimizations that prefer the fast API
|
||||||
over the slow one.
|
over the slow one.
|
||||||
|
* In debug mode (`--debug` or `--debug-node` flags), the fast API calls can be
|
||||||
|
tracked using the `TRACK_V8_FAST_API_CALL("key")` macro. This can be used to
|
||||||
|
count how many times fast paths are taken during tests. The key is a global
|
||||||
|
identifier and should be unique across the codebase.
|
||||||
|
Use `"binding_name.function_name"` or `"binding_name.function_name.suffix"` to
|
||||||
|
ensure uniqueness.
|
||||||
* The fast callback must be idempotent up to the point where error and fallback
|
* The fast callback must be idempotent up to the point where error and fallback
|
||||||
conditions are checked, because otherwise executing the slow callback might
|
conditions are checked, because otherwise executing the slow callback might
|
||||||
produce visible side effects twice.
|
produce visible side effects twice.
|
||||||
@ -77,6 +83,7 @@ A typical function that communicates between JavaScript and C++ is as follows.
|
|||||||
* On the C++ side:
|
* On the C++ side:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
#include "node_debug.h"
|
||||||
#include "v8-fast-api-calls.h"
|
#include "v8-fast-api-calls.h"
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
@ -102,9 +109,11 @@ A typical function that communicates between JavaScript and C++ is as follows.
|
|||||||
const int32_t b,
|
const int32_t b,
|
||||||
v8::FastApiCallbackOptions& options) {
|
v8::FastApiCallbackOptions& options) {
|
||||||
if (b == 0) {
|
if (b == 0) {
|
||||||
|
TRACK_V8_FAST_API_CALL("custom_namespace.divide.error");
|
||||||
options.fallback = true;
|
options.fallback = true;
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
|
TRACK_V8_FAST_API_CALL("custom_namespace.divide.ok");
|
||||||
return a / b;
|
return a / b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,3 +157,42 @@ A typical function that communicates between JavaScript and C++ is as follows.
|
|||||||
const int32_t b,
|
const int32_t b,
|
||||||
v8::FastApiCallbackOptions& options);
|
v8::FastApiCallbackOptions& options);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* In the unit tests:
|
||||||
|
|
||||||
|
Since the fast API function uses `TRACK_V8_FAST_API_CALL`, we can ensure that
|
||||||
|
the fast paths are taken and test them by writing tests that force
|
||||||
|
V8 optimizations and check the counters.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Flags: --expose-internals --no-warnings --allow-natives-syntax
|
||||||
|
'use strict';
|
||||||
|
const common = require('../common');
|
||||||
|
|
||||||
|
const { internalBinding } = require('internal/test/binding');
|
||||||
|
// We could also require a function that uses the internal binding internally.
|
||||||
|
const { divide } = internalBinding('custom_namespace');
|
||||||
|
|
||||||
|
if (common.isDebug) {
|
||||||
|
const { getV8FastApiCallCount } = internalBinding('debug');
|
||||||
|
|
||||||
|
// The function that will be optimized. It has to be a function written in
|
||||||
|
// JavaScript. Since `divide` comes from the C++ side, we need to wrap it.
|
||||||
|
function testFastPath(a, b) {
|
||||||
|
return divide(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
eval('%PrepareFunctionForOptimization(testFastPath)');
|
||||||
|
// This call will let V8 know about the argument types that the function expects.
|
||||||
|
assert.strictEqual(testFastPath(6, 3), 2);
|
||||||
|
|
||||||
|
eval('%OptimizeFunctionOnNextCall(testFastPath)');
|
||||||
|
assert.strictEqual(testFastPath(8, 2), 4);
|
||||||
|
assert.throws(() => testFastPath(1, 0), {
|
||||||
|
code: 'ERR_INVALID_STATE',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.strictEqual(getV8FastApiCallCount('custom_namespace.divide.ok'), 1);
|
||||||
|
assert.strictEqual(getV8FastApiCallCount('custom_namespace.divide.error'), 1);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
2
node.gyp
2
node.gyp
@ -107,6 +107,7 @@
|
|||||||
'src/node_constants.cc',
|
'src/node_constants.cc',
|
||||||
'src/node_contextify.cc',
|
'src/node_contextify.cc',
|
||||||
'src/node_credentials.cc',
|
'src/node_credentials.cc',
|
||||||
|
'src/node_debug.cc',
|
||||||
'src/node_dir.cc',
|
'src/node_dir.cc',
|
||||||
'src/node_dotenv.cc',
|
'src/node_dotenv.cc',
|
||||||
'src/node_env_var.cc',
|
'src/node_env_var.cc',
|
||||||
@ -229,6 +230,7 @@
|
|||||||
'src/node_constants.h',
|
'src/node_constants.h',
|
||||||
'src/node_context_data.h',
|
'src/node_context_data.h',
|
||||||
'src/node_contextify.h',
|
'src/node_contextify.h',
|
||||||
|
'src/node_debug.h',
|
||||||
'src/node_dir.h',
|
'src/node_dir.h',
|
||||||
'src/node_dotenv.h',
|
'src/node_dotenv.h',
|
||||||
'src/node_errors.h',
|
'src/node_errors.h',
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
#include "crypto/crypto_timing.h"
|
#include "crypto/crypto_timing.h"
|
||||||
#include "crypto/crypto_util.h"
|
#include "crypto/crypto_util.h"
|
||||||
#include "env-inl.h"
|
#include "env-inl.h"
|
||||||
|
#include "node.h"
|
||||||
|
#include "node_debug.h"
|
||||||
#include "node_errors.h"
|
#include "node_errors.h"
|
||||||
#include "v8.h"
|
#include "v8.h"
|
||||||
#include "node.h"
|
|
||||||
|
|
||||||
#include <openssl/crypto.h>
|
#include <openssl/crypto.h>
|
||||||
|
|
||||||
@ -57,10 +58,12 @@ bool FastTimingSafeEqual(Local<Value> receiver,
|
|||||||
uint8_t* data_b;
|
uint8_t* data_b;
|
||||||
if (a.length() != b.length() || !a.getStorageIfAligned(&data_a) ||
|
if (a.length() != b.length() || !a.getStorageIfAligned(&data_a) ||
|
||||||
!b.getStorageIfAligned(&data_b)) {
|
!b.getStorageIfAligned(&data_b)) {
|
||||||
|
TRACK_V8_FAST_API_CALL("crypto.timingSafeEqual.error");
|
||||||
options.fallback = true;
|
options.fallback = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TRACK_V8_FAST_API_CALL("crypto.timingSafeEqual.ok");
|
||||||
return CRYPTO_memcmp(data_a, data_b, a.length()) == 0;
|
return CRYPTO_memcmp(data_a, data_b, a.length()) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,12 @@
|
|||||||
#define NODE_BUILTIN_PROFILER_BINDINGS(V)
|
#define NODE_BUILTIN_PROFILER_BINDINGS(V)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define NODE_BUILTIN_DEBUG_BINDINGS(V) V(debug)
|
||||||
|
#else
|
||||||
|
#define NODE_BUILTIN_DEBUG_BINDINGS(V)
|
||||||
|
#endif
|
||||||
|
|
||||||
// A list of built-in bindings. In order to do binding registration
|
// A list of built-in bindings. In order to do binding registration
|
||||||
// in node::Init(), need to add built-in bindings in the following list.
|
// in node::Init(), need to add built-in bindings in the following list.
|
||||||
// Then in binding::RegisterBuiltinBindings(), it calls bindings' registration
|
// Then in binding::RegisterBuiltinBindings(), it calls bindings' registration
|
||||||
@ -96,6 +102,7 @@
|
|||||||
NODE_BUILTIN_OPENSSL_BINDINGS(V) \
|
NODE_BUILTIN_OPENSSL_BINDINGS(V) \
|
||||||
NODE_BUILTIN_ICU_BINDINGS(V) \
|
NODE_BUILTIN_ICU_BINDINGS(V) \
|
||||||
NODE_BUILTIN_PROFILER_BINDINGS(V) \
|
NODE_BUILTIN_PROFILER_BINDINGS(V) \
|
||||||
|
NODE_BUILTIN_DEBUG_BINDINGS(V) \
|
||||||
NODE_BUILTIN_QUIC_BINDINGS(V)
|
NODE_BUILTIN_QUIC_BINDINGS(V)
|
||||||
|
|
||||||
// This is used to load built-in bindings. Instead of using
|
// This is used to load built-in bindings. Instead of using
|
||||||
|
101
src/node_debug.cc
Normal file
101
src/node_debug.cc
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#include "node_debug.h"
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#include "node_binding.h"
|
||||||
|
|
||||||
|
#include "env-inl.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "v8-fast-api-calls.h"
|
||||||
|
#include "v8.h"
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
#include <unordered_map>
|
||||||
|
#endif // DEBUG
|
||||||
|
|
||||||
|
namespace node {
|
||||||
|
namespace debug {
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
using v8::Context;
|
||||||
|
using v8::FastApiCallbackOptions;
|
||||||
|
using v8::FunctionCallbackInfo;
|
||||||
|
using v8::Local;
|
||||||
|
using v8::Number;
|
||||||
|
using v8::Object;
|
||||||
|
using v8::Value;
|
||||||
|
|
||||||
|
thread_local std::unordered_map<std::string_view, int> v8_fast_api_call_counts;
|
||||||
|
|
||||||
|
void TrackV8FastApiCall(std::string_view key) {
|
||||||
|
v8_fast_api_call_counts[key]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetV8FastApiCallCount(std::string_view key) {
|
||||||
|
return v8_fast_api_call_counts[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetV8FastApiCallCount(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
if (!args[0]->IsString()) {
|
||||||
|
env->ThrowError("getV8FastApiCallCount must be called with a string");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Utf8Value utf8_key(env->isolate(), args[0]);
|
||||||
|
args.GetReturnValue().Set(GetV8FastApiCallCount(utf8_key.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SlowIsEven(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
if (!args[0]->IsNumber()) {
|
||||||
|
env->ThrowError("isEven must be called with a number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int64_t value = args[0].As<Number>()->Value();
|
||||||
|
args.GetReturnValue().Set(value % 2 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FastIsEven(Local<Value> receiver,
|
||||||
|
const int64_t value,
|
||||||
|
// NOLINTNEXTLINE(runtime/references)
|
||||||
|
FastApiCallbackOptions& options) {
|
||||||
|
TRACK_V8_FAST_API_CALL("debug.isEven");
|
||||||
|
return value % 2 == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SlowIsOdd(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
if (!args[0]->IsNumber()) {
|
||||||
|
env->ThrowError("isOdd must be called with a number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int64_t value = args[0].As<Number>()->Value();
|
||||||
|
args.GetReturnValue().Set(value % 2 != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FastIsOdd(Local<Value> receiver,
|
||||||
|
const int64_t value,
|
||||||
|
// NOLINTNEXTLINE(runtime/references)
|
||||||
|
FastApiCallbackOptions& options) {
|
||||||
|
TRACK_V8_FAST_API_CALL("debug.isOdd");
|
||||||
|
return value % 2 != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static v8::CFunction fast_is_even(v8::CFunction::Make(FastIsEven));
|
||||||
|
static v8::CFunction fast_is_odd(v8::CFunction::Make(FastIsOdd));
|
||||||
|
|
||||||
|
void Initialize(Local<Object> target,
|
||||||
|
Local<Value> unused,
|
||||||
|
Local<Context> context,
|
||||||
|
void* priv) {
|
||||||
|
SetMethod(context, target, "getV8FastApiCallCount", GetV8FastApiCallCount);
|
||||||
|
SetFastMethod(context, target, "isEven", SlowIsEven, &fast_is_even);
|
||||||
|
SetFastMethod(context, target, "isOdd", SlowIsOdd, &fast_is_odd);
|
||||||
|
}
|
||||||
|
#endif // DEBUG
|
||||||
|
|
||||||
|
} // namespace debug
|
||||||
|
} // namespace node
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
NODE_BINDING_CONTEXT_AWARE_INTERNAL(debug, node::debug::Initialize)
|
||||||
|
#endif // DEBUG
|
24
src/node_debug.h
Normal file
24
src/node_debug.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#include <string_view>
|
||||||
|
#endif // DEBUG
|
||||||
|
|
||||||
|
namespace node {
|
||||||
|
namespace debug {
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
void TrackV8FastApiCall(std::string_view key);
|
||||||
|
int GetV8FastApiCallCount(std::string_view key);
|
||||||
|
|
||||||
|
#define TRACK_V8_FAST_API_CALL(key) node::debug::TrackV8FastApiCall(key)
|
||||||
|
#else // !DEBUG
|
||||||
|
#define TRACK_V8_FAST_API_CALL(key)
|
||||||
|
#endif // DEBUG
|
||||||
|
|
||||||
|
} // namespace debug
|
||||||
|
} // namespace node
|
||||||
|
|
||||||
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
@ -10,7 +10,7 @@ if (!common.isMainThread) {
|
|||||||
common.skip('addons are not supported in workers');
|
common.skip('addons are not supported in workers');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.features.debug) {
|
if (common.isDebug) {
|
||||||
common.skip('benchmark does not work with debug build yet');
|
common.skip('benchmark does not work with debug build yet');
|
||||||
}
|
}
|
||||||
const runBenchmark = require('../common/benchmark');
|
const runBenchmark = require('../common/benchmark');
|
||||||
|
@ -143,6 +143,7 @@ const isOpenBSD = process.platform === 'openbsd';
|
|||||||
const isLinux = process.platform === 'linux';
|
const isLinux = process.platform === 'linux';
|
||||||
const isMacOS = process.platform === 'darwin';
|
const isMacOS = process.platform === 'darwin';
|
||||||
const isASan = process.config.variables.asan === 1;
|
const isASan = process.config.variables.asan === 1;
|
||||||
|
const isDebug = process.features.debug;
|
||||||
const isPi = (() => {
|
const isPi = (() => {
|
||||||
try {
|
try {
|
||||||
// Normal Raspberry Pi detection is to find the `Raspberry Pi` string in
|
// Normal Raspberry Pi detection is to find the `Raspberry Pi` string in
|
||||||
@ -280,7 +281,7 @@ function platformTimeout(ms) {
|
|||||||
const multipliers = typeof ms === 'bigint' ?
|
const multipliers = typeof ms === 'bigint' ?
|
||||||
{ two: 2n, four: 4n, seven: 7n } : { two: 2, four: 4, seven: 7 };
|
{ two: 2n, four: 4n, seven: 7n } : { two: 2, four: 4, seven: 7 };
|
||||||
|
|
||||||
if (process.features.debug)
|
if (isDebug)
|
||||||
ms = multipliers.two * ms;
|
ms = multipliers.two * ms;
|
||||||
|
|
||||||
if (exports.isAIX || exports.isIBMi)
|
if (exports.isAIX || exports.isIBMi)
|
||||||
@ -998,6 +999,7 @@ const common = {
|
|||||||
invalidArgTypeHelper,
|
invalidArgTypeHelper,
|
||||||
isAlive,
|
isAlive,
|
||||||
isASan,
|
isASan,
|
||||||
|
isDebug,
|
||||||
isDumbTerminal,
|
isDumbTerminal,
|
||||||
isFreeBSD,
|
isFreeBSD,
|
||||||
isLinux,
|
isLinux,
|
||||||
|
59
test/parallel/test-debug-v8-fast-api.js
Normal file
59
test/parallel/test-debug-v8-fast-api.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Flags: --expose-internals --no-warnings --allow-natives-syntax
|
||||||
|
'use strict';
|
||||||
|
const common = require('../common');
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const { internalBinding } = require('internal/test/binding');
|
||||||
|
|
||||||
|
if (!common.isDebug) {
|
||||||
|
assert.throws(() => internalBinding('debug'), {
|
||||||
|
message: 'No such binding: debug'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
getV8FastApiCallCount,
|
||||||
|
isEven,
|
||||||
|
isOdd,
|
||||||
|
} = internalBinding('debug');
|
||||||
|
|
||||||
|
assert.throws(() => getV8FastApiCallCount(), {
|
||||||
|
message: 'getV8FastApiCallCount must be called with a string',
|
||||||
|
});
|
||||||
|
|
||||||
|
function testIsEven() {
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
assert.strictEqual(isEven(i), i % 2 === 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testIsOdd() {
|
||||||
|
for (let i = 0; i < 20; i++) {
|
||||||
|
assert.strictEqual(isOdd(i), i % 2 !== 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should return 0 by default for any string.
|
||||||
|
assert.strictEqual(getV8FastApiCallCount(''), 0);
|
||||||
|
assert.strictEqual(getV8FastApiCallCount('foo'), 0);
|
||||||
|
assert.strictEqual(getV8FastApiCallCount('debug.isEven'), 0);
|
||||||
|
assert.strictEqual(getV8FastApiCallCount('debug.isOdd'), 0);
|
||||||
|
|
||||||
|
eval('%PrepareFunctionForOptimization(testIsEven)');
|
||||||
|
testIsEven();
|
||||||
|
eval('%PrepareFunctionForOptimization(testIsOdd)');
|
||||||
|
testIsOdd();
|
||||||
|
|
||||||
|
// Functions should not be optimized yet.
|
||||||
|
assert.strictEqual(getV8FastApiCallCount('debug.isEven'), 0);
|
||||||
|
assert.strictEqual(getV8FastApiCallCount('debug.isOdd'), 0);
|
||||||
|
|
||||||
|
eval('%OptimizeFunctionOnNextCall(testIsEven)');
|
||||||
|
testIsEven();
|
||||||
|
eval('%OptimizeFunctionOnNextCall(testIsOdd)');
|
||||||
|
testIsOdd();
|
||||||
|
|
||||||
|
// Functions should have been optimized and fast path taken.
|
||||||
|
assert.strictEqual(getV8FastApiCallCount('debug.isEven'), 10);
|
||||||
|
assert.strictEqual(getV8FastApiCallCount('debug.isOdd'), 20);
|
@ -1,3 +1,4 @@
|
|||||||
|
// Flags: --expose-internals --no-warnings --allow-natives-syntax
|
||||||
'use strict';
|
'use strict';
|
||||||
const common = require('../common');
|
const common = require('../common');
|
||||||
if (!common.hasCrypto)
|
if (!common.hasCrypto)
|
||||||
@ -91,3 +92,25 @@ assert.throws(
|
|||||||
name: 'TypeError',
|
name: 'TypeError',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (common.isDebug) {
|
||||||
|
const { internalBinding } = require('internal/test/binding');
|
||||||
|
const { getV8FastApiCallCount } = internalBinding('debug');
|
||||||
|
|
||||||
|
const foo = Buffer.from('foo');
|
||||||
|
const bar = Buffer.from('bar');
|
||||||
|
const longer = Buffer.from('longer');
|
||||||
|
function testFastPath(buf1, buf2) {
|
||||||
|
return crypto.timingSafeEqual(buf1, buf2);
|
||||||
|
}
|
||||||
|
eval('%PrepareFunctionForOptimization(testFastPath)');
|
||||||
|
assert.strictEqual(testFastPath(foo, bar), false);
|
||||||
|
eval('%OptimizeFunctionOnNextCall(testFastPath)');
|
||||||
|
assert.strictEqual(testFastPath(foo, bar), false);
|
||||||
|
assert.strictEqual(testFastPath(foo, foo), true);
|
||||||
|
assert.throws(() => testFastPath(foo, longer), {
|
||||||
|
code: 'ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH',
|
||||||
|
});
|
||||||
|
assert.strictEqual(getV8FastApiCallCount('crypto.timingSafeEqual.ok'), 2);
|
||||||
|
assert.strictEqual(getV8FastApiCallCount('crypto.timingSafeEqual.error'), 1);
|
||||||
|
}
|
||||||
|
2
typings/globals.d.ts
vendored
2
typings/globals.d.ts
vendored
@ -2,6 +2,7 @@ import { AsyncWrapBinding } from './internalBinding/async_wrap';
|
|||||||
import { BlobBinding } from './internalBinding/blob';
|
import { BlobBinding } from './internalBinding/blob';
|
||||||
import { ConfigBinding } from './internalBinding/config';
|
import { ConfigBinding } from './internalBinding/config';
|
||||||
import { ConstantsBinding } from './internalBinding/constants';
|
import { ConstantsBinding } from './internalBinding/constants';
|
||||||
|
import { DebugBinding } from './internalBinding/debug';
|
||||||
import { HttpParserBinding } from './internalBinding/http_parser';
|
import { HttpParserBinding } from './internalBinding/http_parser';
|
||||||
import { FsBinding } from './internalBinding/fs';
|
import { FsBinding } from './internalBinding/fs';
|
||||||
import { FsDirBinding } from './internalBinding/fs_dir';
|
import { FsDirBinding } from './internalBinding/fs_dir';
|
||||||
@ -35,6 +36,7 @@ interface InternalBindingMap {
|
|||||||
blob: BlobBinding;
|
blob: BlobBinding;
|
||||||
config: ConfigBinding;
|
config: ConfigBinding;
|
||||||
constants: ConstantsBinding;
|
constants: ConstantsBinding;
|
||||||
|
debug: DebugBinding;
|
||||||
fs: FsBinding;
|
fs: FsBinding;
|
||||||
fs_dir: FsDirBinding;
|
fs_dir: FsDirBinding;
|
||||||
http_parser: HttpParserBinding;
|
http_parser: HttpParserBinding;
|
||||||
|
10
typings/internalBinding/debug.d.ts
vendored
Normal file
10
typings/internalBinding/debug.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* The `internalBinding('debug')` binding provides access to internal debugging
|
||||||
|
* utilities. They are only available when Node.js is built with the `--debug`
|
||||||
|
* or `--debug-node` compile-time flags.
|
||||||
|
*/
|
||||||
|
export interface DebugBinding {
|
||||||
|
getV8FastApiCallCount(name: string): number;
|
||||||
|
isEven(value: number): boolean;
|
||||||
|
isOdd(value: number): boolean;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user