1 /*
2  * Copyright (c) 2014-2016 DeNA Co., Ltd., Kazuho Oku, Fastly, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20  * IN THE SOFTWARE.
21  */
22 #ifndef h2o__probes_h
23 #define h2o__probes_h
24 
25 /* This file is placed under lib, and must only be included from the source files of the h2o / libh2o, because H2O_USE_DTRACE is a
26  * symbol available only during the build phase of h2o.  That's fine, because only h2o / libh2o has the sole right to define probes
27  * belonging to the h2o namespace.
28  */
29 #if H2O_USE_DTRACE
30 
31 #include "picotls.h"
32 /* as probes_.h is used by files under lib/common, structures that are specific to the server-side implementation have to be
33  * forward-declared. */
34 struct st_h2o_conn_t;
35 struct st_h2o_tunnel_t;
36 #include "h2o-probes.h"
37 
38 #define H2O_CONN_IS_PROBED(label, conn) (PTLS_UNLIKELY(H2O_##label##_ENABLED()) && !conn->callbacks->skip_tracing(conn))
39 
40 #define H2O_PROBE_CONN0(label, conn)                                                                                               \
41     do {                                                                                                                           \
42         h2o_conn_t *_conn = (conn);                                                                                                \
43         if (H2O_CONN_IS_PROBED(label, _conn)) {                                                                                    \
44             H2O_##label(_conn->id);                                                                                                \
45         }                                                                                                                          \
46     } while (0)
47 
48 #define H2O_PROBE_CONN(label, conn, ...)                                                                                           \
49     do {                                                                                                                           \
50         h2o_conn_t *_conn = (conn);                                                                                                \
51         if (H2O_CONN_IS_PROBED(label, _conn)) {                                                                                    \
52             H2O_##label(_conn->id, __VA_ARGS__);                                                                                   \
53         }                                                                                                                          \
54     } while (0)
55 
56 #define H2O_PROBE(label, ...)                                                                                                      \
57     do {                                                                                                                           \
58         if (PTLS_UNLIKELY(H2O_##label##_ENABLED())) {                                                                              \
59             H2O_##label(__VA_ARGS__);                                                                                              \
60         }                                                                                                                          \
61     } while (0)
62 
63 #define H2O_PROBE_HEXDUMP(s, l)                                                                                                    \
64     ({                                                                                                                             \
65         size_t _l = (l);                                                                                                           \
66         ptls_hexdump(alloca(_l * 2 + 1), (s), _l);                                                                                 \
67     })
68 
69 #else
70 
71 #define H2O_CONN_IS_PROBED(label, conn) (0)
72 #define H2O_PROBE_CONN0(label, conn)
73 #define H2O_PROBE_CONN(label, conn, ...)
74 #define H2O_PROBE(label, ...)
75 #define H2O_PROBE_HEXDUMP(s, l)
76 
77 #endif
78 
79 /* Helper functions for probing; the functions are defined as non-inlineable, as bcc cannot handle relative offset against a static
80  * const (e.g., H2O_TOKEN_PATH->buf.base). They are available only when h2o.h is included, so that files under lib/common can
81  * include this function without creating dependency against lib/core (e.g., `h2o_req_t`). */
82 #ifdef h2o_h
83 
h2o_probe_request_header(h2o_req_t * req,uint64_t req_index,h2o_iovec_t name,h2o_iovec_t value)84 __attribute__((noinline)) static void h2o_probe_request_header(h2o_req_t *req, uint64_t req_index, h2o_iovec_t name,
85                                                                h2o_iovec_t value)
86 {
87     H2O_PROBE_CONN(RECEIVE_REQUEST_HEADER, req->conn, req_index, name.base, name.len, value.base, value.len);
88 }
89 
h2o_probe_response_header(h2o_req_t * req,uint64_t req_index,h2o_iovec_t name,h2o_iovec_t value)90 __attribute__((noinline)) static void h2o_probe_response_header(h2o_req_t *req, uint64_t req_index, h2o_iovec_t name,
91                                                                 h2o_iovec_t value)
92 {
93     H2O_PROBE_CONN(SEND_RESPONSE_HEADER, req->conn, req_index, name.base, name.len, value.base, value.len);
94 }
95 
h2o_probe_log_request(h2o_req_t * req,uint64_t req_index)96 static inline void h2o_probe_log_request(h2o_req_t *req, uint64_t req_index)
97 {
98     H2O_PROBE_CONN(RECEIVE_REQUEST, req->conn, req_index, req->version);
99     if (H2O_CONN_IS_PROBED(RECEIVE_REQUEST_HEADER, req->conn)) {
100         if (req->input.authority.base != NULL)
101             h2o_probe_request_header(req, req_index, H2O_TOKEN_AUTHORITY->buf, req->input.authority);
102         if (req->input.method.base != NULL)
103             h2o_probe_request_header(req, req_index, H2O_TOKEN_METHOD->buf, req->input.method);
104         if (req->input.path.base != NULL)
105             h2o_probe_request_header(req, req_index, H2O_TOKEN_PATH->buf, req->input.path);
106         if (req->input.scheme != NULL)
107             h2o_probe_request_header(req, req_index, H2O_TOKEN_SCHEME->buf, req->input.scheme->name);
108         size_t i;
109         for (i = 0; i != req->headers.size; ++i) {
110             h2o_header_t *h = req->headers.entries + i;
111             h2o_probe_request_header(req, req_index, *h->name, h->value);
112         }
113     }
114 }
115 
h2o_probe_log_response(h2o_req_t * req,uint64_t req_index)116 static inline void h2o_probe_log_response(h2o_req_t *req, uint64_t req_index)
117 {
118     H2O_PROBE_CONN(SEND_RESPONSE, req->conn, req_index, req->res.status);
119     if (H2O_CONN_IS_PROBED(SEND_RESPONSE_HEADER, req->conn)) {
120         if (req->res.content_length != SIZE_MAX) {
121             char buf[sizeof(H2O_SIZE_T_LONGEST_STR)];
122             size_t len = (size_t)sprintf(buf, "%zu", req->res.content_length);
123             h2o_probe_response_header(req, req_index, H2O_TOKEN_CONTENT_LENGTH->buf, h2o_iovec_init(buf, len));
124         }
125         size_t i;
126         for (i = 0; i != req->res.headers.size; ++i) {
127             h2o_header_t *h = req->res.headers.entries + i;
128             h2o_probe_response_header(req, req_index, *h->name, h->value);
129         }
130     }
131 }
132 
133 #endif
134 
135 #endif
136