Skip to content

Commit

Permalink
Merge pull request #553 from h2o/kazuho/trace2
Browse files Browse the repository at this point in the history
sampling-based tracing through Unix sockets
  • Loading branch information
kazuho authored Dec 17, 2024
2 parents d13ff3b + a125f00 commit 402544b
Show file tree
Hide file tree
Showing 3 changed files with 538 additions and 91 deletions.
208 changes: 161 additions & 47 deletions include/picotls.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ extern "C" {
#include <inttypes.h>
#include <string.h>
#include <sys/types.h>
#ifndef _WINDOWS
#include <netinet/in.h>
#include <arpa/inet.h>
#endif

#if __GNUC__ >= 3
#define PTLS_LIKELY(x) __builtin_expect(!!(x), 1)
Expand Down Expand Up @@ -64,8 +68,15 @@ extern "C" {
#define PTLS_THREADLOCAL __declspec(thread)
#else
#define PTLS_THREADLOCAL __thread
#endif

#ifndef PTLS_HAVE_LOG
#ifdef _WINDOWS
#define PTLS_HAVE_LOG 0
#else
#define PTLS_HAVE_LOG 1
#endif
#endif

#ifndef PTLS_FUZZ_HANDSHAKE
#define PTLS_FUZZ_HANDSHAKE 0
Expand Down Expand Up @@ -1386,35 +1397,44 @@ uint64_t ptls_decode_quicint(const uint8_t **src, const uint8_t *end);
ptls_decode_assert_block_close((src), end); \
} while (0)

#define PTLS_LOG__DO_LOG(module, type, block) \
#define PTLS_LOG__DO_LOG(module, name, conn_state, get_sni, get_sni_arg, add_time, block) \
do { \
int ptlslog_skip = 0; \
char smallbuf[128]; \
ptls_buffer_t ptlslogbuf; \
ptls_buffer_init(&ptlslogbuf, smallbuf, sizeof(smallbuf)); \
PTLS_LOG__DO_PUSH_SAFESTR("{\"module\":\"" PTLS_TO_STR(module) "\",\"type\":\"" PTLS_TO_STR(type) "\""); \
int ptlslog_skip = 0, ptlslog_include_appdata = 0; \
do { \
block \
} while (0); \
PTLS_LOG__DO_PUSH_SAFESTR("}\n"); \
if (!ptlslog_skip) \
ptls_log__do_write(&ptlslogbuf); \
ptls_buffer_dispose(&ptlslogbuf); \
char smallbuf[128]; \
ptls_buffer_t ptlslogbuf; \
ptls_log__do_write_start(&logpoint, &ptlslogbuf, smallbuf, sizeof(smallbuf), (add_time)); \
do { \
block \
} while (0); \
ptlslog_include_appdata = ptls_log__do_write_end(ptlslog_skip ? NULL : &logpoint, (conn_state), (get_sni), \
(get_sni_arg), &ptlslogbuf, ptlslog_include_appdata); \
} while (ptlslog_include_appdata); \
} while (0)

#define PTLS_LOG(module, type, block) \
#define PTLS_LOG_DEFINE_POINT(_module, _name, _var) \
static struct st_ptls_log_point_t _var = {.name = PTLS_TO_STR(_module) ":" PTLS_TO_STR(_name)}

#define PTLS_LOG(module, name, block) \
do { \
if (!ptls_log.is_active) \
PTLS_LOG_DEFINE_POINT(module, name, logpoint); \
if (ptls_log_point_maybe_active(&logpoint) == 0) \
break; \
PTLS_LOG__DO_LOG((module), (type), (block)); \
PTLS_LOG__DO_LOG(module, name, NULL, NULL, NULL, 1, {block}); \
} while (0)

#define PTLS_LOG_CONN(type, tls, block) \
#define PTLS_LOG_CONN(name, tls, block) \
do { \
PTLS_LOG_DEFINE_POINT(picotls, name, logpoint); \
uint32_t active = ptls_log_point_maybe_active(&logpoint); \
if (active == 0) \
break; \
ptls_t *_tls = (tls); \
if (!ptls_log.is_active || ptls_skip_tracing(_tls)) \
ptls_log_conn_state_t *conn_state = ptls_get_log_state(_tls); \
active &= ptls_log_conn_maybe_active(conn_state, (const char *(*)(void *))ptls_get_server_name, _tls); \
if (active == 0) \
break; \
PTLS_LOG__DO_LOG(picotls, type, { \
PTLS_LOG__DO_LOG(picotls, name, conn_state, (const char *(*)(void *))ptls_get_server_name, _tls, 1, { \
PTLS_LOG_ELEMENT_PTR(tls, _tls); \
do { \
block \
Expand Down Expand Up @@ -1461,14 +1481,14 @@ uint64_t ptls_decode_quicint(const uint8_t **src, const uint8_t *end);
#define PTLS_LOG_APPDATA_ELEMENT_UNSAFESTR(name, value, value_len) \
do { \
size_t _len = (value_len); \
if (ptls_log.include_appdata) \
if (ptlslog_include_appdata) \
PTLS_LOG_ELEMENT_UNSAFESTR(name, value, _len); \
PTLS_LOG_ELEMENT__DO_UNSIGNED(name, "_len", _len); \
} while (0)
#define PTLS_LOG_APPDATA_ELEMENT_HEXDUMP(name, value, value_len) \
do { \
size_t _len = (value_len); \
if (ptls_log.include_appdata) \
if (ptlslog_include_appdata) \
PTLS_LOG_ELEMENT_HEXDUMP(name, value, _len); \
PTLS_LOG_ELEMENT__DO_UNSIGNED(name, "_len", _len); \
} while (0)
Expand Down Expand Up @@ -1514,27 +1534,100 @@ uint64_t ptls_decode_quicint(const uint8_t **src, const uint8_t *end);
} while (0)

/**
* User API is exposed only when logging is supported by the platform.
* retains a list of connections that are bound to the object
*/
struct st_ptls_log_state_t {
/**
* bit array of connections (1 is active)
*/
uint32_t active_conns;
/**
* generation counter used for staleness check; see `ptls_log._generation`
*/
uint64_t generation;
};

/**
* represents a log point identified by name (`module:type`)
*/
struct st_ptls_log_point_t {
const char *name;
struct st_ptls_log_state_t state;
};

/**
* represents a logging state of each connection
*/
typedef struct st_ptls_log_conn_state_t {
/**
* random value between 0 (inclusive) and 1 (non-inclusive) used to determine the ratio of sampling-based logging; see
* `ptls_add_fd'`. To disable logging entirely, use `ptls_log.dummy_conn_state`, or set the value exactly to 1.
*/
float random_;
/**
* represents peer address; ipv4 addresses are stored using the mapped form (::ffff:192.0.2.1)
*/
struct in6_addr address;
struct st_ptls_log_state_t state;
} ptls_log_conn_state_t;

/**
* see `ptls_get_log_state`
*/
extern PTLS_THREADLOCAL ptls_log_conn_state_t *ptls_log_conn_state_override;

/**
* global variables exposed
*/
extern struct st_ptls_log_t {
/**
* if application-data (e.g., payload) should be emitted as well
*/
volatile unsigned may_include_appdata : 1;
/**
* endpoints that want to disable logging entirely can provide this value to the loggers
*/
ptls_log_conn_state_t dummy_conn_state;
/**
* generation counter that is incremented whenever the state of loggers change; see `st_ptls_log_state_t::generation`
*/
volatile uint64_t _generation;
} ptls_log;

/**
* initializes a ptls_log_conn_state_t
*/
void ptls_log_init_conn_state(ptls_log_conn_state_t *state, void (*random_bytes)(void *, size_t));
/**
* forces recalculation of the log state (should be called when SNI is determined)
*/
typedef struct st_ptls_log_t {
unsigned is_active : 1;
unsigned include_appdata : 1;
} ptls_log_t;
static void ptls_log_recalc_conn_state(ptls_log_conn_state_t *state);
/**
* returns a bitmap indicating the loggers active for given log point
*/
static uint32_t ptls_log_point_maybe_active(struct st_ptls_log_point_t *point);
/**
* returns a bitmap indicating the loggers active for given connection
*/
static uint32_t ptls_log_conn_maybe_active(ptls_log_conn_state_t *conn, const char *(*get_sni)(void *), void *get_sni_arg);

#if PTLS_HAVE_LOG
extern volatile ptls_log_t ptls_log;
/**
* Returns the number of log events that were unable to be emitted.
*/
size_t ptls_log_num_lost(void);
/**
* Registers an fd to the logger. A registered fd is automatically closed and removed if it is invalidated.
* Registers an fd to the logger. A registered fd is automatically closed and removed when it is closed by the peer.
* @param sample_ratio sampling ratio between 0 and 1
* @param points list of points to log, in the form of p1\0p2\0\0 (i.e., concatenated list of C strings with an empty string
* marking the end). An empty list means attach to all.
* @param snis list of SNIs to log, using the same form as points
* @param addresses list of IPv4/v6 addresses to log, using the same form as points
*/
int ptls_log_add_fd(int fd);
#else
static const ptls_log_t ptls_log = {0};
#endif
int ptls_log_add_fd(int fd, float sample_ratio, const char *points, const char *snis, const char *addresses, int appdata);

void ptls_log__recalc_point(int caller_locked, struct st_ptls_log_point_t *point);
void ptls_log__recalc_conn(int caller_locked, struct st_ptls_log_conn_state_t *conn, const char *(*get_sni)(void *),
void *get_sni_arg);
static int ptls_log__do_push_safestr(ptls_buffer_t *buf, const char *s);
int ptls_log__do_push_unsafestr(ptls_buffer_t *buf, const char *s, size_t l);
int ptls_log__do_push_hexdump(ptls_buffer_t *buf, const void *s, size_t l);
Expand All @@ -1543,7 +1636,10 @@ int ptls_log__do_push_signed32(ptls_buffer_t *buf, int32_t v);
int ptls_log__do_push_signed64(ptls_buffer_t *buf, int64_t v);
int ptls_log__do_push_unsigned32(ptls_buffer_t *buf, uint32_t v);
int ptls_log__do_push_unsigned64(ptls_buffer_t *buf, uint64_t v);
void ptls_log__do_write(const ptls_buffer_t *buf);
void ptls_log__do_write_start(struct st_ptls_log_point_t *point, ptls_buffer_t *buf, void *smallbuf, size_t smallbufsize,
int add_time);
int ptls_log__do_write_end(struct st_ptls_log_point_t *point, struct st_ptls_log_conn_state_t *conn, const char *(*get_sni)(void *),
void *get_sni_arg, ptls_buffer_t *buf, int includes_appdata);

/**
* create a client object to handle new TLS connection
Expand Down Expand Up @@ -1648,13 +1744,11 @@ int ptls_is_ech_handshake(ptls_t *tls, uint8_t *config_id, ptls_hpke_kem_t **kem
*/
void **ptls_get_data_ptr(ptls_t *tls);
/**
*
* Returns `ptls_log_conn_state_t` of `ptls_t`. By default, the state is initialized by calling `ptls_log_init_conn_state`, but the
* behavior can be overidden by setting `ptls_log_conn_state_override`.
* This value can be changed by setting `ptls_log_random_override` or by calling `ptls_set_log_random`.
*/
int ptls_skip_tracing(ptls_t *tls);
/**
*
*/
void ptls_set_skip_tracing(ptls_t *tls, int skip_tracing);
ptls_log_conn_state_t *ptls_get_log_state(ptls_t *tls);
/**
* proceeds with the handshake, optionally taking some input from peer. The function returns zero in case the handshake completed
* successfully. PTLS_ERROR_IN_PROGRESS is returned in case the handshake is incomplete. Otherwise, an error value is returned. The
Expand Down Expand Up @@ -1890,6 +1984,11 @@ char *ptls_hexdump(char *dst, const void *src, size_t len);
* Builds a JSON-safe string without double quotes. Supplied buffer MUST be at least 6x + 1 bytes larger than the input.
*/
char *ptls_jsonescape(char *buf, const char *s, size_t len);
/**
* builds a v4-mapped address (i.e., ::ffff:192.0.2.1)
*/
void ptls_build_v4_mapped_v6_address(struct in6_addr *v6, const struct in_addr *v4);

/**
* the default get_time callback
*/
Expand All @@ -1898,17 +1997,32 @@ extern ptls_get_time_t ptls_get_time;
* default hash clone function that calls memcpy
*/
static void ptls_hash_clone_memcpy(void *dst, const void *src, size_t size);
#if defined(PICOTLS_USE_DTRACE) && PICOTLS_USE_DTRACE
/**
*
*/
extern PTLS_THREADLOCAL unsigned ptls_default_skip_tracing;
#else
#define ptls_default_skip_tracing 0
#endif

/* inline functions */

inline uint32_t ptls_log_point_maybe_active(struct st_ptls_log_point_t *point)
{
if (!PTLS_HAVE_LOG)
return 0;
if (PTLS_UNLIKELY(point->state.generation != ptls_log._generation))
ptls_log__recalc_point(0, point);
return point->state.active_conns;
}

inline void ptls_log_recalc_conn_state(ptls_log_conn_state_t *state)
{
state->state.generation = 0;
}

inline uint32_t ptls_log_conn_maybe_active(ptls_log_conn_state_t *conn, const char *(*get_sni)(void *), void *get_sni_arg)
{
if (!PTLS_HAVE_LOG)
return 0;
if (PTLS_UNLIKELY(conn->state.generation != ptls_log._generation))
ptls_log__recalc_conn(0, conn, get_sni, get_sni_arg);
return conn->state.active_conns;
}

inline int ptls_log__do_push_safestr(ptls_buffer_t *buf, const char *s)
{
return ptls_log__do_pushv(buf, s, strlen(s));
Expand Down
Loading

0 comments on commit 402544b

Please sign in to comment.