@@ -2,16 +2,201 @@ package container
22
33import (
44 "context"
5+ "errors"
6+ "fmt"
7+ "strconv"
8+ "strings"
9+ "time"
510
611 "github.com/YLonely/sysdig-monitor/log"
712 "github.com/YLonely/sysdig-monitor/server/model"
8- "github.com/YLonely/sysdig-monitor/sysdig"
913)
1014
11- func processLoop ( ctx context. Context , c * model. Container , ch chan sysdig. Event ) {
12- log . L . WithField ( "container-id" , c . ID ). Info ( "process loop start." )
13- for e := range ch {
15+ const latency100 = time . Millisecond * 100
16+ const latency10 = time . Millisecond * 10
17+ const latency1 = time . Millisecond * 1
1418
19+ type containerEvent struct {
20+ eventType string
21+ eventDir string
22+ containerID string
23+ containerName string
24+ fdType string
25+ fdName string
26+ isIOWrite , isIORead bool
27+ bufferLen int
28+ latency time.Duration
29+ rawRes int
30+ syscallType string
31+ virtualtid int
32+ }
33+
34+ func processLoop (ctx context.Context , c * model.Container , ch chan containerEvent ) error {
35+ var e containerEvent
36+ var err error
37+ for {
38+ select {
39+ case e = <- ch :
40+ case <- ctx .Done ():
41+ return nil
42+ }
43+ if e .containerID != c .ID {
44+ return errors .New ("event id mismatch" )
45+ }
46+ // container exits
47+ if e .eventType == "procexit" && e .virtualtid == 1 {
48+ return nil
49+ }
50+ if e .eventDir == "<" {
51+ if err = handleSysCall (c , e ); err != nil {
52+ log .L .WithError (err ).WithField ("container-id" , c .ID ).Error ("syscall handler error" )
53+ }
54+ if e .rawRes >= 0 && (e .isIORead || e .isIOWrite ) {
55+ if err = handleIO (c , e ); err != nil {
56+ //if something wrong happens, just log it out
57+ log .L .WithField ("container-id" , c .ID ).WithError (err ).Error ("io handle error" )
58+ }
59+ } else {
60+ // may have some other handler?
61+ if err = handleNetwork (c , e ); err != nil {
62+ log .L .WithField ("container-id" , c .ID ).WithError (err ).Error ("network handler error" )
63+ }
64+ }
65+ }
66+ }
67+ }
68+
69+ func handleSysCall (c * model.Container , e containerEvent ) error {
70+ syscall := e .syscallType
71+ latency := e .latency
72+ if len (syscall ) <= 0 {
73+ return nil
74+ }
75+ if _ , exists := c .IndividualCalls [syscall ]; ! exists {
76+ c .IndividualCalls [syscall ] = & model.SystemCall {Name : syscall }
77+ }
78+ call := c .IndividualCalls [syscall ]
79+ call .Calls ++
80+ call .TotalTime += latency
81+ c .SystemCalls .TotalCalls ++
82+ return nil
83+ }
84+
85+ func handleIO (c * model.Container , e containerEvent ) error {
86+ if e .fdType == "file" {
87+ return handleFileIO (c , e )
88+ } else if e .fdType == "ipv4" || e .fdType == "ipv6" {
89+ return handleNetIO (c , e )
90+ }
91+ return nil
92+ }
93+
94+ func handleNetIO (c * model.Container , e containerEvent ) error {
95+ bufLen := e .bufferLen
96+ meta , err := connectionMeta (e .fdName )
97+ if err != nil {
98+ return err
99+ }
100+ // if event shows that a net io begins before "connect" or "accpet",
101+ // we just ignore the error sequence and add a new connection
102+ if _ , exists := c .ActiveConnections [meta ]; ! exists {
103+ c .ActiveConnections [meta ] = & model.Connection {Type : e .fdType }
104+ }
105+ conn := c .ActiveConnections [meta ]
106+ if e .isIORead {
107+ conn .ReadIn += int64 (bufLen )
108+ c .Network .TotalReadIn += int64 (bufLen )
109+ } else if e .isIOWrite {
110+ conn .WriteOut += int64 (bufLen )
111+ c .Network .TotalWriteOut += int64 (bufLen )
112+ }
113+ return nil
114+ }
115+
116+ func handleFileIO (c * model.Container , e containerEvent ) error {
117+ fileName := e .fdName
118+ bufLen := e .bufferLen
119+ if _ , exists := c .AccessedFiles [fileName ]; ! exists {
120+ c .AccessedFiles [fileName ] = & model.File {Name : fileName }
121+ }
122+ file := c .AccessedFiles [fileName ]
123+ if e .isIOWrite {
124+ file .WriteOut += int64 (bufLen )
125+ c .FileSystem .TotalWriteOut += int64 (bufLen )
126+ } else if e .isIORead {
127+ file .ReadIn += int64 (bufLen )
128+ c .FileSystem .TotalReadIn += int64 (bufLen )
129+ }
130+ latency := e .latency
131+ if ! strings .HasPrefix (e .fdName , "/dev/" ) {
132+ iocall := & model.IOCall {FileName : fileName , Latency : latency }
133+ if latency > latency100 {
134+ c .IOCalls100 = append (c .IOCalls100 , iocall )
135+ }
136+ if latency > latency10 {
137+ c .IOCalls10 = append (c .IOCalls10 , iocall )
138+ }
139+ if latency > latency1 {
140+ c .IOCalls1 = append (c .IOCalls1 , iocall )
141+ }
142+ }
143+ return nil
144+ }
145+
146+ func handleNetwork (c * model.Container , e containerEvent ) error {
147+ var (
148+ meta model.ConnectionMeta
149+ err error
150+ )
151+ if e .eventType == "connect" || e .eventType == "accept" {
152+ if meta , err = connectionMeta (e .fdName ); err != nil {
153+ return err
154+ }
155+ if _ , exists := c .ActiveConnections [meta ]; ! exists {
156+ c .ActiveConnections [meta ] = & model.Connection {Type : e .fdType }
157+ }
158+ } else if e .eventType == "close" && ! strings .HasPrefix (e .fdName , "/" ) {
159+ if meta , err = connectionMeta (e .fdName ); err != nil {
160+ return err
161+ }
162+ // should return nonexists error?
163+ if _ , exists := c .ActiveConnections [meta ]; exists {
164+ c .ActiveConnections [meta ] = nil
165+ }
166+ }
167+ return nil
168+ }
169+
170+ func connectionMeta (fdname string ) (model.ConnectionMeta , error ) {
171+ parts := strings .Split (fdname , "->" )
172+ meta := model.ConnectionMeta {}
173+ if len (parts ) != 2 {
174+ return meta , fmt .Errorf ("wrong connection meta format:%v" , fdname )
175+ }
176+ source , dest := parts [0 ], parts [1 ]
177+
178+ var err error
179+ if meta .SourceIP , meta .SourcePort , err = splitAddress (source ); err != nil {
180+ return meta , err
181+ }
182+ if meta .DestIP , meta .DestPort , err = splitAddress (dest ); err != nil {
183+ return meta , err
184+ }
185+ return meta , nil
186+ }
187+
188+ func splitAddress (address string ) (string , int , error ) {
189+ var (
190+ portStart , port int
191+ err error
192+ )
193+ if len (address ) <= 0 {
194+ return "" , - 1 , errors .New ("empty address" )
195+ }
196+ for portStart := len (address ) - 1 ; portStart >= 0 && address [portStart ] != ':' ; portStart -- {
197+ }
198+ if port , err = strconv .Atoi (address [portStart + 1 :]); err != nil {
199+ return "" , - 1 , fmt .Errorf ("wrong address format:%v" , address )
15200 }
16- log . L . WithField ( "container-id" , c . ID ). Error ( "process loop unexpected exited." )
201+ return address [: portStart ], port , nil
17202}
0 commit comments