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
|
||||
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
|
||||
`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
|
||||
`PerformanceEntry` `startTime` and the reception of the first header.
|
||||
|
||||
If `name` is equal to `Http2Session`, the `PerformanceEntry` will contain the
|
||||
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
|
||||
of a `PING` frame and the reception of its acknowledgment. Only present if
|
||||
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
|
||||
all `Http2Stream` instances.
|
||||
* `framesReceived` {number} The number of HTTP/2 frames received by the
|
||||
`Http2Session`.
|
||||
* `streamCount` {number} The number of `Http2Stream` instances processed by
|
||||
the `Http2Session`.
|
||||
* `type` {string} Either `'server'` or `'client'` to identify the type of
|
||||
`Http2Session`.
|
||||
|
||||
|
@ -66,6 +66,70 @@ const observerableTypes = [
|
||||
'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;
|
||||
function lazyErrors() {
|
||||
if (errors === undefined)
|
||||
@ -467,6 +531,10 @@ function doNotify() {
|
||||
// Set up the callback used to receive PerformanceObserver notifications
|
||||
function observersCallback(entry) {
|
||||
const type = mapTypes(entry.entryType);
|
||||
|
||||
if (type === NODE_PERFORMANCE_ENTRY_TYPE_HTTP2)
|
||||
collectHttp2Stats(entry);
|
||||
|
||||
performance[kInsertEntry](entry);
|
||||
const list = getObserversList(type);
|
||||
|
||||
|
@ -471,6 +471,8 @@ Http2Session::Callbacks::Callbacks(bool kHasGetPaddingCallback) {
|
||||
callbacks, OnSendData);
|
||||
nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
|
||||
callbacks, OnInvalidFrame);
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback(
|
||||
callbacks, OnFrameSent);
|
||||
|
||||
if (kHasGetPaddingCallback) {
|
||||
nghttp2_session_callbacks_set_select_padding_callback(
|
||||
@ -559,28 +561,35 @@ inline void Http2Stream::EmitStatistics() {
|
||||
if (!HasHttp2Observer(env()))
|
||||
return;
|
||||
Http2StreamPerformanceEntry* entry =
|
||||
new Http2StreamPerformanceEntry(env(), statistics_);
|
||||
new Http2StreamPerformanceEntry(env(), id_, statistics_);
|
||||
env()->SetImmediate([](Environment* env, void* data) {
|
||||
Local<Context> context = env->context();
|
||||
Http2StreamPerformanceEntry* entry =
|
||||
static_cast<Http2StreamPerformanceEntry*>(data);
|
||||
if (HasHttp2Observer(env)) {
|
||||
Local<Object> obj = entry->ToObject();
|
||||
v8::PropertyAttribute attr =
|
||||
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
|
||||
obj->DefineOwnProperty(
|
||||
context,
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(), "timeToFirstByte"),
|
||||
Number::New(env->isolate(),
|
||||
(entry->first_byte() - entry->startTimeNano()) / 1e6),
|
||||
attr).FromJust();
|
||||
obj->DefineOwnProperty(
|
||||
context,
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(), "timeToFirstHeader"),
|
||||
Number::New(env->isolate(),
|
||||
(entry->first_header() - entry->startTimeNano()) / 1e6),
|
||||
attr).FromJust();
|
||||
entry->Notify(obj);
|
||||
AliasedBuffer<double, v8::Float64Array>& buffer =
|
||||
env->http2_state()->stream_stats_buffer;
|
||||
buffer[IDX_STREAM_STATS_ID] = entry->id();
|
||||
if (entry->first_byte() != 0) {
|
||||
buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTE] =
|
||||
(entry->first_byte() - entry->startTimeNano()) / 1e6;
|
||||
} else {
|
||||
buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTE] = 0;
|
||||
}
|
||||
if (entry->first_header() != 0) {
|
||||
buffer[IDX_STREAM_STATS_TIMETOFIRSTHEADER] =
|
||||
(entry->first_header() - entry->startTimeNano()) / 1e6;
|
||||
} else {
|
||||
buffer[IDX_STREAM_STATS_TIMETOFIRSTHEADER] = 0;
|
||||
}
|
||||
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;
|
||||
}, static_cast<void*>(entry));
|
||||
@ -590,45 +599,25 @@ inline void Http2Session::EmitStatistics() {
|
||||
if (!HasHttp2Observer(env()))
|
||||
return;
|
||||
Http2SessionPerformanceEntry* entry =
|
||||
new Http2SessionPerformanceEntry(env(), statistics_, TypeName());
|
||||
new Http2SessionPerformanceEntry(env(), statistics_, session_type_);
|
||||
env()->SetImmediate([](Environment* env, void* data) {
|
||||
Local<Context> context = env->context();
|
||||
Http2SessionPerformanceEntry* entry =
|
||||
static_cast<Http2SessionPerformanceEntry*>(data);
|
||||
if (HasHttp2Observer(env)) {
|
||||
Local<Object> obj = entry->ToObject();
|
||||
v8::PropertyAttribute attr =
|
||||
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
|
||||
obj->DefineOwnProperty(
|
||||
context,
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(), "type"),
|
||||
String::NewFromUtf8(env->isolate(),
|
||||
entry->typeName(),
|
||||
v8::NewStringType::kInternalized)
|
||||
.ToLocalChecked(), attr).FromJust();
|
||||
if (entry->ping_rtt() != 0) {
|
||||
obj->DefineOwnProperty(
|
||||
context,
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(), "pingRTT"),
|
||||
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);
|
||||
AliasedBuffer<double, v8::Float64Array>& buffer =
|
||||
env->http2_state()->session_stats_buffer;
|
||||
buffer[IDX_SESSION_STATS_TYPE] = entry->type();
|
||||
buffer[IDX_SESSION_STATS_PINGRTT] = entry->ping_rtt() / 1e6;
|
||||
buffer[IDX_SESSION_STATS_FRAMESRECEIVED] = entry->frame_count();
|
||||
buffer[IDX_SESSION_STATS_FRAMESSENT] = entry->frame_sent();
|
||||
buffer[IDX_SESSION_STATS_STREAMCOUNT] = entry->stream_count();
|
||||
buffer[IDX_SESSION_STATS_STREAMAVERAGEDURATION] =
|
||||
entry->stream_average_duration();
|
||||
buffer[IDX_SESSION_STATS_DATA_SENT] = entry->data_sent();
|
||||
buffer[IDX_SESSION_STATS_DATA_RECEIVED] = entry->data_received();
|
||||
buffer[IDX_SESSION_STATS_MAX_CONCURRENT_STREAMS] =
|
||||
entry->max_concurrent_streams();
|
||||
entry->Notify(entry->ToObject());
|
||||
}
|
||||
delete entry;
|
||||
}, static_cast<void*>(entry));
|
||||
@ -694,6 +683,9 @@ inline bool Http2Session::CanAddStream() {
|
||||
inline void Http2Session::AddStream(Http2Stream* stream) {
|
||||
CHECK_GE(++statistics_.stream_count, 0);
|
||||
streams_[stream->id()] = stream;
|
||||
size_t size = streams_.size();
|
||||
if (size > statistics_.max_concurrent_streams)
|
||||
statistics_.max_concurrent_streams = size;
|
||||
IncrementCurrentSessionMemory(stream->self_size());
|
||||
}
|
||||
|
||||
@ -962,6 +954,14 @@ inline int Http2Session::OnFrameNotSent(nghttp2_session* handle,
|
||||
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.
|
||||
inline int Http2Session::OnStreamClose(nghttp2_session* handle,
|
||||
int32_t id,
|
||||
@ -1039,6 +1039,7 @@ inline int Http2Session::OnDataChunkReceived(nghttp2_session* handle,
|
||||
// If the stream has been destroyed, ignore this chunk
|
||||
if (stream->IsDestroyed())
|
||||
return 0;
|
||||
stream->statistics_.received_bytes += len;
|
||||
stream->AddChunk(data, len);
|
||||
}
|
||||
return 0;
|
||||
@ -1493,6 +1494,7 @@ void Http2Session::SendPendingData() {
|
||||
size_t offset = 0;
|
||||
size_t i = 0;
|
||||
for (const nghttp2_stream_write& write : outgoing_buffers_) {
|
||||
statistics_.data_sent += write.buf.len;
|
||||
if (write.buf.base == nullptr) {
|
||||
bufs[i++] = uv_buf_init(
|
||||
reinterpret_cast<char*>(outgoing_storage_.data() + offset),
|
||||
@ -1642,6 +1644,7 @@ void Http2Session::OnStreamReadImpl(ssize_t nread,
|
||||
if (bufs->len > 0) {
|
||||
// Only pass data on if nread > 0
|
||||
uv_buf_t buf[] { uv_buf_init((*bufs).base, nread) };
|
||||
session->statistics_.data_received += nread;
|
||||
ssize_t ret = session->Write(buf, 1);
|
||||
|
||||
// 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) {
|
||||
Http2Session* session = static_cast<Http2Session*>(user_data);
|
||||
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);
|
||||
CHECK_EQ(id, stream->id());
|
||||
@ -2191,6 +2196,7 @@ ssize_t Http2Stream::Provider::FD::OnRead(nghttp2_session* handle,
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
stream->statistics_.sent_bytes += numchars;
|
||||
return numchars;
|
||||
}
|
||||
|
||||
@ -2216,6 +2222,8 @@ ssize_t Http2Stream::Provider::Stream::OnRead(nghttp2_session* handle,
|
||||
Http2Session* session = static_cast<Http2Session*>(user_data);
|
||||
DEBUG_HTTP2SESSION2(session, "reading outbound data for stream %d", id);
|
||||
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());
|
||||
|
||||
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())
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
stream->statistics_.sent_bytes += amount;
|
||||
return amount;
|
||||
}
|
||||
|
||||
@ -2862,6 +2872,10 @@ void Initialize(Local<Object> target,
|
||||
"settingsBuffer", state->settings_buffer.GetJSArray());
|
||||
SET_STATE_TYPEDARRAY(
|
||||
"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
|
||||
|
||||
env->set_http2_state(std::move(state));
|
||||
|
@ -716,7 +716,10 @@ class Http2Stream : public AsyncWrap,
|
||||
uint64_t start_time;
|
||||
uint64_t end_time;
|
||||
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_ = {};
|
||||
@ -949,8 +952,12 @@ class Http2Session : public AsyncWrap {
|
||||
uint64_t start_time;
|
||||
uint64_t end_time;
|
||||
uint64_t ping_rtt;
|
||||
uint64_t data_sent;
|
||||
uint64_t data_received;
|
||||
uint32_t frame_count;
|
||||
uint32_t frame_sent;
|
||||
int32_t stream_count;
|
||||
size_t max_concurrent_streams;
|
||||
double stream_average_duration;
|
||||
};
|
||||
|
||||
@ -995,6 +1002,10 @@ class Http2Session : public AsyncWrap {
|
||||
const nghttp2_frame* frame,
|
||||
int error_code,
|
||||
void* user_data);
|
||||
static inline int OnFrameSent(
|
||||
nghttp2_session* session,
|
||||
const nghttp2_frame* frame,
|
||||
void* user_data);
|
||||
static inline int OnStreamClose(
|
||||
nghttp2_session* session,
|
||||
int32_t id,
|
||||
@ -1115,21 +1126,29 @@ class Http2SessionPerformanceEntry : public PerformanceEntry {
|
||||
Http2SessionPerformanceEntry(
|
||||
Environment* env,
|
||||
const Http2Session::Statistics& stats,
|
||||
const char* kind) :
|
||||
nghttp2_session_type type) :
|
||||
PerformanceEntry(env, "Http2Session", "http2",
|
||||
stats.start_time,
|
||||
stats.end_time),
|
||||
ping_rtt_(stats.ping_rtt),
|
||||
data_sent_(stats.data_sent),
|
||||
data_received_(stats.data_received),
|
||||
frame_count_(stats.frame_count),
|
||||
frame_sent_(stats.frame_sent),
|
||||
stream_count_(stats.stream_count),
|
||||
max_concurrent_streams_(stats.max_concurrent_streams),
|
||||
stream_average_duration_(stats.stream_average_duration),
|
||||
kind_(kind) { }
|
||||
session_type_(type) { }
|
||||
|
||||
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_sent() const { return frame_sent_; }
|
||||
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_; }
|
||||
const char* typeName() const { return kind_; }
|
||||
nghttp2_session_type type() const { return session_type_; }
|
||||
|
||||
void Notify(Local<Value> obj) {
|
||||
PerformanceEntry::Notify(env(), kind(), obj);
|
||||
@ -1137,33 +1156,50 @@ class Http2SessionPerformanceEntry : public PerformanceEntry {
|
||||
|
||||
private:
|
||||
uint64_t ping_rtt_;
|
||||
uint64_t data_sent_;
|
||||
uint64_t data_received_;
|
||||
uint32_t frame_count_;
|
||||
uint32_t frame_sent_;
|
||||
int32_t stream_count_;
|
||||
size_t max_concurrent_streams_;
|
||||
double stream_average_duration_;
|
||||
const char* kind_;
|
||||
nghttp2_session_type session_type_;
|
||||
};
|
||||
|
||||
class Http2StreamPerformanceEntry : public PerformanceEntry {
|
||||
public:
|
||||
Http2StreamPerformanceEntry(
|
||||
Environment* env,
|
||||
int32_t id,
|
||||
const Http2Stream::Statistics& stats) :
|
||||
PerformanceEntry(env, "Http2Stream", "http2",
|
||||
stats.start_time,
|
||||
stats.end_time),
|
||||
id_(id),
|
||||
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_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) {
|
||||
PerformanceEntry::Notify(env(), kind(), obj);
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t id_;
|
||||
uint64_t first_header_;
|
||||
uint64_t first_byte_;
|
||||
uint64_t first_byte_sent_;
|
||||
uint64_t sent_bytes_;
|
||||
uint64_t received_bytes_;
|
||||
};
|
||||
|
||||
class Http2Session::Http2Ping : public AsyncWrap {
|
||||
|
@ -61,6 +61,29 @@ namespace http2 {
|
||||
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 {
|
||||
public:
|
||||
explicit http2_state(v8::Isolate* isolate) :
|
||||
@ -77,6 +100,16 @@ class http2_state {
|
||||
offsetof(http2_state_internal, stream_state_buffer),
|
||||
IDX_STREAM_STATE_COUNT,
|
||||
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(
|
||||
isolate,
|
||||
offsetof(http2_state_internal, padding_buffer),
|
||||
@ -97,6 +130,8 @@ class http2_state {
|
||||
AliasedBuffer<uint8_t, v8::Uint8Array> root_buffer;
|
||||
AliasedBuffer<double, v8::Float64Array> session_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> options_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
|
||||
double session_state_buffer[IDX_SESSION_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 options_buffer[IDX_OPTIONS_FLAGS + 1];
|
||||
uint32_t settings_buffer[IDX_SETTINGS_COUNT + 1];
|
||||
|
@ -8,7 +8,7 @@ const h2 = require('http2');
|
||||
|
||||
const { PerformanceObserver } = require('perf_hooks');
|
||||
|
||||
const obs = new PerformanceObserver((items) => {
|
||||
const obs = new PerformanceObserver(common.mustCall((items) => {
|
||||
const entry = items.getEntries()[0];
|
||||
assert.strictEqual(entry.entryType, 'http2');
|
||||
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.streamCount, '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) {
|
||||
case 'server':
|
||||
assert.strictEqual(entry.streamCount, 1);
|
||||
@ -34,12 +38,15 @@ const obs = new PerformanceObserver((items) => {
|
||||
break;
|
||||
case 'Http2Stream':
|
||||
assert.strictEqual(typeof entry.timeToFirstByte, 'number');
|
||||
assert.strictEqual(typeof entry.timeToFirstByteSent, 'number');
|
||||
assert.strictEqual(typeof entry.timeToFirstHeader, 'number');
|
||||
assert.strictEqual(typeof entry.bytesWritten, 'number');
|
||||
assert.strictEqual(typeof entry.bytesRead, 'number');
|
||||
break;
|
||||
default:
|
||||
assert.fail('invalid entry name');
|
||||
}
|
||||
});
|
||||
}, 4));
|
||||
obs.observe({ entryTypes: ['http2'] });
|
||||
|
||||
const body =
|
||||
|
Loading…
x
Reference in New Issue
Block a user