-
Notifications
You must be signed in to change notification settings - Fork 3
/
dotnetdiag.go
156 lines (133 loc) · 3.25 KB
/
dotnetdiag.go
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
package dotnetdiag
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"io"
"golang.org/x/text/encoding/unicode"
)
var (
ErrSessionIDMismatch = fmt.Errorf("session ID missmatch")
ErrHeaderMalformed = fmt.Errorf("malformed header")
ErrDiagnosticServer = fmt.Errorf("diagnostic server")
)
// DOTNET_IPC_V1 magic header.
var magic = [...]byte{0x44, 0x4F, 0x54, 0x4E, 0x45, 0x54, 0x5f, 0x49, 0x50, 0x43, 0x5F, 0x56, 0x31, 0x00}
type Header struct {
Magic [14]uint8
Size uint16
CommandSet uint8
CommandID uint8
Reserved uint16
}
const headerSize = 20
const (
_ = iota
CommandSetDump
CommandSetEventPipe
CommandSetProfiler
CommandSetProcess
CommandSetServer = 0xFF
)
const (
_ = iota
EventPipeStopTracing
EventPipeCollectTracing
EventPipeCollectTracing2
)
type CollectTracingPayload struct {
CircularBufferSizeMB uint32
Format Format
Providers []ProviderConfig
}
type Format uint32
const (
FormatNetPerf Format = iota
FormatNetTrace
)
type ProviderConfig struct {
Keywords uint64
LogLevel uint32
ProviderName string
FilterData string
}
type ErrorResponse struct {
Code uint32
}
type CollectTracingResponse struct {
SessionID uint64
}
type StopTracingPayload struct {
SessionID uint64
}
type StopTracingResponse struct {
SessionID uint64
}
func writeMessage(w io.Writer, commandSet, commandID uint8, payload []byte) error {
bw := bufio.NewWriter(w)
err := binary.Write(bw, binary.LittleEndian, Header{
Magic: magic,
Size: uint16(headerSize + len(payload)),
CommandSet: commandSet,
CommandID: commandID,
Reserved: 0,
})
if err != nil {
return err
}
if _, err = bw.Write(payload); err != nil {
return err
}
return bw.Flush()
}
func readResponse(r io.Reader, v interface{}) error {
var h Header
if err := binary.Read(r, binary.LittleEndian, &h); err != nil {
return err
}
if h.Magic != magic {
return ErrHeaderMalformed
}
if !(h.CommandSet == CommandSetServer && h.CommandID == 0xFF) {
return binary.Read(r, binary.LittleEndian, v)
}
// TODO: improve error handling.
var er ErrorResponse
if err := binary.Read(r, binary.LittleEndian, &er); err != nil {
return err
}
return fmt.Errorf("%w: error code %#x", ErrDiagnosticServer, er.Code)
}
func (p CollectTracingPayload) Bytes() []byte {
b := new(bytes.Buffer)
_ = binary.Write(b, binary.LittleEndian, p.CircularBufferSizeMB)
_ = binary.Write(b, binary.LittleEndian, p.Format)
_ = binary.Write(b, binary.LittleEndian, uint32(len(p.Providers)))
for _, x := range p.Providers {
_ = binary.Write(b, binary.LittleEndian, x.Keywords)
_ = binary.Write(b, binary.LittleEndian, x.LogLevel)
b.Write(mustStringBytes(x.ProviderName))
b.Write(mustStringBytes(x.FilterData))
}
return b.Bytes()
}
func (p StopTracingPayload) Bytes() []byte {
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, p.SessionID)
return b
}
var enc = unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()
func mustStringBytes(s string) []byte {
b := new(bytes.Buffer) // TODO pre-allocate
if len(s) > 0 {
_ = binary.Write(b, binary.LittleEndian, uint32(len(s)+1))
x, err := enc.Bytes([]byte(s))
if err != nil {
panic(err)
}
b.Write(x)
}
_ = binary.Write(b, binary.LittleEndian, uint16(0))
return b.Bytes()
}