@@ -5,7 +5,6 @@ package dotnetspy
5
5
import (
6
6
"context"
7
7
"io"
8
- "sync"
9
8
"time"
10
9
11
10
"github.com/pyroscope-io/dotnetdiag"
@@ -14,13 +13,13 @@ import (
14
13
)
15
14
16
15
type session struct {
17
- pid int
16
+ pid int
17
+ timeout time.Duration
18
18
19
19
config dotnetdiag.CollectTracingConfig
20
20
session * dotnetdiag.Session
21
21
22
22
ch chan line
23
- m sync.Mutex
24
23
stopped bool
25
24
}
26
25
@@ -31,7 +30,8 @@ type line struct {
31
30
32
31
func newSession (pid int ) * session {
33
32
return & session {
34
- pid : pid ,
33
+ pid : pid ,
34
+ timeout : 3 * time .Second ,
35
35
config : dotnetdiag.CollectTracingConfig {
36
36
CircularBufferSizeMB : 100 ,
37
37
Providers : []dotnetdiag.ProviderConfig {
@@ -45,10 +45,13 @@ func newSession(pid int) *session {
45
45
}
46
46
}
47
47
48
+ // start opens a new diagnostic session to the process given, and asynchronously
49
+ // processes the event stream.
48
50
func (s * session ) start () error {
49
- ctx , cancel := context .WithTimeout (context .Background (), time . Second * 3 )
51
+ ctx , cancel := context .WithTimeout (context .Background (), s . timeout )
50
52
defer cancel ()
51
-
53
+ // If the process does not create Diagnostic Server, the next call will
54
+ // fail, and a session won't be created.
52
55
client := dotnetdiag .NewClient (waitDiagnosticServer (ctx , s .pid ))
53
56
ns , err := client .CollectTracing (s .config )
54
57
if err != nil {
@@ -78,6 +81,8 @@ func (s *session) start() error {
78
81
case nil :
79
82
continue
80
83
case io .EOF :
84
+ // The session is closed by us (on flush or stop call),
85
+ // or the target process has exited.
81
86
for k , v := range p .Samples () {
82
87
s .ch <- line {
83
88
name : []byte (k ),
@@ -92,30 +97,41 @@ func (s *session) start() error {
92
97
return nil
93
98
}
94
99
100
+ // flush closes NetTrace stream in order to retrieve samples,
101
+ // and starts a new session, if not in stopped state.
95
102
func (s * session ) flush (cb func ([]byte , uint64 )) error {
103
+ // Ignore call, if NetTrace session has not been established.
104
+ if s .session == nil {
105
+ return nil
106
+ }
96
107
_ = s .session .Close ()
97
108
for v := range s .ch {
98
109
cb (v .name , uint64 (v .val ))
99
110
}
100
- s .m .Lock ()
101
- defer s .m .Unlock ()
102
111
if s .stopped {
103
112
return nil
104
113
}
105
114
return s .start ()
106
115
}
107
116
117
+ // stop closes diagnostic session, if it was established, and sets the
118
+ // flag preventing session to start again.
108
119
func (s * session ) stop () error {
109
- s . m . Lock ()
110
- defer s . m . Unlock ()
111
- _ = s . session . Close ()
120
+ if s . session != nil {
121
+ _ = s . session . Close ()
122
+ }
112
123
s .stopped = true
113
124
return nil
114
125
}
115
126
116
127
// .Net runtime requires some time to initialize diagnostic IPC server and
117
- // start accepting connections.
128
+ // start accepting connections. If it fails before context cancel, an empty
129
+ // string will be returned.
118
130
func waitDiagnosticServer (ctx context.Context , pid int ) string {
131
+ // Do not wait for the timer to fire for the first time.
132
+ if addr := dotnetdiag .DefaultServerAddress (pid ); addr != "" {
133
+ return addr
134
+ }
119
135
ticker := time .NewTicker (time .Millisecond * 100 )
120
136
defer ticker .Stop ()
121
137
for {
0 commit comments