summaryrefslogtreecommitdiff
path: root/internal/eventloop.go
blob: 0dca08342c8faa8e3735bf0dca42c29c1db4c916 (plain)
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
package internal

import "C"

import (
	"fmt"

	. "ioriotng/internal/generated/types"
)

type eventLoop struct {
	evCh      chan *eventPair       // Channel of events (enter+exit tracepoint results of a syscall).
	enterEvs  map[uint32]*eventPair // Temp. store of sys_enter tracepoints per Tid.
	files     map[int32]file        // Track all open files by file descriptor.
	comms     map[uint32]string     // Program or thread name of the current Tid.
	prevPairs map[uint32]*eventPair // Previous event (to calculate time differences between two events)
}

func newEventLoop() *eventLoop {
	return &eventLoop{
		evCh:      make(chan *eventPair),
		enterEvs:  make(map[uint32]*eventPair),
		files:     make(map[int32]file),
		comms:     make(map[uint32]string),
		prevPairs: make(map[uint32]*eventPair),
	}
}

func (e *eventLoop) run(rawCh <-chan []byte) {
	fmt.Println(eventStreamHeader)
	for ev := range e.events(rawCh) {
		fmt.Println(ev.String())
		if ev.prevPair != nil {
			// Only recycle the previous event, as the current event is the previous event of the next event!
			ev.prevPair.recycle()
			continue
		}
	}
	fmt.Println("Good bye")
}

func (e *eventLoop) events(rawCh <-chan []byte) <-chan *eventPair {
	// Syscall entered
	enter := func(enterEv event) {
		e.enterEvs[enterEv.GetTid()] = newEventPair(enterEv)
	}

	// Syscall exited
	exit := func(exitEv event) {
		ev, ok := e.enterEvs[exitEv.GetTid()]
		if !ok {
			exitEv.Recycle()
			return
		}
		delete(e.enterEvs, exitEv.GetTid())
		ev.exitEv = exitEv

		// Expect ID one lower, otherwise, enter and exit tracepoints
		// don't match up. E.g.:
		// enterEv:SYS_ENTER_OPEN => exitEv:SYS_EXIT_OPEN
		if ev.enterEv.GetTraceId()-1 != ev.exitEv.GetTraceId() {
			ev.tracepointMismatch = true
		}

		switch v := ev.enterEv.(type) {
		case *OpenEvent:
			openEv := ev.enterEv.(*OpenEvent)

			fd := int32(ev.exitEv.(*RetEvent).Ret)
			file := fdFile{fd, string(openEv.Filename[:])}
			if fd >= 0 {
				e.files[fd] = file
			}
			ev.file = file

			comm := string(openEv.Comm[:])
			e.comms[openEv.Tid] = comm

		case *NameEvent:
			nameEvent := ev.enterEv.(*NameEvent)
			ev.file = oldnameNewnameFile{
				oldname: string(nameEvent.Oldname[:]),
				newname: string(nameEvent.Newname[:]),
			}
			ev.comm, _ = e.comms[ev.enterEv.GetTid()]

		case *PathEvent:
			nameEvent := ev.enterEv.(*PathEvent)
			ev.file = pathnameFile{string(nameEvent.Pathname[:])}
			ev.comm, _ = e.comms[ev.enterEv.GetTid()]

		case *FdEvent:
			fd := ev.enterEv.(*FdEvent).Fd
			if file_, ok := e.files[fd]; ok {
				ev.file = file_
				if ev.is(SYS_ENTER_CLOSE) {
					delete(e.files, fd)
				}
			} else {
				ev.file = fdFile{fd, "?"}
			}
			ev.comm, _ = e.comms[ev.enterEv.GetTid()]

		case *NullEvent:
			ev.comm, _ = e.comms[ev.enterEv.GetTid()]

		default:
			panic(fmt.Sprintf("unknown type: %v", v))
		}

		ev.prevPair, _ = e.prevPairs[ev.enterEv.GetTid()]
		ev.calculateDurations()
		e.prevPairs[ev.enterEv.GetTid()] = ev
		e.evCh <- ev
	}

	// Deserialise raw byte stream from BPF ringbuffer.
	go func() {
		defer close(e.evCh)
		for raw := range rawCh {
			switch EventType(raw[0]) {
			case ENTER_OPEN_EVENT:
				enter(NewOpenEvent(raw))
			case EXIT_OPEN_EVENT:
				exit(NewFdEvent(raw))
			case ENTER_FD_EVENT:
				enter(NewFdEvent(raw))
			case EXIT_FD_EVENT:
				exit(NewFdEvent(raw))
			case EXIT_NULL_EVENT:
				exit(NewNullEvent(raw))
			case EXIT_RET_EVENT:
				exit(NewRetEvent(raw))
			case ENTER_NAME_EVENT:
				enter(NewNameEvent(raw))
			case ENTER_PATH_EVENT:
				enter(NewPathEvent(raw))
			default:
				panic(fmt.Sprintf("unhandled event type %v: %v", EventType(raw[0]), raw))
			}
		}
	}()

	return e.evCh
}