http2: use aliased buffer for perf stats, add stats
Add an aliased buffer for session and stream statistics, add a few more metrics PR-URL: https://github.com/nodejs/node/pull/18020 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
This commit is contained in:
parent
ececdd3167
commit
a02fcd2716
@ -3003,23 +3003,35 @@ The `name` property of the `PerformanceEntry` will be equal to either
|
|||||||
If `name` is equal to `Http2Stream`, the `PerformanceEntry` will contain the
|
If `name` is equal to `Http2Stream`, the `PerformanceEntry` will contain the
|
||||||
following additional properties:
|
following additional properties:
|
||||||
|
|
||||||
|
* `bytesRead` {number} The number of DATA frame bytes received for this
|
||||||
|
`Http2Stream`.
|
||||||
|
* `bytesWritten` {number} The number of DATA frame bytes sent for this
|
||||||
|
`Http2Stream`.
|
||||||
|
* `id` {number} The identifier of the associated `Http2Stream`
|
||||||
* `timeToFirstByte` {number} The number of milliseconds elapsed between the
|
* `timeToFirstByte` {number} The number of milliseconds elapsed between the
|
||||||
`PerformanceEntry` `startTime` and the reception of the first `DATA` frame.
|
`PerformanceEntry` `startTime` and the reception of the first `DATA` frame.
|
||||||
|
* `timeToFirstByteSent` {number} The number of milliseconds elapsed between
|
||||||
|
the `PerformanceEntry` `startTime` and sending of the first `DATA` frame.
|
||||||
* `timeToFirstHeader` {number} The number of milliseconds elapsed between the
|
* `timeToFirstHeader` {number} The number of milliseconds elapsed between the
|
||||||
`PerformanceEntry` `startTime` and the reception of the first header.
|
`PerformanceEntry` `startTime` and the reception of the first header.
|
||||||
|
|
||||||
If `name` is equal to `Http2Session`, the `PerformanceEntry` will contain the
|
If `name` is equal to `Http2Session`, the `PerformanceEntry` will contain the
|
||||||
following additional properties:
|
following additional properties:
|
||||||
|
|
||||||
|
* `bytesRead` {number} The number of bytes received for this `Http2Session`.
|
||||||
|
* `bytesWritten` {number} The number of bytes sent for this `Http2Session`.
|
||||||
|
* `framesReceived` {number} The number of HTTP/2 frames received by the
|
||||||
|
`Http2Session`.
|
||||||
|
* `framesSent` {number} The number of HTTP/2 frames sent by the `Http2Session`.
|
||||||
|
* `maxConcurrentStreams` {number} The maximum number of streams concurrently
|
||||||
|
open during the lifetime of the `Http2Session`.
|
||||||
* `pingRTT` {number} The number of milliseconds elapsed since the transmission
|
* `pingRTT` {number} The number of milliseconds elapsed since the transmission
|
||||||
of a `PING` frame and the reception of its acknowledgment. Only present if
|
of a `PING` frame and the reception of its acknowledgment. Only present if
|
||||||
a `PING` frame has been sent on the `Http2Session`.
|
a `PING` frame has been sent on the `Http2Session`.
|
||||||
* `streamCount` {number} The number of `Http2Stream` instances processed by
|
|
||||||
the `Http2Session`.
|
|
||||||
* `streamAverageDuration` {number} The average duration (in milliseconds) for
|
* `streamAverageDuration` {number} The average duration (in milliseconds) for
|
||||||
all `Http2Stream` instances.
|
all `Http2Stream` instances.
|
||||||
* `framesReceived` {number} The number of HTTP/2 frames received by the
|
* `streamCount` {number} The number of `Http2Stream` instances processed by
|
||||||
`Http2Session`.
|
the `Http2Session`.
|
||||||
* `type` {string} Either `'server'` or `'client'` to identify the type of
|
* `type` {string} Either `'server'` or `'client'` to identify the type of
|
||||||
`Http2Session`.
|
`Http2Session`.
|
||||||
|
|
||||||
|
@ -66,6 +66,70 @@ const observerableTypes = [
|
|||||||
'http2'
|
'http2'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const IDX_STREAM_STATS_ID = 0;
|
||||||
|
const IDX_STREAM_STATS_TIMETOFIRSTBYTE = 1;
|
||||||
|
const IDX_STREAM_STATS_TIMETOFIRSTHEADER = 2;
|
||||||
|
const IDX_STREAM_STATS_TIMETOFIRSTBYTESENT = 3;
|
||||||
|
const IDX_STREAM_STATS_SENTBYTES = 4;
|
||||||
|
const IDX_STREAM_STATS_RECEIVEDBYTES = 5;
|
||||||
|
|
||||||
|
const IDX_SESSION_STATS_TYPE = 0;
|
||||||
|
const IDX_SESSION_STATS_PINGRTT = 1;
|
||||||
|
const IDX_SESSION_STATS_FRAMESRECEIVED = 2;
|
||||||
|
const IDX_SESSION_STATS_FRAMESSENT = 3;
|
||||||
|
const IDX_SESSION_STATS_STREAMCOUNT = 4;
|
||||||
|
const IDX_SESSION_STATS_STREAMAVERAGEDURATION = 5;
|
||||||
|
const IDX_SESSION_STATS_DATA_SENT = 6;
|
||||||
|
const IDX_SESSION_STATS_DATA_RECEIVED = 7;
|
||||||
|
const IDX_SESSION_STATS_MAX_CONCURRENT_STREAMS = 8;
|
||||||
|
|
||||||
|
let sessionStats;
|
||||||
|
let streamStats;
|
||||||
|
|
||||||
|
function collectHttp2Stats(entry) {
|
||||||
|
switch (entry.name) {
|
||||||
|
case 'Http2Stream':
|
||||||
|
if (streamStats === undefined)
|
||||||
|
streamStats = process.binding('http2').streamStats;
|
||||||
|
entry.id =
|
||||||
|
streamStats[IDX_STREAM_STATS_ID] >>> 0;
|
||||||
|
entry.timeToFirstByte =
|
||||||
|
streamStats[IDX_STREAM_STATS_TIMETOFIRSTBYTE];
|
||||||
|
entry.timeToFirstHeader =
|
||||||
|
streamStats[IDX_STREAM_STATS_TIMETOFIRSTHEADER];
|
||||||
|
entry.timeToFirstByteSent =
|
||||||
|
streamStats[IDX_STREAM_STATS_TIMETOFIRSTBYTESENT];
|
||||||
|
entry.bytesWritten =
|
||||||
|
streamStats[IDX_STREAM_STATS_SENTBYTES];
|
||||||
|
entry.bytesRead =
|
||||||
|
streamStats[IDX_STREAM_STATS_RECEIVEDBYTES];
|
||||||
|
break;
|
||||||
|
case 'Http2Session':
|
||||||
|
if (sessionStats === undefined)
|
||||||
|
sessionStats = process.binding('http2').sessionStats;
|
||||||
|
entry.type =
|
||||||
|
sessionStats[IDX_SESSION_STATS_TYPE] >>> 0 === 0 ? 'server' : 'client';
|
||||||
|
entry.pingRTT =
|
||||||
|
sessionStats[IDX_SESSION_STATS_PINGRTT];
|
||||||
|
entry.framesReceived =
|
||||||
|
sessionStats[IDX_SESSION_STATS_FRAMESRECEIVED];
|
||||||
|
entry.framesSent =
|
||||||
|
sessionStats[IDX_SESSION_STATS_FRAMESSENT];
|
||||||
|
entry.streamCount =
|
||||||
|
sessionStats[IDX_SESSION_STATS_STREAMCOUNT];
|
||||||
|
entry.streamAverageDuration =
|
||||||
|
sessionStats[IDX_SESSION_STATS_STREAMAVERAGEDURATION];
|
||||||
|
entry.bytesWritten =
|
||||||
|
sessionStats[IDX_SESSION_STATS_DATA_SENT];
|
||||||
|
entry.bytesRead =
|
||||||
|
sessionStats[IDX_SESSION_STATS_DATA_RECEIVED];
|
||||||
|
entry.maxConcurrentStreams =
|
||||||
|
sessionStats[IDX_SESSION_STATS_MAX_CONCURRENT_STREAMS];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
let errors;
|
let errors;
|
||||||
function lazyErrors() {
|
function lazyErrors() {
|
||||||
if (errors === undefined)
|
if (errors === undefined)
|
||||||
@ -467,6 +531,10 @@ function doNotify() {
|
|||||||
// Set up the callback used to receive PerformanceObserver notifications
|
// Set up the callback used to receive PerformanceObserver notifications
|
||||||
function observersCallback(entry) {
|
function observersCallback(entry) {
|
||||||
const type = mapTypes(entry.entryType);
|
const type = mapTypes(entry.entryType);
|
||||||
|
|
||||||
|
if (type === NODE_PERFORMANCE_ENTRY_TYPE_HTTP2)
|
||||||
|
collectHttp2Stats(entry);
|
||||||
|
|
||||||
performance[kInsertEntry](entry);
|
performance[kInsertEntry](entry);
|
||||||
const list = getObserversList(type);
|
const list = getObserversList(type);
|
||||||
|
|
||||||
|
@ -471,6 +471,8 @@ Http2Session::Callbacks::Callbacks(bool kHasGetPaddingCallback) {
|
|||||||
callbacks, OnSendData);
|
callbacks, OnSendData);
|
||||||
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
|
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
|
||||||
callbacks, OnInvalidFrame);
|
callbacks, OnInvalidFrame);
|
||||||
|
nghttp2_session_callbacks_set_on_frame_send_callback(
|
||||||
|
callbacks, OnFrameSent);
|
||||||
|
|
||||||
if (kHasGetPaddingCallback) {
|
if (kHasGetPaddingCallback) {
|
||||||
nghttp2_session_callbacks_set_select_padding_callback(
|
nghttp2_session_callbacks_set_select_padding_callback(
|
||||||
@ -559,28 +561,35 @@ inline void Http2Stream::EmitStatistics() {
|
|||||||
if (!HasHttp2Observer(env()))
|
if (!HasHttp2Observer(env()))
|
||||||
return;
|
return;
|
||||||
Http2StreamPerformanceEntry* entry =
|
Http2StreamPerformanceEntry* entry =
|
||||||
new Http2StreamPerformanceEntry(env(), statistics_);
|
new Http2StreamPerformanceEntry(env(), id_, statistics_);
|
||||||
env()->SetImmediate([](Environment* env, void* data) {
|
env()->SetImmediate([](Environment* env, void* data) {
|
||||||
Local<Context> context = env->context();
|
|
||||||
Http2StreamPerformanceEntry* entry =
|
Http2StreamPerformanceEntry* entry =
|
||||||
static_cast<Http2StreamPerformanceEntry*>(data);
|
static_cast<Http2StreamPerformanceEntry*>(data);
|
||||||
if (HasHttp2Observer(env)) {
|
if (HasHttp2Observer(env)) {
|
||||||
Local<Object> obj = entry->ToObject();
|
AliasedBuffer<double, v8::Float64Array>& buffer =
|
||||||
v8::PropertyAttribute attr =
|
env->http2_state()->stream_stats_buffer;
|
||||||
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
|
buffer[IDX_STREAM_STATS_ID] = entry->id();
|
||||||
obj->DefineOwnProperty(
|
if (entry->first_byte() != 0) {
|
||||||
context,
|
buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTE] =
|
||||||
FIXED_ONE_BYTE_STRING(env->isolate(), "timeToFirstByte"),
|
(entry->first_byte() - entry->startTimeNano()) / 1e6;
|
||||||
Number::New(env->isolate(),
|
} else {
|
||||||
(entry->first_byte() - entry->startTimeNano()) / 1e6),
|
buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTE] = 0;
|
||||||
attr).FromJust();
|
}
|
||||||
obj->DefineOwnProperty(
|
if (entry->first_header() != 0) {
|
||||||
context,
|
buffer[IDX_STREAM_STATS_TIMETOFIRSTHEADER] =
|
||||||
FIXED_ONE_BYTE_STRING(env->isolate(), "timeToFirstHeader"),
|
(entry->first_header() - entry->startTimeNano()) / 1e6;
|
||||||
Number::New(env->isolate(),
|
} else {
|
||||||
(entry->first_header() - entry->startTimeNano()) / 1e6),
|
buffer[IDX_STREAM_STATS_TIMETOFIRSTHEADER] = 0;
|
||||||
attr).FromJust();
|
}
|
||||||
entry->Notify(obj);
|
if (entry->first_byte_sent() != 0) {
|
||||||
|
buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTESENT] =
|
||||||
|
(entry->first_byte_sent() - entry->startTimeNano()) / 1e6;
|
||||||
|
} else {
|
||||||
|
buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTESENT] = 0;
|
||||||
|
}
|
||||||
|
buffer[IDX_STREAM_STATS_SENTBYTES] = entry->sent_bytes();
|
||||||
|
buffer[IDX_STREAM_STATS_RECEIVEDBYTES] = entry->received_bytes();
|
||||||
|
entry->Notify(entry->ToObject());
|
||||||
}
|
}
|
||||||
delete entry;
|
delete entry;
|
||||||
}, static_cast<void*>(entry));
|
}, static_cast<void*>(entry));
|
||||||
@ -590,45 +599,25 @@ inline void Http2Session::EmitStatistics() {
|
|||||||
if (!HasHttp2Observer(env()))
|
if (!HasHttp2Observer(env()))
|
||||||
return;
|
return;
|
||||||
Http2SessionPerformanceEntry* entry =
|
Http2SessionPerformanceEntry* entry =
|
||||||
new Http2SessionPerformanceEntry(env(), statistics_, TypeName());
|
new Http2SessionPerformanceEntry(env(), statistics_, session_type_);
|
||||||
env()->SetImmediate([](Environment* env, void* data) {
|
env()->SetImmediate([](Environment* env, void* data) {
|
||||||
Local<Context> context = env->context();
|
|
||||||
Http2SessionPerformanceEntry* entry =
|
Http2SessionPerformanceEntry* entry =
|
||||||
static_cast<Http2SessionPerformanceEntry*>(data);
|
static_cast<Http2SessionPerformanceEntry*>(data);
|
||||||
if (HasHttp2Observer(env)) {
|
if (HasHttp2Observer(env)) {
|
||||||
Local<Object> obj = entry->ToObject();
|
AliasedBuffer<double, v8::Float64Array>& buffer =
|
||||||
v8::PropertyAttribute attr =
|
env->http2_state()->session_stats_buffer;
|
||||||
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
|
buffer[IDX_SESSION_STATS_TYPE] = entry->type();
|
||||||
obj->DefineOwnProperty(
|
buffer[IDX_SESSION_STATS_PINGRTT] = entry->ping_rtt() / 1e6;
|
||||||
context,
|
buffer[IDX_SESSION_STATS_FRAMESRECEIVED] = entry->frame_count();
|
||||||
FIXED_ONE_BYTE_STRING(env->isolate(), "type"),
|
buffer[IDX_SESSION_STATS_FRAMESSENT] = entry->frame_sent();
|
||||||
String::NewFromUtf8(env->isolate(),
|
buffer[IDX_SESSION_STATS_STREAMCOUNT] = entry->stream_count();
|
||||||
entry->typeName(),
|
buffer[IDX_SESSION_STATS_STREAMAVERAGEDURATION] =
|
||||||
v8::NewStringType::kInternalized)
|
entry->stream_average_duration();
|
||||||
.ToLocalChecked(), attr).FromJust();
|
buffer[IDX_SESSION_STATS_DATA_SENT] = entry->data_sent();
|
||||||
if (entry->ping_rtt() != 0) {
|
buffer[IDX_SESSION_STATS_DATA_RECEIVED] = entry->data_received();
|
||||||
obj->DefineOwnProperty(
|
buffer[IDX_SESSION_STATS_MAX_CONCURRENT_STREAMS] =
|
||||||
context,
|
entry->max_concurrent_streams();
|
||||||
FIXED_ONE_BYTE_STRING(env->isolate(), "pingRTT"),
|
entry->Notify(entry->ToObject());
|
||||||
Number::New(env->isolate(), entry->ping_rtt() / 1e6),
|
|
||||||
attr).FromJust();
|
|
||||||
}
|
|
||||||
obj->DefineOwnProperty(
|
|
||||||
context,
|
|
||||||
FIXED_ONE_BYTE_STRING(env->isolate(), "framesReceived"),
|
|
||||||
Integer::NewFromUnsigned(env->isolate(), entry->frame_count()),
|
|
||||||
attr).FromJust();
|
|
||||||
obj->DefineOwnProperty(
|
|
||||||
context,
|
|
||||||
FIXED_ONE_BYTE_STRING(env->isolate(), "streamCount"),
|
|
||||||
Integer::New(env->isolate(), entry->stream_count()),
|
|
||||||
attr).FromJust();
|
|
||||||
obj->DefineOwnProperty(
|
|
||||||
context,
|
|
||||||
FIXED_ONE_BYTE_STRING(env->isolate(), "streamAverageDuration"),
|
|
||||||
Number::New(env->isolate(), entry->stream_average_duration()),
|
|
||||||
attr).FromJust();
|
|
||||||
entry->Notify(obj);
|
|
||||||
}
|
}
|
||||||
delete entry;
|
delete entry;
|
||||||
}, static_cast<void*>(entry));
|
}, static_cast<void*>(entry));
|
||||||
@ -694,6 +683,9 @@ inline bool Http2Session::CanAddStream() {
|
|||||||
inline void Http2Session::AddStream(Http2Stream* stream) {
|
inline void Http2Session::AddStream(Http2Stream* stream) {
|
||||||
CHECK_GE(++statistics_.stream_count, 0);
|
CHECK_GE(++statistics_.stream_count, 0);
|
||||||
streams_[stream->id()] = stream;
|
streams_[stream->id()] = stream;
|
||||||
|
size_t size = streams_.size();
|
||||||
|
if (size > statistics_.max_concurrent_streams)
|
||||||
|
statistics_.max_concurrent_streams = size;
|
||||||
IncrementCurrentSessionMemory(stream->self_size());
|
IncrementCurrentSessionMemory(stream->self_size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -962,6 +954,14 @@ inline int Http2Session::OnFrameNotSent(nghttp2_session* handle,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline int Http2Session::OnFrameSent(nghttp2_session* handle,
|
||||||
|
const nghttp2_frame* frame,
|
||||||
|
void* user_data) {
|
||||||
|
Http2Session* session = static_cast<Http2Session*>(user_data);
|
||||||
|
session->statistics_.frame_sent += 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Called by nghttp2 when a stream closes.
|
// Called by nghttp2 when a stream closes.
|
||||||
inline int Http2Session::OnStreamClose(nghttp2_session* handle,
|
inline int Http2Session::OnStreamClose(nghttp2_session* handle,
|
||||||
int32_t id,
|
int32_t id,
|
||||||
@ -1039,6 +1039,7 @@ inline int Http2Session::OnDataChunkReceived(nghttp2_session* handle,
|
|||||||
// If the stream has been destroyed, ignore this chunk
|
// If the stream has been destroyed, ignore this chunk
|
||||||
if (stream->IsDestroyed())
|
if (stream->IsDestroyed())
|
||||||
return 0;
|
return 0;
|
||||||
|
stream->statistics_.received_bytes += len;
|
||||||
stream->AddChunk(data, len);
|
stream->AddChunk(data, len);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -1493,6 +1494,7 @@ void Http2Session::SendPendingData() {
|
|||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (const nghttp2_stream_write& write : outgoing_buffers_) {
|
for (const nghttp2_stream_write& write : outgoing_buffers_) {
|
||||||
|
statistics_.data_sent += write.buf.len;
|
||||||
if (write.buf.base == nullptr) {
|
if (write.buf.base == nullptr) {
|
||||||
bufs[i++] = uv_buf_init(
|
bufs[i++] = uv_buf_init(
|
||||||
reinterpret_cast<char*>(outgoing_storage_.data() + offset),
|
reinterpret_cast<char*>(outgoing_storage_.data() + offset),
|
||||||
@ -1642,6 +1644,7 @@ void Http2Session::OnStreamReadImpl(ssize_t nread,
|
|||||||
if (bufs->len > 0) {
|
if (bufs->len > 0) {
|
||||||
// Only pass data on if nread > 0
|
// Only pass data on if nread > 0
|
||||||
uv_buf_t buf[] { uv_buf_init((*bufs).base, nread) };
|
uv_buf_t buf[] { uv_buf_init((*bufs).base, nread) };
|
||||||
|
session->statistics_.data_received += nread;
|
||||||
ssize_t ret = session->Write(buf, 1);
|
ssize_t ret = session->Write(buf, 1);
|
||||||
|
|
||||||
// Note: if ssize_t is not defined (e.g. on Win32), nghttp2 will typedef
|
// Note: if ssize_t is not defined (e.g. on Win32), nghttp2 will typedef
|
||||||
@ -2141,6 +2144,8 @@ ssize_t Http2Stream::Provider::FD::OnRead(nghttp2_session* handle,
|
|||||||
void* user_data) {
|
void* user_data) {
|
||||||
Http2Session* session = static_cast<Http2Session*>(user_data);
|
Http2Session* session = static_cast<Http2Session*>(user_data);
|
||||||
Http2Stream* stream = session->FindStream(id);
|
Http2Stream* stream = session->FindStream(id);
|
||||||
|
if (stream->statistics_.first_byte_sent == 0)
|
||||||
|
stream->statistics_.first_byte_sent = uv_hrtime();
|
||||||
|
|
||||||
DEBUG_HTTP2SESSION2(session, "reading outbound file data for stream %d", id);
|
DEBUG_HTTP2SESSION2(session, "reading outbound file data for stream %d", id);
|
||||||
CHECK_EQ(id, stream->id());
|
CHECK_EQ(id, stream->id());
|
||||||
@ -2191,6 +2196,7 @@ ssize_t Http2Stream::Provider::FD::OnRead(nghttp2_session* handle,
|
|||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stream->statistics_.sent_bytes += numchars;
|
||||||
return numchars;
|
return numchars;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2216,6 +2222,8 @@ ssize_t Http2Stream::Provider::Stream::OnRead(nghttp2_session* handle,
|
|||||||
Http2Session* session = static_cast<Http2Session*>(user_data);
|
Http2Session* session = static_cast<Http2Session*>(user_data);
|
||||||
DEBUG_HTTP2SESSION2(session, "reading outbound data for stream %d", id);
|
DEBUG_HTTP2SESSION2(session, "reading outbound data for stream %d", id);
|
||||||
Http2Stream* stream = GetStream(session, id, source);
|
Http2Stream* stream = GetStream(session, id, source);
|
||||||
|
if (stream->statistics_.first_byte_sent == 0)
|
||||||
|
stream->statistics_.first_byte_sent = uv_hrtime();
|
||||||
CHECK_EQ(id, stream->id());
|
CHECK_EQ(id, stream->id());
|
||||||
|
|
||||||
size_t amount = 0; // amount of data being sent in this data frame.
|
size_t amount = 0; // amount of data being sent in this data frame.
|
||||||
@ -2249,6 +2257,8 @@ ssize_t Http2Stream::Provider::Stream::OnRead(nghttp2_session* handle,
|
|||||||
if (session->IsDestroyed())
|
if (session->IsDestroyed())
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stream->statistics_.sent_bytes += amount;
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2862,6 +2872,10 @@ void Initialize(Local<Object> target,
|
|||||||
"settingsBuffer", state->settings_buffer.GetJSArray());
|
"settingsBuffer", state->settings_buffer.GetJSArray());
|
||||||
SET_STATE_TYPEDARRAY(
|
SET_STATE_TYPEDARRAY(
|
||||||
"optionsBuffer", state->options_buffer.GetJSArray());
|
"optionsBuffer", state->options_buffer.GetJSArray());
|
||||||
|
SET_STATE_TYPEDARRAY(
|
||||||
|
"streamStats", state->stream_stats_buffer.GetJSArray());
|
||||||
|
SET_STATE_TYPEDARRAY(
|
||||||
|
"sessionStats", state->session_stats_buffer.GetJSArray());
|
||||||
#undef SET_STATE_TYPEDARRAY
|
#undef SET_STATE_TYPEDARRAY
|
||||||
|
|
||||||
env->set_http2_state(std::move(state));
|
env->set_http2_state(std::move(state));
|
||||||
|
@ -715,8 +715,11 @@ class Http2Stream : public AsyncWrap,
|
|||||||
struct Statistics {
|
struct Statistics {
|
||||||
uint64_t start_time;
|
uint64_t start_time;
|
||||||
uint64_t end_time;
|
uint64_t end_time;
|
||||||
uint64_t first_header; // Time first header was received
|
uint64_t first_header; // Time first header was received
|
||||||
uint64_t first_byte; // Time first data frame byte was received
|
uint64_t first_byte; // Time first DATA frame byte was received
|
||||||
|
uint64_t first_byte_sent; // Time first DATA frame byte was sent
|
||||||
|
uint64_t sent_bytes;
|
||||||
|
uint64_t received_bytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
Statistics statistics_ = {};
|
Statistics statistics_ = {};
|
||||||
@ -949,8 +952,12 @@ class Http2Session : public AsyncWrap {
|
|||||||
uint64_t start_time;
|
uint64_t start_time;
|
||||||
uint64_t end_time;
|
uint64_t end_time;
|
||||||
uint64_t ping_rtt;
|
uint64_t ping_rtt;
|
||||||
|
uint64_t data_sent;
|
||||||
|
uint64_t data_received;
|
||||||
uint32_t frame_count;
|
uint32_t frame_count;
|
||||||
|
uint32_t frame_sent;
|
||||||
int32_t stream_count;
|
int32_t stream_count;
|
||||||
|
size_t max_concurrent_streams;
|
||||||
double stream_average_duration;
|
double stream_average_duration;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -995,6 +1002,10 @@ class Http2Session : public AsyncWrap {
|
|||||||
const nghttp2_frame* frame,
|
const nghttp2_frame* frame,
|
||||||
int error_code,
|
int error_code,
|
||||||
void* user_data);
|
void* user_data);
|
||||||
|
static inline int OnFrameSent(
|
||||||
|
nghttp2_session* session,
|
||||||
|
const nghttp2_frame* frame,
|
||||||
|
void* user_data);
|
||||||
static inline int OnStreamClose(
|
static inline int OnStreamClose(
|
||||||
nghttp2_session* session,
|
nghttp2_session* session,
|
||||||
int32_t id,
|
int32_t id,
|
||||||
@ -1115,21 +1126,29 @@ class Http2SessionPerformanceEntry : public PerformanceEntry {
|
|||||||
Http2SessionPerformanceEntry(
|
Http2SessionPerformanceEntry(
|
||||||
Environment* env,
|
Environment* env,
|
||||||
const Http2Session::Statistics& stats,
|
const Http2Session::Statistics& stats,
|
||||||
const char* kind) :
|
nghttp2_session_type type) :
|
||||||
PerformanceEntry(env, "Http2Session", "http2",
|
PerformanceEntry(env, "Http2Session", "http2",
|
||||||
stats.start_time,
|
stats.start_time,
|
||||||
stats.end_time),
|
stats.end_time),
|
||||||
ping_rtt_(stats.ping_rtt),
|
ping_rtt_(stats.ping_rtt),
|
||||||
|
data_sent_(stats.data_sent),
|
||||||
|
data_received_(stats.data_received),
|
||||||
frame_count_(stats.frame_count),
|
frame_count_(stats.frame_count),
|
||||||
|
frame_sent_(stats.frame_sent),
|
||||||
stream_count_(stats.stream_count),
|
stream_count_(stats.stream_count),
|
||||||
|
max_concurrent_streams_(stats.max_concurrent_streams),
|
||||||
stream_average_duration_(stats.stream_average_duration),
|
stream_average_duration_(stats.stream_average_duration),
|
||||||
kind_(kind) { }
|
session_type_(type) { }
|
||||||
|
|
||||||
uint64_t ping_rtt() const { return ping_rtt_; }
|
uint64_t ping_rtt() const { return ping_rtt_; }
|
||||||
|
uint64_t data_sent() const { return data_sent_; }
|
||||||
|
uint64_t data_received() const { return data_received_; }
|
||||||
uint32_t frame_count() const { return frame_count_; }
|
uint32_t frame_count() const { return frame_count_; }
|
||||||
|
uint32_t frame_sent() const { return frame_sent_; }
|
||||||
int32_t stream_count() const { return stream_count_; }
|
int32_t stream_count() const { return stream_count_; }
|
||||||
|
size_t max_concurrent_streams() const { return max_concurrent_streams_; }
|
||||||
double stream_average_duration() const { return stream_average_duration_; }
|
double stream_average_duration() const { return stream_average_duration_; }
|
||||||
const char* typeName() const { return kind_; }
|
nghttp2_session_type type() const { return session_type_; }
|
||||||
|
|
||||||
void Notify(Local<Value> obj) {
|
void Notify(Local<Value> obj) {
|
||||||
PerformanceEntry::Notify(env(), kind(), obj);
|
PerformanceEntry::Notify(env(), kind(), obj);
|
||||||
@ -1137,33 +1156,50 @@ class Http2SessionPerformanceEntry : public PerformanceEntry {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t ping_rtt_;
|
uint64_t ping_rtt_;
|
||||||
|
uint64_t data_sent_;
|
||||||
|
uint64_t data_received_;
|
||||||
uint32_t frame_count_;
|
uint32_t frame_count_;
|
||||||
|
uint32_t frame_sent_;
|
||||||
int32_t stream_count_;
|
int32_t stream_count_;
|
||||||
|
size_t max_concurrent_streams_;
|
||||||
double stream_average_duration_;
|
double stream_average_duration_;
|
||||||
const char* kind_;
|
nghttp2_session_type session_type_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Http2StreamPerformanceEntry : public PerformanceEntry {
|
class Http2StreamPerformanceEntry : public PerformanceEntry {
|
||||||
public:
|
public:
|
||||||
Http2StreamPerformanceEntry(
|
Http2StreamPerformanceEntry(
|
||||||
Environment* env,
|
Environment* env,
|
||||||
|
int32_t id,
|
||||||
const Http2Stream::Statistics& stats) :
|
const Http2Stream::Statistics& stats) :
|
||||||
PerformanceEntry(env, "Http2Stream", "http2",
|
PerformanceEntry(env, "Http2Stream", "http2",
|
||||||
stats.start_time,
|
stats.start_time,
|
||||||
stats.end_time),
|
stats.end_time),
|
||||||
|
id_(id),
|
||||||
first_header_(stats.first_header),
|
first_header_(stats.first_header),
|
||||||
first_byte_(stats.first_byte) { }
|
first_byte_(stats.first_byte),
|
||||||
|
first_byte_sent_(stats.first_byte_sent),
|
||||||
|
sent_bytes_(stats.sent_bytes),
|
||||||
|
received_bytes_(stats.received_bytes) { }
|
||||||
|
|
||||||
|
int32_t id() const { return id_; }
|
||||||
uint64_t first_header() const { return first_header_; }
|
uint64_t first_header() const { return first_header_; }
|
||||||
uint64_t first_byte() const { return first_byte_; }
|
uint64_t first_byte() const { return first_byte_; }
|
||||||
|
uint64_t first_byte_sent() const { return first_byte_sent_; }
|
||||||
|
uint64_t sent_bytes() const { return sent_bytes_; }
|
||||||
|
uint64_t received_bytes() const { return received_bytes_; }
|
||||||
|
|
||||||
void Notify(Local<Value> obj) {
|
void Notify(Local<Value> obj) {
|
||||||
PerformanceEntry::Notify(env(), kind(), obj);
|
PerformanceEntry::Notify(env(), kind(), obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
int32_t id_;
|
||||||
uint64_t first_header_;
|
uint64_t first_header_;
|
||||||
uint64_t first_byte_;
|
uint64_t first_byte_;
|
||||||
|
uint64_t first_byte_sent_;
|
||||||
|
uint64_t sent_bytes_;
|
||||||
|
uint64_t received_bytes_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Http2Session::Http2Ping : public AsyncWrap {
|
class Http2Session::Http2Ping : public AsyncWrap {
|
||||||
|
@ -61,6 +61,29 @@ namespace http2 {
|
|||||||
PADDING_BUF_FIELD_COUNT
|
PADDING_BUF_FIELD_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Http2StreamStatisticsIndex {
|
||||||
|
IDX_STREAM_STATS_ID,
|
||||||
|
IDX_STREAM_STATS_TIMETOFIRSTBYTE,
|
||||||
|
IDX_STREAM_STATS_TIMETOFIRSTHEADER,
|
||||||
|
IDX_STREAM_STATS_TIMETOFIRSTBYTESENT,
|
||||||
|
IDX_STREAM_STATS_SENTBYTES,
|
||||||
|
IDX_STREAM_STATS_RECEIVEDBYTES,
|
||||||
|
IDX_STREAM_STATS_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Http2SessionStatisticsIndex {
|
||||||
|
IDX_SESSION_STATS_TYPE,
|
||||||
|
IDX_SESSION_STATS_PINGRTT,
|
||||||
|
IDX_SESSION_STATS_FRAMESRECEIVED,
|
||||||
|
IDX_SESSION_STATS_FRAMESSENT,
|
||||||
|
IDX_SESSION_STATS_STREAMCOUNT,
|
||||||
|
IDX_SESSION_STATS_STREAMAVERAGEDURATION,
|
||||||
|
IDX_SESSION_STATS_DATA_SENT,
|
||||||
|
IDX_SESSION_STATS_DATA_RECEIVED,
|
||||||
|
IDX_SESSION_STATS_MAX_CONCURRENT_STREAMS,
|
||||||
|
IDX_SESSION_STATS_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
class http2_state {
|
class http2_state {
|
||||||
public:
|
public:
|
||||||
explicit http2_state(v8::Isolate* isolate) :
|
explicit http2_state(v8::Isolate* isolate) :
|
||||||
@ -77,6 +100,16 @@ class http2_state {
|
|||||||
offsetof(http2_state_internal, stream_state_buffer),
|
offsetof(http2_state_internal, stream_state_buffer),
|
||||||
IDX_STREAM_STATE_COUNT,
|
IDX_STREAM_STATE_COUNT,
|
||||||
root_buffer),
|
root_buffer),
|
||||||
|
stream_stats_buffer(
|
||||||
|
isolate,
|
||||||
|
offsetof(http2_state_internal, stream_stats_buffer),
|
||||||
|
IDX_STREAM_STATS_COUNT,
|
||||||
|
root_buffer),
|
||||||
|
session_stats_buffer(
|
||||||
|
isolate,
|
||||||
|
offsetof(http2_state_internal, session_stats_buffer),
|
||||||
|
IDX_SESSION_STATS_COUNT,
|
||||||
|
root_buffer),
|
||||||
padding_buffer(
|
padding_buffer(
|
||||||
isolate,
|
isolate,
|
||||||
offsetof(http2_state_internal, padding_buffer),
|
offsetof(http2_state_internal, padding_buffer),
|
||||||
@ -97,6 +130,8 @@ class http2_state {
|
|||||||
AliasedBuffer<uint8_t, v8::Uint8Array> root_buffer;
|
AliasedBuffer<uint8_t, v8::Uint8Array> root_buffer;
|
||||||
AliasedBuffer<double, v8::Float64Array> session_state_buffer;
|
AliasedBuffer<double, v8::Float64Array> session_state_buffer;
|
||||||
AliasedBuffer<double, v8::Float64Array> stream_state_buffer;
|
AliasedBuffer<double, v8::Float64Array> stream_state_buffer;
|
||||||
|
AliasedBuffer<double, v8::Float64Array> stream_stats_buffer;
|
||||||
|
AliasedBuffer<double, v8::Float64Array> session_stats_buffer;
|
||||||
AliasedBuffer<uint32_t, v8::Uint32Array> padding_buffer;
|
AliasedBuffer<uint32_t, v8::Uint32Array> padding_buffer;
|
||||||
AliasedBuffer<uint32_t, v8::Uint32Array> options_buffer;
|
AliasedBuffer<uint32_t, v8::Uint32Array> options_buffer;
|
||||||
AliasedBuffer<uint32_t, v8::Uint32Array> settings_buffer;
|
AliasedBuffer<uint32_t, v8::Uint32Array> settings_buffer;
|
||||||
@ -106,6 +141,8 @@ class http2_state {
|
|||||||
// doubles first so that they are always sizeof(double)-aligned
|
// doubles first so that they are always sizeof(double)-aligned
|
||||||
double session_state_buffer[IDX_SESSION_STATE_COUNT];
|
double session_state_buffer[IDX_SESSION_STATE_COUNT];
|
||||||
double stream_state_buffer[IDX_STREAM_STATE_COUNT];
|
double stream_state_buffer[IDX_STREAM_STATE_COUNT];
|
||||||
|
double stream_stats_buffer[IDX_STREAM_STATS_COUNT];
|
||||||
|
double session_stats_buffer[IDX_SESSION_STATS_COUNT];
|
||||||
uint32_t padding_buffer[PADDING_BUF_FIELD_COUNT];
|
uint32_t padding_buffer[PADDING_BUF_FIELD_COUNT];
|
||||||
uint32_t options_buffer[IDX_OPTIONS_FLAGS + 1];
|
uint32_t options_buffer[IDX_OPTIONS_FLAGS + 1];
|
||||||
uint32_t settings_buffer[IDX_SETTINGS_COUNT + 1];
|
uint32_t settings_buffer[IDX_SETTINGS_COUNT + 1];
|
||||||
|
@ -8,7 +8,7 @@ const h2 = require('http2');
|
|||||||
|
|
||||||
const { PerformanceObserver } = require('perf_hooks');
|
const { PerformanceObserver } = require('perf_hooks');
|
||||||
|
|
||||||
const obs = new PerformanceObserver((items) => {
|
const obs = new PerformanceObserver(common.mustCall((items) => {
|
||||||
const entry = items.getEntries()[0];
|
const entry = items.getEntries()[0];
|
||||||
assert.strictEqual(entry.entryType, 'http2');
|
assert.strictEqual(entry.entryType, 'http2');
|
||||||
assert.strictEqual(typeof entry.startTime, 'number');
|
assert.strictEqual(typeof entry.startTime, 'number');
|
||||||
@ -19,6 +19,10 @@ const obs = new PerformanceObserver((items) => {
|
|||||||
assert.strictEqual(typeof entry.streamAverageDuration, 'number');
|
assert.strictEqual(typeof entry.streamAverageDuration, 'number');
|
||||||
assert.strictEqual(typeof entry.streamCount, 'number');
|
assert.strictEqual(typeof entry.streamCount, 'number');
|
||||||
assert.strictEqual(typeof entry.framesReceived, 'number');
|
assert.strictEqual(typeof entry.framesReceived, 'number');
|
||||||
|
assert.strictEqual(typeof entry.framesSent, 'number');
|
||||||
|
assert.strictEqual(typeof entry.bytesWritten, 'number');
|
||||||
|
assert.strictEqual(typeof entry.bytesRead, 'number');
|
||||||
|
assert.strictEqual(typeof entry.maxConcurrentStreams, 'number');
|
||||||
switch (entry.type) {
|
switch (entry.type) {
|
||||||
case 'server':
|
case 'server':
|
||||||
assert.strictEqual(entry.streamCount, 1);
|
assert.strictEqual(entry.streamCount, 1);
|
||||||
@ -34,12 +38,15 @@ const obs = new PerformanceObserver((items) => {
|
|||||||
break;
|
break;
|
||||||
case 'Http2Stream':
|
case 'Http2Stream':
|
||||||
assert.strictEqual(typeof entry.timeToFirstByte, 'number');
|
assert.strictEqual(typeof entry.timeToFirstByte, 'number');
|
||||||
|
assert.strictEqual(typeof entry.timeToFirstByteSent, 'number');
|
||||||
assert.strictEqual(typeof entry.timeToFirstHeader, 'number');
|
assert.strictEqual(typeof entry.timeToFirstHeader, 'number');
|
||||||
|
assert.strictEqual(typeof entry.bytesWritten, 'number');
|
||||||
|
assert.strictEqual(typeof entry.bytesRead, 'number');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert.fail('invalid entry name');
|
assert.fail('invalid entry name');
|
||||||
}
|
}
|
||||||
});
|
}, 4));
|
||||||
obs.observe({ entryTypes: ['http2'] });
|
obs.observe({ entryTypes: ['http2'] });
|
||||||
|
|
||||||
const body =
|
const body =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user