mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-03 20:27:35 +03:00
201 lines
5.2 KiB
Go
201 lines
5.2 KiB
Go
package quictrace
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
|
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
|
"github.com/lucas-clemente/quic-go/internal/wire"
|
|
"github.com/lucas-clemente/quic-go/quictrace/pb"
|
|
)
|
|
|
|
type traceEvent struct {
|
|
connID protocol.ConnectionID
|
|
ev Event
|
|
}
|
|
|
|
// A tracer is used to trace a QUIC connection
|
|
type tracer struct {
|
|
eventQueue chan traceEvent
|
|
|
|
events map[string] /* conn ID */ []traceEvent
|
|
}
|
|
|
|
var _ Tracer = &tracer{}
|
|
|
|
// NewTracer creates a new Tracer
|
|
func NewTracer() Tracer {
|
|
qt := &tracer{
|
|
eventQueue: make(chan traceEvent, 1<<10),
|
|
events: make(map[string][]traceEvent),
|
|
}
|
|
go qt.run()
|
|
return qt
|
|
}
|
|
|
|
// Trace traces an event
|
|
func (t *tracer) Trace(connID protocol.ConnectionID, ev Event) {
|
|
t.eventQueue <- traceEvent{connID: connID, ev: ev}
|
|
}
|
|
|
|
func (t *tracer) run() {
|
|
for tev := range t.eventQueue {
|
|
key := string(tev.connID)
|
|
if _, ok := t.events[key]; !ok {
|
|
t.events[key] = make([]traceEvent, 0, 10*1<<10)
|
|
}
|
|
t.events[key] = append(t.events[key], tev)
|
|
}
|
|
}
|
|
|
|
func (t *tracer) GetAllTraces() map[string][]byte {
|
|
traces := make(map[string][]byte)
|
|
for connID := range t.events {
|
|
data, err := t.emitByConnIDAsString(connID)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
traces[connID] = data
|
|
}
|
|
return traces
|
|
}
|
|
|
|
// Emit emits the serialized protobuf that will be consumed by quic-trace
|
|
func (t *tracer) Emit(connID protocol.ConnectionID) ([]byte, error) {
|
|
return t.emitByConnIDAsString(string(connID))
|
|
}
|
|
|
|
func (t *tracer) emitByConnIDAsString(connID string) ([]byte, error) {
|
|
events, ok := t.events[connID]
|
|
if !ok {
|
|
return nil, fmt.Errorf("no trace found for connection ID %s", connID)
|
|
}
|
|
trace := &pb.Trace{
|
|
DestinationConnectionId: []byte{1, 2, 3, 4, 5, 6, 7, 8},
|
|
SourceConnectionId: []byte{1, 2, 3, 4, 5, 6, 7, 8},
|
|
ProtocolVersion: []byte{0xff, 0, 0, 19},
|
|
Events: make([]*pb.Event, len(events)),
|
|
}
|
|
var startTime time.Time
|
|
for i, ev := range events {
|
|
event := ev.ev
|
|
if i == 0 {
|
|
startTime = event.Time
|
|
}
|
|
|
|
packetNumber := uint64(event.PacketNumber)
|
|
packetSize := uint64(event.PacketSize)
|
|
|
|
trace.Events[i] = &pb.Event{
|
|
TimeUs: durationToUs(event.Time.Sub(startTime)),
|
|
EventType: getEventType(event.EventType),
|
|
PacketSize: &packetSize,
|
|
PacketNumber: &packetNumber,
|
|
TransportState: getTransportState(event.TransportState),
|
|
EncryptionLevel: getEncryptionLevel(event.EncryptionLevel),
|
|
Frames: getFrames(event.Frames),
|
|
}
|
|
}
|
|
delete(t.events, connID)
|
|
return proto.Marshal(trace)
|
|
}
|
|
|
|
func getEventType(evType EventType) *pb.EventType {
|
|
var t pb.EventType
|
|
switch evType {
|
|
case PacketSent:
|
|
t = pb.EventType_PACKET_SENT
|
|
case PacketReceived:
|
|
t = pb.EventType_PACKET_RECEIVED
|
|
case PacketLost:
|
|
t = pb.EventType_PACKET_LOST
|
|
default:
|
|
panic("unknown event type")
|
|
}
|
|
return &t
|
|
}
|
|
|
|
func getEncryptionLevel(encLevel protocol.EncryptionLevel) *pb.EncryptionLevel {
|
|
enc := pb.EncryptionLevel_ENCRYPTION_UNKNOWN
|
|
switch encLevel {
|
|
case protocol.EncryptionInitial:
|
|
enc = pb.EncryptionLevel_ENCRYPTION_INITIAL
|
|
case protocol.EncryptionHandshake:
|
|
enc = pb.EncryptionLevel_ENCRYPTION_HANDSHAKE
|
|
case protocol.Encryption1RTT:
|
|
enc = pb.EncryptionLevel_ENCRYPTION_1RTT
|
|
}
|
|
return &enc
|
|
}
|
|
|
|
func getFrames(wframes []wire.Frame) []*pb.Frame {
|
|
streamFrameType := pb.FrameType_STREAM
|
|
cryptoFrameType := pb.FrameType_CRYPTO
|
|
ackFrameType := pb.FrameType_ACK
|
|
var frames []*pb.Frame
|
|
for _, frame := range wframes {
|
|
switch f := frame.(type) {
|
|
case *wire.CryptoFrame:
|
|
offset := uint64(f.Offset)
|
|
length := uint64(len(f.Data))
|
|
frames = append(frames, &pb.Frame{
|
|
FrameType: &cryptoFrameType,
|
|
CryptoFrameInfo: &pb.CryptoFrameInfo{
|
|
Offset: &offset,
|
|
Length: &length,
|
|
},
|
|
})
|
|
case *wire.StreamFrame:
|
|
streamID := uint64(f.StreamID)
|
|
offset := uint64(f.Offset)
|
|
length := uint64(f.DataLen())
|
|
frames = append(frames, &pb.Frame{
|
|
FrameType: &streamFrameType,
|
|
StreamFrameInfo: &pb.StreamFrameInfo{
|
|
StreamId: &streamID,
|
|
Offset: &offset,
|
|
Length: &length,
|
|
},
|
|
})
|
|
case *wire.AckFrame:
|
|
var ackedPackets []*pb.AckBlock
|
|
for _, ackBlock := range f.AckRanges {
|
|
firstPacket := uint64(ackBlock.Smallest)
|
|
lastPacket := uint64(ackBlock.Largest)
|
|
ackedPackets = append(ackedPackets, &pb.AckBlock{
|
|
FirstPacket: &firstPacket,
|
|
LastPacket: &lastPacket,
|
|
})
|
|
}
|
|
frames = append(frames, &pb.Frame{
|
|
FrameType: &ackFrameType,
|
|
AckInfo: &pb.AckInfo{
|
|
AckDelayUs: durationToUs(f.DelayTime),
|
|
AckedPackets: ackedPackets,
|
|
},
|
|
})
|
|
}
|
|
}
|
|
return frames
|
|
}
|
|
|
|
func getTransportState(state *TransportState) *pb.TransportState {
|
|
bytesInFlight := uint64(state.BytesInFlight)
|
|
congestionWindow := uint64(state.CongestionWindow)
|
|
ccs := fmt.Sprintf("InSlowStart: %t, InRecovery: %t", state.InSlowStart, state.InRecovery)
|
|
return &pb.TransportState{
|
|
MinRttUs: durationToUs(state.MinRTT),
|
|
SmoothedRttUs: durationToUs(state.SmoothedRTT),
|
|
LastRttUs: durationToUs(state.LatestRTT),
|
|
InFlightBytes: &bytesInFlight,
|
|
CwndBytes: &congestionWindow,
|
|
CongestionControlState: &ccs,
|
|
}
|
|
}
|
|
|
|
func durationToUs(d time.Duration) *uint64 {
|
|
dur := uint64(d / 1000)
|
|
return &dur
|
|
}
|