github.com/MerlinKodo/quic-go@v0.39.2/qlog/qlog.go (about) 1 package qlog 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "log" 8 "net" 9 "runtime/debug" 10 "sync" 11 "time" 12 13 "github.com/MerlinKodo/quic-go/internal/protocol" 14 "github.com/MerlinKodo/quic-go/internal/utils" 15 "github.com/MerlinKodo/quic-go/internal/wire" 16 "github.com/MerlinKodo/quic-go/logging" 17 18 "github.com/francoispqt/gojay" 19 ) 20 21 // Setting of this only works when quic-go is used as a library. 22 // When building a binary from this repository, the version can be set using the following go build flag: 23 // -ldflags="-X github.com/MerlinKodo/quic-go/qlog.quicGoVersion=foobar" 24 var quicGoVersion = "(devel)" 25 26 func init() { 27 if quicGoVersion != "(devel)" { // variable set by ldflags 28 return 29 } 30 info, ok := debug.ReadBuildInfo() 31 if !ok { // no build info available. This happens when quic-go is not used as a library. 32 return 33 } 34 for _, d := range info.Deps { 35 if d.Path == "github.com/MerlinKodo/quic-go" { 36 quicGoVersion = d.Version 37 if d.Replace != nil { 38 if len(d.Replace.Version) > 0 { 39 quicGoVersion = d.Version 40 } else { 41 quicGoVersion += " (replaced)" 42 } 43 } 44 break 45 } 46 } 47 } 48 49 const eventChanSize = 50 50 51 type connectionTracer struct { 52 mutex sync.Mutex 53 54 w io.WriteCloser 55 odcid protocol.ConnectionID 56 perspective protocol.Perspective 57 referenceTime time.Time 58 59 events chan event 60 encodeErr error 61 runStopped chan struct{} 62 63 lastMetrics *metrics 64 } 65 66 // NewConnectionTracer creates a new tracer to record a qlog for a connection. 67 func NewConnectionTracer(w io.WriteCloser, p protocol.Perspective, odcid protocol.ConnectionID) *logging.ConnectionTracer { 68 t := connectionTracer{ 69 w: w, 70 perspective: p, 71 odcid: odcid, 72 runStopped: make(chan struct{}), 73 events: make(chan event, eventChanSize), 74 referenceTime: time.Now(), 75 } 76 go t.run() 77 return &logging.ConnectionTracer{ 78 StartedConnection: func(local, remote net.Addr, srcConnID, destConnID logging.ConnectionID) { 79 t.StartedConnection(local, remote, srcConnID, destConnID) 80 }, 81 NegotiatedVersion: func(chosen logging.VersionNumber, clientVersions, serverVersions []logging.VersionNumber) { 82 t.NegotiatedVersion(chosen, clientVersions, serverVersions) 83 }, 84 ClosedConnection: func(e error) { t.ClosedConnection(e) }, 85 SentTransportParameters: func(tp *wire.TransportParameters) { t.SentTransportParameters(tp) }, 86 ReceivedTransportParameters: func(tp *wire.TransportParameters) { t.ReceivedTransportParameters(tp) }, 87 RestoredTransportParameters: func(tp *wire.TransportParameters) { t.RestoredTransportParameters(tp) }, 88 SentLongHeaderPacket: func(hdr *logging.ExtendedHeader, size logging.ByteCount, ecn logging.ECN, ack *logging.AckFrame, frames []logging.Frame) { 89 t.SentLongHeaderPacket(hdr, size, ecn, ack, frames) 90 }, 91 SentShortHeaderPacket: func(hdr *logging.ShortHeader, size logging.ByteCount, ecn logging.ECN, ack *logging.AckFrame, frames []logging.Frame) { 92 t.SentShortHeaderPacket(hdr, size, ecn, ack, frames) 93 }, 94 ReceivedLongHeaderPacket: func(hdr *logging.ExtendedHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { 95 t.ReceivedLongHeaderPacket(hdr, size, ecn, frames) 96 }, 97 ReceivedShortHeaderPacket: func(hdr *logging.ShortHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { 98 t.ReceivedShortHeaderPacket(hdr, size, ecn, frames) 99 }, 100 ReceivedRetry: func(hdr *wire.Header) { 101 t.ReceivedRetry(hdr) 102 }, 103 ReceivedVersionNegotiationPacket: func(dest, src logging.ArbitraryLenConnectionID, versions []logging.VersionNumber) { 104 t.ReceivedVersionNegotiationPacket(dest, src, versions) 105 }, 106 BufferedPacket: func(pt logging.PacketType, size protocol.ByteCount) { 107 t.BufferedPacket(pt, size) 108 }, 109 DroppedPacket: func(pt logging.PacketType, size protocol.ByteCount, reason logging.PacketDropReason) { 110 t.DroppedPacket(pt, size, reason) 111 }, 112 UpdatedMetrics: func(rttStats *utils.RTTStats, cwnd, bytesInFlight protocol.ByteCount, packetsInFlight int) { 113 t.UpdatedMetrics(rttStats, cwnd, bytesInFlight, packetsInFlight) 114 }, 115 LostPacket: func(encLevel protocol.EncryptionLevel, pn protocol.PacketNumber, lossReason logging.PacketLossReason) { 116 t.LostPacket(encLevel, pn, lossReason) 117 }, 118 UpdatedCongestionState: func(state logging.CongestionState) { 119 t.UpdatedCongestionState(state) 120 }, 121 UpdatedPTOCount: func(value uint32) { 122 t.UpdatedPTOCount(value) 123 }, 124 UpdatedKeyFromTLS: func(encLevel protocol.EncryptionLevel, pers protocol.Perspective) { 125 t.UpdatedKeyFromTLS(encLevel, pers) 126 }, 127 UpdatedKey: func(generation protocol.KeyPhase, remote bool) { 128 t.UpdatedKey(generation, remote) 129 }, 130 DroppedEncryptionLevel: func(encLevel protocol.EncryptionLevel) { 131 t.DroppedEncryptionLevel(encLevel) 132 }, 133 DroppedKey: func(generation protocol.KeyPhase) { 134 t.DroppedKey(generation) 135 }, 136 SetLossTimer: func(tt logging.TimerType, encLevel protocol.EncryptionLevel, timeout time.Time) { 137 t.SetLossTimer(tt, encLevel, timeout) 138 }, 139 LossTimerExpired: func(tt logging.TimerType, encLevel protocol.EncryptionLevel) { 140 t.LossTimerExpired(tt, encLevel) 141 }, 142 LossTimerCanceled: func() { 143 t.LossTimerCanceled() 144 }, 145 ECNStateUpdated: func(state logging.ECNState, trigger logging.ECNStateTrigger) { 146 t.ECNStateUpdated(state, trigger) 147 }, 148 Debug: func(name, msg string) { 149 t.Debug(name, msg) 150 }, 151 Close: func() { 152 t.Close() 153 }, 154 } 155 } 156 157 func (t *connectionTracer) run() { 158 defer close(t.runStopped) 159 buf := &bytes.Buffer{} 160 enc := gojay.NewEncoder(buf) 161 tl := &topLevel{ 162 trace: trace{ 163 VantagePoint: vantagePoint{Type: t.perspective}, 164 CommonFields: commonFields{ 165 ODCID: t.odcid, 166 GroupID: t.odcid, 167 ReferenceTime: t.referenceTime, 168 }, 169 }, 170 } 171 if err := enc.Encode(tl); err != nil { 172 panic(fmt.Sprintf("qlog encoding into a bytes.Buffer failed: %s", err)) 173 } 174 if err := buf.WriteByte('\n'); err != nil { 175 panic(fmt.Sprintf("qlog encoding into a bytes.Buffer failed: %s", err)) 176 } 177 if _, err := t.w.Write(buf.Bytes()); err != nil { 178 t.encodeErr = err 179 } 180 enc = gojay.NewEncoder(t.w) 181 for ev := range t.events { 182 if t.encodeErr != nil { // if encoding failed, just continue draining the event channel 183 continue 184 } 185 if err := enc.Encode(ev); err != nil { 186 t.encodeErr = err 187 continue 188 } 189 if _, err := t.w.Write([]byte{'\n'}); err != nil { 190 t.encodeErr = err 191 } 192 } 193 } 194 195 func (t *connectionTracer) Close() { 196 if err := t.export(); err != nil { 197 log.Printf("exporting qlog failed: %s\n", err) 198 } 199 } 200 201 // export writes a qlog. 202 func (t *connectionTracer) export() error { 203 close(t.events) 204 <-t.runStopped 205 if t.encodeErr != nil { 206 return t.encodeErr 207 } 208 return t.w.Close() 209 } 210 211 func (t *connectionTracer) recordEvent(eventTime time.Time, details eventDetails) { 212 t.events <- event{ 213 RelativeTime: eventTime.Sub(t.referenceTime), 214 eventDetails: details, 215 } 216 } 217 218 func (t *connectionTracer) StartedConnection(local, remote net.Addr, srcConnID, destConnID protocol.ConnectionID) { 219 // ignore this event if we're not dealing with UDP addresses here 220 localAddr, ok := local.(*net.UDPAddr) 221 if !ok { 222 return 223 } 224 remoteAddr, ok := remote.(*net.UDPAddr) 225 if !ok { 226 return 227 } 228 t.mutex.Lock() 229 t.recordEvent(time.Now(), &eventConnectionStarted{ 230 SrcAddr: localAddr, 231 DestAddr: remoteAddr, 232 SrcConnectionID: srcConnID, 233 DestConnectionID: destConnID, 234 }) 235 t.mutex.Unlock() 236 } 237 238 func (t *connectionTracer) NegotiatedVersion(chosen logging.VersionNumber, client, server []logging.VersionNumber) { 239 var clientVersions, serverVersions []versionNumber 240 if len(client) > 0 { 241 clientVersions = make([]versionNumber, len(client)) 242 for i, v := range client { 243 clientVersions[i] = versionNumber(v) 244 } 245 } 246 if len(server) > 0 { 247 serverVersions = make([]versionNumber, len(server)) 248 for i, v := range server { 249 serverVersions[i] = versionNumber(v) 250 } 251 } 252 t.mutex.Lock() 253 t.recordEvent(time.Now(), &eventVersionNegotiated{ 254 clientVersions: clientVersions, 255 serverVersions: serverVersions, 256 chosenVersion: versionNumber(chosen), 257 }) 258 t.mutex.Unlock() 259 } 260 261 func (t *connectionTracer) ClosedConnection(e error) { 262 t.mutex.Lock() 263 t.recordEvent(time.Now(), &eventConnectionClosed{e: e}) 264 t.mutex.Unlock() 265 } 266 267 func (t *connectionTracer) SentTransportParameters(tp *wire.TransportParameters) { 268 t.recordTransportParameters(t.perspective, tp) 269 } 270 271 func (t *connectionTracer) ReceivedTransportParameters(tp *wire.TransportParameters) { 272 t.recordTransportParameters(t.perspective.Opposite(), tp) 273 } 274 275 func (t *connectionTracer) RestoredTransportParameters(tp *wire.TransportParameters) { 276 ev := t.toTransportParameters(tp) 277 ev.Restore = true 278 279 t.mutex.Lock() 280 t.recordEvent(time.Now(), ev) 281 t.mutex.Unlock() 282 } 283 284 func (t *connectionTracer) recordTransportParameters(sentBy protocol.Perspective, tp *wire.TransportParameters) { 285 ev := t.toTransportParameters(tp) 286 ev.Owner = ownerLocal 287 if sentBy != t.perspective { 288 ev.Owner = ownerRemote 289 } 290 ev.SentBy = sentBy 291 292 t.mutex.Lock() 293 t.recordEvent(time.Now(), ev) 294 t.mutex.Unlock() 295 } 296 297 func (t *connectionTracer) toTransportParameters(tp *wire.TransportParameters) *eventTransportParameters { 298 var pa *preferredAddress 299 if tp.PreferredAddress != nil { 300 pa = &preferredAddress{ 301 IPv4: tp.PreferredAddress.IPv4, 302 PortV4: tp.PreferredAddress.IPv4Port, 303 IPv6: tp.PreferredAddress.IPv6, 304 PortV6: tp.PreferredAddress.IPv6Port, 305 ConnectionID: tp.PreferredAddress.ConnectionID, 306 StatelessResetToken: tp.PreferredAddress.StatelessResetToken, 307 } 308 } 309 return &eventTransportParameters{ 310 OriginalDestinationConnectionID: tp.OriginalDestinationConnectionID, 311 InitialSourceConnectionID: tp.InitialSourceConnectionID, 312 RetrySourceConnectionID: tp.RetrySourceConnectionID, 313 StatelessResetToken: tp.StatelessResetToken, 314 DisableActiveMigration: tp.DisableActiveMigration, 315 MaxIdleTimeout: tp.MaxIdleTimeout, 316 MaxUDPPayloadSize: tp.MaxUDPPayloadSize, 317 AckDelayExponent: tp.AckDelayExponent, 318 MaxAckDelay: tp.MaxAckDelay, 319 ActiveConnectionIDLimit: tp.ActiveConnectionIDLimit, 320 InitialMaxData: tp.InitialMaxData, 321 InitialMaxStreamDataBidiLocal: tp.InitialMaxStreamDataBidiLocal, 322 InitialMaxStreamDataBidiRemote: tp.InitialMaxStreamDataBidiRemote, 323 InitialMaxStreamDataUni: tp.InitialMaxStreamDataUni, 324 InitialMaxStreamsBidi: int64(tp.MaxBidiStreamNum), 325 InitialMaxStreamsUni: int64(tp.MaxUniStreamNum), 326 PreferredAddress: pa, 327 MaxDatagramFrameSize: tp.MaxDatagramFrameSize, 328 } 329 } 330 331 func (t *connectionTracer) SentLongHeaderPacket( 332 hdr *logging.ExtendedHeader, 333 size logging.ByteCount, 334 ecn logging.ECN, 335 ack *logging.AckFrame, 336 frames []logging.Frame, 337 ) { 338 t.sentPacket(*transformLongHeader(hdr), size, hdr.Length, ecn, ack, frames) 339 } 340 341 func (t *connectionTracer) SentShortHeaderPacket( 342 hdr *logging.ShortHeader, 343 size logging.ByteCount, 344 ecn logging.ECN, 345 ack *logging.AckFrame, 346 frames []logging.Frame, 347 ) { 348 t.sentPacket(*transformShortHeader(hdr), size, 0, ecn, ack, frames) 349 } 350 351 func (t *connectionTracer) sentPacket( 352 hdr gojay.MarshalerJSONObject, 353 size, payloadLen logging.ByteCount, 354 ecn logging.ECN, 355 ack *logging.AckFrame, 356 frames []logging.Frame, 357 ) { 358 numFrames := len(frames) 359 if ack != nil { 360 numFrames++ 361 } 362 fs := make([]frame, 0, numFrames) 363 if ack != nil { 364 fs = append(fs, frame{Frame: ack}) 365 } 366 for _, f := range frames { 367 fs = append(fs, frame{Frame: f}) 368 } 369 t.mutex.Lock() 370 t.recordEvent(time.Now(), &eventPacketSent{ 371 Header: hdr, 372 Length: size, 373 PayloadLength: payloadLen, 374 ECN: ecn, 375 Frames: fs, 376 }) 377 t.mutex.Unlock() 378 } 379 380 func (t *connectionTracer) ReceivedLongHeaderPacket(hdr *logging.ExtendedHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { 381 fs := make([]frame, len(frames)) 382 for i, f := range frames { 383 fs[i] = frame{Frame: f} 384 } 385 header := *transformLongHeader(hdr) 386 t.mutex.Lock() 387 t.recordEvent(time.Now(), &eventPacketReceived{ 388 Header: header, 389 Length: size, 390 PayloadLength: hdr.Length, 391 ECN: ecn, 392 Frames: fs, 393 }) 394 t.mutex.Unlock() 395 } 396 397 func (t *connectionTracer) ReceivedShortHeaderPacket(hdr *logging.ShortHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { 398 fs := make([]frame, len(frames)) 399 for i, f := range frames { 400 fs[i] = frame{Frame: f} 401 } 402 header := *transformShortHeader(hdr) 403 t.mutex.Lock() 404 t.recordEvent(time.Now(), &eventPacketReceived{ 405 Header: header, 406 Length: size, 407 PayloadLength: size - wire.ShortHeaderLen(hdr.DestConnectionID, hdr.PacketNumberLen), 408 ECN: ecn, 409 Frames: fs, 410 }) 411 t.mutex.Unlock() 412 } 413 414 func (t *connectionTracer) ReceivedRetry(hdr *wire.Header) { 415 t.mutex.Lock() 416 t.recordEvent(time.Now(), &eventRetryReceived{ 417 Header: *transformHeader(hdr), 418 }) 419 t.mutex.Unlock() 420 } 421 422 func (t *connectionTracer) ReceivedVersionNegotiationPacket(dest, src logging.ArbitraryLenConnectionID, versions []logging.VersionNumber) { 423 ver := make([]versionNumber, len(versions)) 424 for i, v := range versions { 425 ver[i] = versionNumber(v) 426 } 427 t.mutex.Lock() 428 t.recordEvent(time.Now(), &eventVersionNegotiationReceived{ 429 Header: packetHeaderVersionNegotiation{ 430 SrcConnectionID: src, 431 DestConnectionID: dest, 432 }, 433 SupportedVersions: ver, 434 }) 435 t.mutex.Unlock() 436 } 437 438 func (t *connectionTracer) BufferedPacket(pt logging.PacketType, size protocol.ByteCount) { 439 t.mutex.Lock() 440 t.recordEvent(time.Now(), &eventPacketBuffered{ 441 PacketType: pt, 442 PacketSize: size, 443 }) 444 t.mutex.Unlock() 445 } 446 447 func (t *connectionTracer) DroppedPacket(pt logging.PacketType, size protocol.ByteCount, reason logging.PacketDropReason) { 448 t.mutex.Lock() 449 t.recordEvent(time.Now(), &eventPacketDropped{ 450 PacketType: pt, 451 PacketSize: size, 452 Trigger: packetDropReason(reason), 453 }) 454 t.mutex.Unlock() 455 } 456 457 func (t *connectionTracer) UpdatedMetrics(rttStats *utils.RTTStats, cwnd, bytesInFlight protocol.ByteCount, packetsInFlight int) { 458 m := &metrics{ 459 MinRTT: rttStats.MinRTT(), 460 SmoothedRTT: rttStats.SmoothedRTT(), 461 LatestRTT: rttStats.LatestRTT(), 462 RTTVariance: rttStats.MeanDeviation(), 463 CongestionWindow: cwnd, 464 BytesInFlight: bytesInFlight, 465 PacketsInFlight: packetsInFlight, 466 } 467 t.mutex.Lock() 468 t.recordEvent(time.Now(), &eventMetricsUpdated{ 469 Last: t.lastMetrics, 470 Current: m, 471 }) 472 t.lastMetrics = m 473 t.mutex.Unlock() 474 } 475 476 func (t *connectionTracer) AcknowledgedPacket(protocol.EncryptionLevel, protocol.PacketNumber) {} 477 478 func (t *connectionTracer) LostPacket(encLevel protocol.EncryptionLevel, pn protocol.PacketNumber, lossReason logging.PacketLossReason) { 479 t.mutex.Lock() 480 t.recordEvent(time.Now(), &eventPacketLost{ 481 PacketType: getPacketTypeFromEncryptionLevel(encLevel), 482 PacketNumber: pn, 483 Trigger: packetLossReason(lossReason), 484 }) 485 t.mutex.Unlock() 486 } 487 488 func (t *connectionTracer) UpdatedCongestionState(state logging.CongestionState) { 489 t.mutex.Lock() 490 t.recordEvent(time.Now(), &eventCongestionStateUpdated{state: congestionState(state)}) 491 t.mutex.Unlock() 492 } 493 494 func (t *connectionTracer) UpdatedPTOCount(value uint32) { 495 t.mutex.Lock() 496 t.recordEvent(time.Now(), &eventUpdatedPTO{Value: value}) 497 t.mutex.Unlock() 498 } 499 500 func (t *connectionTracer) UpdatedKeyFromTLS(encLevel protocol.EncryptionLevel, pers protocol.Perspective) { 501 t.mutex.Lock() 502 t.recordEvent(time.Now(), &eventKeyUpdated{ 503 Trigger: keyUpdateTLS, 504 KeyType: encLevelToKeyType(encLevel, pers), 505 }) 506 t.mutex.Unlock() 507 } 508 509 func (t *connectionTracer) UpdatedKey(generation protocol.KeyPhase, remote bool) { 510 trigger := keyUpdateLocal 511 if remote { 512 trigger = keyUpdateRemote 513 } 514 t.mutex.Lock() 515 now := time.Now() 516 t.recordEvent(now, &eventKeyUpdated{ 517 Trigger: trigger, 518 KeyType: keyTypeClient1RTT, 519 Generation: generation, 520 }) 521 t.recordEvent(now, &eventKeyUpdated{ 522 Trigger: trigger, 523 KeyType: keyTypeServer1RTT, 524 Generation: generation, 525 }) 526 t.mutex.Unlock() 527 } 528 529 func (t *connectionTracer) DroppedEncryptionLevel(encLevel protocol.EncryptionLevel) { 530 t.mutex.Lock() 531 now := time.Now() 532 if encLevel == protocol.Encryption0RTT { 533 t.recordEvent(now, &eventKeyDiscarded{KeyType: encLevelToKeyType(encLevel, t.perspective)}) 534 } else { 535 t.recordEvent(now, &eventKeyDiscarded{KeyType: encLevelToKeyType(encLevel, protocol.PerspectiveServer)}) 536 t.recordEvent(now, &eventKeyDiscarded{KeyType: encLevelToKeyType(encLevel, protocol.PerspectiveClient)}) 537 } 538 t.mutex.Unlock() 539 } 540 541 func (t *connectionTracer) DroppedKey(generation protocol.KeyPhase) { 542 t.mutex.Lock() 543 now := time.Now() 544 t.recordEvent(now, &eventKeyDiscarded{ 545 KeyType: encLevelToKeyType(protocol.Encryption1RTT, protocol.PerspectiveServer), 546 Generation: generation, 547 }) 548 t.recordEvent(now, &eventKeyDiscarded{ 549 KeyType: encLevelToKeyType(protocol.Encryption1RTT, protocol.PerspectiveClient), 550 Generation: generation, 551 }) 552 t.mutex.Unlock() 553 } 554 555 func (t *connectionTracer) SetLossTimer(tt logging.TimerType, encLevel protocol.EncryptionLevel, timeout time.Time) { 556 t.mutex.Lock() 557 now := time.Now() 558 t.recordEvent(now, &eventLossTimerSet{ 559 TimerType: timerType(tt), 560 EncLevel: encLevel, 561 Delta: timeout.Sub(now), 562 }) 563 t.mutex.Unlock() 564 } 565 566 func (t *connectionTracer) LossTimerExpired(tt logging.TimerType, encLevel protocol.EncryptionLevel) { 567 t.mutex.Lock() 568 t.recordEvent(time.Now(), &eventLossTimerExpired{ 569 TimerType: timerType(tt), 570 EncLevel: encLevel, 571 }) 572 t.mutex.Unlock() 573 } 574 575 func (t *connectionTracer) LossTimerCanceled() { 576 t.mutex.Lock() 577 t.recordEvent(time.Now(), &eventLossTimerCanceled{}) 578 t.mutex.Unlock() 579 } 580 581 func (t *connectionTracer) ECNStateUpdated(state logging.ECNState, trigger logging.ECNStateTrigger) { 582 t.mutex.Lock() 583 t.recordEvent(time.Now(), &eventECNStateUpdated{state: state, trigger: trigger}) 584 t.mutex.Unlock() 585 } 586 587 func (t *connectionTracer) Debug(name, msg string) { 588 t.mutex.Lock() 589 t.recordEvent(time.Now(), &eventGeneric{ 590 name: name, 591 msg: msg, 592 }) 593 t.mutex.Unlock() 594 }