-
Notifications
You must be signed in to change notification settings - Fork 1
/
patch.diff
227 lines (221 loc) · 7.02 KB
/
patch.diff
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
diff --git a/go-src/src/net/http/h2_bundle.go b/go-src/src/net/http/h2_bundle.go
index 1e0b83d493..3756355b96 100644
--- a/go-src/src/net/http/h2_bundle.go
+++ b/go-src/src/net/http/h2_bundle.go
@@ -6045,7 +6045,9 @@ func (sc *http2serverConn) runHandler(rw *http2responseWriter, req *Request, han
return
}
rw.handlerDone()
+ tracingHandlerEnd(didPanic)
}()
+ tracingHandlerStart(req)
handler(rw, req)
didPanic = false
}
diff --git a/go-src/src/net/http/server.go b/go-src/src/net/http/server.go
index c3c3f91d9a..0ed21233dc 100644
--- a/go-src/src/net/http/server.go
+++ b/go-src/src/net/http/server.go
@@ -13,6 +13,7 @@ import (
"crypto/tls"
"errors"
"fmt"
+ "golang.org/x/net/http/httpguts"
"internal/godebug"
"io"
"log"
@@ -29,8 +30,6 @@ import (
"sync"
"sync/atomic"
"time"
-
- "golang.org/x/net/http/httpguts"
)
// Errors used by the HTTP server.
@@ -1847,12 +1846,19 @@ func (c *conn) serve(ctx context.Context) {
c.remoteAddr = c.rwc.RemoteAddr().String()
ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
var inFlightResponse *response
+ var didPanic bool
defer func() {
if err := recover(); err != nil && err != ErrAbortHandler {
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
+
+ // If we're here, then we panicked from inside the handler.
+ // and need to trigger the end of the tracing
+ if didPanic {
+ tracingHandlerEnd(true)
+ }
}
if inFlightResponse != nil {
inFlightResponse.cancelCtx()
@@ -1992,13 +1998,23 @@ func (c *conn) serve(ctx context.Context) {
// But we're not going to implement HTTP pipelining because it
// was never deployed in the wild and the answer is HTTP/2.
inFlightResponse = w
+
+ // Trace the start of the request
+ tracingHandlerStart(w.req)
+ didPanic = true
serverHandler{c.server}.ServeHTTP(w, w.req)
+ didPanic = false
+
inFlightResponse = nil
w.cancelCtx()
if c.hijacked() {
return
}
w.finishRequest()
+
+ // Trace the end of the request
+ tracingHandlerEnd(false)
+
c.rwc.SetWriteDeadline(time.Time{})
if !w.shouldReuseConnection() {
if w.requestBodyLimitHit || w.closedRequestBodyEarly() {
diff --git a/go-src/src/net/http/tracing.go b/go-src/src/net/http/tracing.go
index d02cfda642..97b482fa3b 100644
--- a/go-src/src/net/http/tracing.go
+++ b/go-src/src/net/http/tracing.go
@@ -1 +1,20 @@
package http
+
+import (
+ _ "unsafe"
+)
+
+// tracingHandlerStart is called when a HTTP request starts.
+func tracingHandlerStart(req *Request)
+
+// tracingHandlerEnd is called when a HTTP request ends.
+//
+// If the handler panicked, didPanic will be true
+// otherwise it will be false.
+func tracingHandlerEnd(didPanic bool)
+
+// tracingStartRoundTrip is called when a HTTP request starts.
+func tracingStartRoundTrip(req *Request)
+
+// tracingEndRoundTrip is called when a HTTP request ends.
+func tracingEndRoundTrip(resp *Response, err error)
diff --git a/go-src/src/net/http/transport.go b/go-src/src/net/http/transport.go
index ddcb64815c..da5d3b6e89 100644
--- a/go-src/src/net/http/transport.go
+++ b/go-src/src/net/http/transport.go
@@ -17,6 +17,8 @@ import (
"crypto/tls"
"errors"
"fmt"
+ "golang.org/x/net/http/httpguts"
+ "golang.org/x/net/http/httpproxy"
"internal/godebug"
"io"
"log"
@@ -30,9 +32,6 @@ import (
"sync"
"sync/atomic"
"time"
-
- "golang.org/x/net/http/httpguts"
- "golang.org/x/net/http/httpproxy"
)
// DefaultTransport is the default implementation of Transport and is
@@ -509,7 +508,12 @@ func (t *Transport) alternateRoundTripper(req *Request) RoundTripper {
}
// roundTrip implements a RoundTripper over HTTP.
-func (t *Transport) roundTrip(req *Request) (*Response, error) {
+func (t *Transport) roundTrip(req *Request) (returnResp *Response, returnErr error) {
+ tracingStartRoundTrip(req)
+ defer func() {
+ tracingEndRoundTrip(returnResp, returnErr)
+ }()
+
t.nextProtoOnce.Do(t.onceSetNextProtoDefaults)
ctx := req.Context()
trace := httptrace.ContextClientTrace(ctx)
diff --git a/go-src/src/runtime/proc.go b/go-src/src/runtime/proc.go
index 554a60d747..bea66d54a5 100644
--- a/go-src/src/runtime/proc.go
+++ b/go-src/src/runtime/proc.go
@@ -3628,6 +3628,12 @@ func goexit0(gp *g) {
mp := getg().m
pp := mp.p.ptr()
+ // If we have trace data, then we need to call the exit hook.
+ // to let our library know it's exiting
+ if traceData := gp.traceData; traceData != nil {
+ tracingGExit(gp.goid, traceData)
+ }
+
casgstatus(gp, _Grunning, _Gdead)
gcController.addScannableStack(pp, -int64(gp.stack.hi-gp.stack.lo))
if isSystemGoroutine(gp, false) {
@@ -4342,6 +4348,13 @@ func newproc1(fn *funcval, callergp *g, callerpc uintptr) *g {
if trace.enabled {
traceGoCreate(newg, newg.startpc)
}
+
+ // Call our trace goroutine start hook if we have trace
+ // data on the current goroutine.
+ if traceData := callergp.traceData; callergp != nil {
+ newg.traceData = tracingGStart(newg.goid, traceData)
+ }
+
releasem(mp)
return newg
diff --git a/go-src/src/runtime/runtime2.go b/go-src/src/runtime/runtime2.go
index 9381d1e3f7..31c8982aec 100644
--- a/go-src/src/runtime/runtime2.go
+++ b/go-src/src/runtime/runtime2.go
@@ -489,6 +489,10 @@ type g struct {
timer *timer // cached timer for time.Sleep
selectDone atomic.Uint32 // are we participating in a select and did someone win the race?
+ // traceData is a pointer to a trace object
+ // that we will define later in our library code
+ traceData unsafe.Pointer
+
// goroutineProfiled indicates the status of this goroutine's stack for the
// current in-progress goroutine profile
goroutineProfiled goroutineProfileStateHolder
diff --git a/go-src/src/runtime/tracing.go b/go-src/src/runtime/tracing.go
index 7ccdf5f690..9af043edca 100644
--- a/go-src/src/runtime/tracing.go
+++ b/go-src/src/runtime/tracing.go
@@ -1 +1,35 @@
package runtime
+
+import (
+ "unsafe"
+)
+
+// tracingGStart is called when a goroutine starts. It returns a pointer to a
+// the parent routines trace data, and is expected to return a pointer to the
+// new routines trace data.
+//
+// If the parent Go routine has no tracing data, this won't get called
+// thus it is always safe to assume parent is non-nil
+func tracingGStart(goRoutinueID uint64, parentTraceData unsafe.Pointer) unsafe.Pointer
+
+// tracingGExit is called when a goroutine exits. It is passed a pointer to the
+// trace data of the exiting goroutine.
+//
+// If no tracing data is available, this won't get called
+// thus it is always safe to assume parent is non-nil
+func tracingGExit(goRoutinueID uint64, traceData unsafe.Pointer)
+
+// tracingAttachDataToG attaches the given data to the current goroutine.
+func tracingAttachDataToG(data unsafe.Pointer) {
+ getg().traceData = data
+}
+
+// tracingGetDataFromG returns the tracing data attached to the current goroutine.
+func tracingGetDataFromG() unsafe.Pointer {
+ return getg().traceData
+}
+
+// getgoid returns the ID of the current goroutine.
+func getgoid() uint64 {
+ return getg().goid
+}