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
157
158
159
160
161
162
163
164
|
//go:build amd64
package main
import (
"bytes"
"encoding/binary"
"errors"
"flag"
"fmt"
"log"
"os"
"os/signal"
"path/filepath"
"syscall"
"github.com/cilium/ebpf/link"
"github.com/cilium/ebpf/perf"
"github.com/cilium/ebpf/rlimit"
"golang.org/x/sys/unix"
)
// 这个是bpf2go 的生成代码
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -target amd64 -type event bpf dbstats.c -- -I../headers
const (
symbol = "exec_simple_query"
)
var binPath string
var logPath string
var logFile *os.File
var pid int
var slow int
func init() {
flag.StringVar(&binPath, "P", "", "the path of postgres")
flag.IntVar(&pid, "p", 0, "pid")
flag.IntVar(&slow, "t", 200, "the threshold of slow query")
flag.StringVar(&logPath, "l", "", "the log output file path")
}
func Config() bool {
flag.Parse()
if binPath == "" && pid == 0 {
fmt.Printf("invalid argument path [%s] pid %d", binPath, pid)
return true
}
if binPath == "" {
if path, err := filepath.EvalSymlinks(fmt.Sprintf("/proc/%d/exe", pid)); err != nil {
fmt.Printf("pid %d err %v", pid, err)
return true
} else {
binPath = path
}
}
if logPath != "" {
path, err := filepath.Abs(logPath)
if err != nil {
fmt.Printf("get abs path failed %v\n", path)
}
if file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666); err != nil {
fmt.Printf("open log file failed %v\n", err)
} else {
logFile = file
log.SetOutput(file)
}
}
if slow < 0 || slow > 2000 {
return true
}
return false
}
func main() {
shouldReturn := Config()
if shouldReturn {
return
}
defer func(file *os.File) {
if file != nil {
file.Close()
}
}(logFile)
stopper := make(chan os.Signal, 1)
signal.Notify(stopper, os.Interrupt, syscall.SIGTERM)
// Allow the current process to lock memory for eBPF resources.
if err := rlimit.RemoveMemlock(); err != nil {
log.Fatal(err)
}
// Load pre-compiled programs and maps into the kernel.
objs := bpfObjects{}
if err := loadBpfObjects(&objs, nil); err != nil {
log.Fatalf("loading objects: [%v]", err)
}
defer objs.Close()
// 200ms 是慢查询上限
objs.SlowMap.Put(0, slow)
// Open an ELF binary and read its symbols.
ex, err := link.OpenExecutable(binPath)
if err != nil {
log.Fatalf("opening executable: %s", err)
}
// Open a Uretprobe at the exit point of the symbol and attach
// the pre-compiled eBPF program to it.
up, err := ex.Uretprobe(symbol, objs.UretprobeExecSimpleQuery, nil)
if err != nil {
log.Fatalf("creating uretprobe: %s", err)
}
defer up.Close()
do, err := ex.Uprobe(symbol, objs.UprobeExecSimpleQuery, nil)
if err != nil {
log.Fatalf("creating uprobe: %s", err)
}
defer do.Close()
// Open a perf event reader from userspace on the PERF_EVENT_ARRAY map
// described in the eBPF C program.
rd, err := perf.NewReader(objs.Events, os.Getpagesize())
if err != nil {
log.Fatalf("creating perf event reader: %s", err)
}
defer rd.Close()
go func() {
// Wait for a signal and close the perf reader,
// which will interrupt rd.Read() and make the program exit.
<-stopper
log.Println("Received signal, exiting program..")
if err := rd.Close(); err != nil {
log.Fatalf("closing perf event reader: %s", err)
}
}()
log.Printf("Listening for events..")
// bpfEvent is generated by bpf2go.
var event bpfEvent
for {
record, err := rd.Read()
if err != nil {
if errors.Is(err, perf.ErrClosed) {
return
}
log.Printf("reading from perf event reader: %s", err)
continue
}
if record.LostSamples != 0 {
log.Printf("perf event ring buffer full, dropped %d samples", record.LostSamples)
continue
}
// Parse the perf event entry into a bpfEvent structure.
if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil {
log.Printf("parsing perf event: %s", err)
continue
}
log.Printf("%s:%f/ms\n", unix.ByteSliceToString(event.Cmd[:]), float64(event.Timestamp)/1000000.0)
}
}
|