Skip to content
This repository has been archived by the owner on Aug 11, 2020. It is now read-only.

quic: support memory tracking #145

Closed
wants to merge 2 commits into from
Closed
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
4 changes: 2 additions & 2 deletions src/memory_tracker-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@ void MemoryTracker::TrackField(const char* edge_name,
}
}

template <typename T>
template <typename T, typename D>
void MemoryTracker::TrackField(const char* edge_name,
const std::unique_ptr<T>& value,
const std::unique_ptr<T, D>& value,
const char* node_name) {
if (value.get() == nullptr) {
return;
Expand Down
4 changes: 2 additions & 2 deletions src/memory_tracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ class MemoryTracker {
size_t size,
const char* node_name = nullptr);
// Shortcut to extract the underlying object out of the smart pointer
template <typename T>
template <typename T, typename D>
inline void TrackField(const char* edge_name,
const std::unique_ptr<T>& value,
const std::unique_ptr<T, D>& value,
const char* node_name = nullptr);
template <typename T, bool kIsWeak>
void TrackField(const char* edge_name,
Expand Down
28 changes: 28 additions & 0 deletions src/node_quic_session.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1883,6 +1883,24 @@ bool QuicSession::UpdateKey() {
&crypto_ctx_);
}

void QuicSession::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackField("alpn", alpn_);
tracker->TrackField("idle", idle_);
tracker->TrackField("retransmit", retransmit_);
tracker->TrackField("rx_secret", rx_secret_);
tracker->TrackField("tx_secret", tx_secret_);
tracker->TrackField("sendbuf", sendbuf_);
tracker->TrackField("handshake", handshake_);
tracker->TrackField("txbuf", txbuf_);
tracker->TrackField("peer_handshake", peer_handshake_);
tracker->TrackField("streams", streams_);
tracker->TrackField("state", state_);
tracker->TrackField("crypto_rx_ack", crypto_rx_ack_);
tracker->TrackField("crypto_handshake_rate", crypto_handshake_rate_);
tracker->TrackField("stats_buffer", stats_buffer_);
tracker->TrackField("recovery_stats_buffer", recovery_stats_buffer_);
tracker->TrackFieldWithSize("current_ngtcp2_memory", current_ngtcp2_memory_);
}

// QuicServerSession
QuicServerSession::InitialPacketResult QuicServerSession::Accept(
Expand Down Expand Up @@ -2376,6 +2394,11 @@ int QuicServerSession::VerifyPeerIdentity(const char* hostname) {
return VerifyPeerCertificate(ssl());
}

void QuicServerSession::MemoryInfo(MemoryTracker* tracker) const {
QuicSession::MemoryInfo(tracker);
tracker->TrackField("conn_closebuf", conn_closebuf_);
tracker->TrackField("ocsp_response", ocsp_response_);
}

// QuicClientSession

Expand Down Expand Up @@ -2888,6 +2911,11 @@ int QuicClientSession::VerifyPeerIdentity(const char* hostname) {
return 0;
}

void QuicClientSession::MemoryInfo(MemoryTracker* tracker) const {
QuicSession::MemoryInfo(tracker);
tracker->TrackField("hostname", hostname_);
}

// Static ngtcp2 callbacks are registered when ngtcp2 when a new ngtcp2_conn is
// created. These are static functions that, for the most part, simply defer to
// a QuicSession instance that is passed through as user_data.
Expand Down
8 changes: 6 additions & 2 deletions src/node_quic_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,8 @@ class QuicSession : public AsyncWrap,
QuicSession* session_;
};

void MemoryInfo(MemoryTracker* tracker) const override;

private:
// Returns true if the QuicSession has entered the
// closing period following a call to ImmediateClose.
Expand Down Expand Up @@ -1063,7 +1065,8 @@ class QuicServerSession : public QuicSession {
const ngtcp2_cid* rcid() const { return &rcid_; }
ngtcp2_cid* pscid() { return &pscid_; }

void MemoryInfo(MemoryTracker* tracker) const override {}
void MemoryInfo(MemoryTracker* tracker) const override;

SET_MEMORY_INFO_NAME(QuicServerSession)
SET_SELF_SIZE(QuicServerSession)

Expand Down Expand Up @@ -1168,7 +1171,8 @@ class QuicClientSession : public QuicSession {

bool SendConnectionClose() override;

void MemoryInfo(MemoryTracker* tracker) const override {}
void MemoryInfo(MemoryTracker* tracker) const override;

SET_MEMORY_INFO_NAME(QuicClientSession)
SET_SELF_SIZE(QuicClientSession)

Expand Down
8 changes: 7 additions & 1 deletion src/node_quic_socket.cc
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,13 @@ QuicSocket::~QuicSocket() {
}

void QuicSocket::MemoryInfo(MemoryTracker* tracker) const {
// TODO(@jasnell): Implement memory tracking information
tracker->TrackField("sessions", sessions_);
tracker->TrackField("dcid_to_scid", dcid_to_scid_);
tracker->TrackFieldWithSize("addr_counts",
addr_counts_.size() * (sizeof(sockaddr*) + sizeof(size_t)));
tracker->TrackField("validated_addrs", validated_addrs_);
tracker->TrackField("stats_buffer", stats_buffer_);
tracker->TrackFieldWithSize("current_ngtcp2_memory", current_ngtcp2_memory_);
}

void QuicSocket::AddSession(
Expand Down
8 changes: 8 additions & 0 deletions src/node_quic_stream.cc
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,14 @@ BaseObjectPtr<QuicStream> QuicStream::New(
return stream;
}

void QuicStream::MemoryInfo(MemoryTracker* tracker) const {
tracker->TrackField("buffer", &streambuf_);
tracker->TrackField("data_rx_rate", data_rx_rate_);
tracker->TrackField("data_rx_size", data_rx_size_);
tracker->TrackField("data_rx_ack", data_rx_ack_);
tracker->TrackField("stats_buffer", stats_buffer_);
}

// JavaScript API
namespace {
void QuicStreamGetID(const FunctionCallbackInfo<Value>& args) {
Expand Down
4 changes: 1 addition & 3 deletions src/node_quic_stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,7 @@ class QuicStream : public AsyncWrap, public StreamBase {

AsyncWrap* GetAsyncWrap() override { return this; }

void MemoryInfo(MemoryTracker* tracker) const override {
tracker->TrackField("buffer", &streambuf_);
}
void MemoryInfo(MemoryTracker* tracker) const override;

SET_MEMORY_INFO_NAME(QuicStream)
SET_SELF_SIZE(QuicStream)
Expand Down
8 changes: 6 additions & 2 deletions src/node_quic_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ void IncrementStat(
// Simple timer wrapper that is used to implement the internals
// for idle and retransmission timeouts. Call Update to start or
// reset the timer; Stop to halt the timer.
class Timer {
class Timer final : public MemoryRetainer {
public:
explicit Timer(Environment* env, std::function<void()> fn)
: stopped_(false),
Expand All @@ -395,7 +395,7 @@ class Timer {
env->AddCleanupHook(CleanupHook, this);
}

~Timer() {
~Timer() override {
env_->RemoveCleanupHook(CleanupHook, this);
}

Expand Down Expand Up @@ -423,6 +423,10 @@ class Timer {

static void Free(Timer* timer);

SET_NO_MEMORY_INFO()
SET_MEMORY_INFO_NAME(Timer)
SET_SELF_SIZE(Timer)

private:
static void OnTimeout(uv_timer_t* timer);
static void CleanupHook(void* data);
Expand Down
144 changes: 144 additions & 0 deletions test/pummel/test-heapdump-quic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Flags: --expose-internals
'use strict';
const common = require('../common');
if (!common.hasQuic)
common.skip('missing quic');

const quic = require('quic');

const { recordState } = require('../common/heap');
const fixtures = require('../common/fixtures');
const key = fixtures.readKey('agent1-key.pem', 'binary');
const cert = fixtures.readKey('agent1-cert.pem', 'binary');
const ca = fixtures.readKey('ca1-cert.pem', 'binary');

{
const state = recordState();
state.validateSnapshotNodes('Node / QuicStream', []);
state.validateSnapshotNodes('Node / QuicSession', []);
state.validateSnapshotNodes('Node / QuicSocket', []);
}

const server = quic.createSocket({ port: 0, validateAddress: true });

server.listen({
key,
cert,
ca,
rejectUnauthorized: false,
maxCryptoBuffer: 4096,
alpn: 'meow'
});

server.on('session', common.mustCall((session) => {
session.on('secure', common.mustCall((servername, alpn, cipher) => {
// eslint-disable-next-line no-unused-vars
const stream = session.openStream({ halfOpen: false });

const state = recordState();

state.validateSnapshotNodes('Node / QuicSocket', [
{
children: [
{ node_name: 'QuicSocket', edge_name: 'wrapped' },
{ node_name: 'BigUint64Array', edge_name: 'stats_buffer' },
{ node_name: 'Node / sessions', edge_name: 'sessions' },
{ node_name: 'Node / dcid_to_scid', edge_name: 'dcid_to_scid' },
]
}
], { loose: true });

state.validateSnapshotNodes('Node / QuicStream', [
{
children: [
{ node_name: 'QuicStream', edge_name: 'wrapped' },
{ node_name: 'BigUint64Array', edge_name: 'stats_buffer' },
{ node_name: 'Node / QuicBuffer', edge_name: 'buffer' },
{ node_name: 'Node / HistogramBase', edge_name: 'data_rx_rate' },
{ node_name: 'Node / HistogramBase', edge_name: 'data_rx_size' },
{ node_name: 'Node / HistogramBase', edge_name: 'data_rx_ack' }
]
}
], { loose: true });

state.validateSnapshotNodes('Node / QuicBuffer', [
{
children: [
{ node_name: 'Node / length', edge_name: 'length' }
]
}
], { loose: true });

state.validateSnapshotNodes('Node / QuicServerSession', [
{
children: [
{ node_name: 'QuicServerSession', edge_name: 'wrapped' },
{ node_name: 'Node / rx_secret', edge_name: 'rx_secret' },
{ node_name: 'Node / tx_secret', edge_name: 'tx_secret' },
{ node_name: 'Node / HistogramBase', edge_name: 'crypto_rx_ack' },
{ node_name: 'Node / HistogramBase',
edge_name: 'crypto_handshake_rate' },
{ node_name: 'Node / Timer', edge_name: 'retransmit' },
{ node_name: 'Node / Timer', edge_name: 'idle' },
{ node_name: 'Node / QuicBuffer', edge_name: 'sendbuf' },
{ node_name: 'Node / QuicBuffer', edge_name: 'txbuf' },
{ node_name: 'Node / peer_handshake', edge_name: 'peer_handshake' },
{ node_name: 'Float64Array', edge_name: 'recovery_stats_buffer' },
{ node_name: 'BigUint64Array', edge_name: 'stats_buffer' },
{ node_name: 'Node / current_ngtcp2_memory',
edge_name: 'current_ngtcp2_memory' },
{ node_name: 'Node / streams', edge_name: 'streams' },
{ node_name: 'Node / QuicBuffer', edge_name: 'handshake' },
{ node_name: 'Node / std::basic_string', edge_name: 'alpn' },
{ node_name: 'Float64Array', edge_name: 'state' },
]
}
], { loose: true });

state.validateSnapshotNodes('Node / QuicClientSession', [
{
children: [
{ node_name: 'QuicClientSession', edge_name: 'wrapped' },
{ node_name: 'Node / rx_secret', edge_name: 'rx_secret' },
{ node_name: 'Node / tx_secret', edge_name: 'tx_secret' },
{ node_name: 'Node / HistogramBase', edge_name: 'crypto_rx_ack' },
{ node_name: 'Node / HistogramBase',
edge_name: 'crypto_handshake_rate' },
{ node_name: 'Node / Timer', edge_name: 'retransmit' },
{ node_name: 'Node / Timer', edge_name: 'idle' },
{ node_name: 'Node / QuicBuffer', edge_name: 'sendbuf' },
{ node_name: 'Node / QuicBuffer', edge_name: 'txbuf' },
{ node_name: 'Node / peer_handshake', edge_name: 'peer_handshake' },
{ node_name: 'Float64Array', edge_name: 'recovery_stats_buffer' },
{ node_name: 'BigUint64Array', edge_name: 'stats_buffer' },
{ node_name: 'Node / current_ngtcp2_memory',
edge_name: 'current_ngtcp2_memory' },
{ node_name: 'Node / QuicBuffer', edge_name: 'handshake' },
{ node_name: 'Node / std::basic_string', edge_name: 'alpn' },
{ node_name: 'Node / std::basic_string', edge_name: 'hostname' },
{ node_name: 'Float64Array', edge_name: 'state' },
]
}
], { loose: true });

session.destroy();
server.close();
}));
}));

server.on('ready', common.mustCall(() => {
const client = quic.createSocket({
port: 0,
client: {
key,
cert,
ca,
alpn: 'meow'
}
});

client.connect({
address: 'localhost',
port: server.address.port
}).on('close', common.mustCall(() => client.close()));
}));