Skip to content

Commit e80048a

Browse files
committed
Output: Add json output
This commit adds a command line flag that enables outputting pwru events in json format. Signed-off-by: darox <maderdario@gmail.com>
1 parent 05cc882 commit e80048a

File tree

3 files changed

+235
-123
lines changed

3 files changed

+235
-123
lines changed

internal/pwru/output.go

Lines changed: 177 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package pwru
66

77
import (
8+
"encoding/json"
89
"errors"
910
"fmt"
1011
"io"
@@ -42,6 +43,32 @@ type output struct {
4243
ifaceCache map[uint64]map[uint32]string
4344
}
4445

46+
// outputStructured is a struct to hold the data for the json output
47+
type jsonPrinter struct {
48+
Skb string `json:"skb,omitempty"`
49+
Cpu uint32 `json:"cpu,omitempty"`
50+
Process string `json:"process,omitempty"`
51+
Func string `json:"func,omitempty"`
52+
Time interface{} `json:"time,omitempty"`
53+
Netns uint32 `json:"netns,omitempty"`
54+
Mark uint32 `json:"mark,omitempty"`
55+
Iface string `json:"iface,omitempty"`
56+
Proto uint16 `json:"proto,omitempty"`
57+
Mtu uint32 `json:"mtu,omitempty"`
58+
Len uint32 `json:"len,omitempty"`
59+
Tuple *jsonTuple `json:"tuple,omitempty"`
60+
Stack interface{} `json:"stack,omitempty"`
61+
SkbMetadata interface{} `json:"skb_metadata,omitempty"`
62+
}
63+
64+
type jsonTuple struct {
65+
Saddr string `json:"saddr,omitempty"`
66+
Daddr string `json:"daddr,omitempty"`
67+
Sport uint16 `json:"sport,omitempty"`
68+
Dport uint16 `json:"dport,omitempty"`
69+
Proto uint8 `json:"proto,omitempty"`
70+
}
71+
4572
func NewOutput(flags *Flags, printSkbMap *ebpf.Map, printStackMap *ebpf.Map,
4673
addr2Name Addr2Name, kprobeMulti bool, btfSpec *btf.Spec,
4774
) (*output, error) {
@@ -92,25 +119,93 @@ func (o *output) PrintHeader() {
92119
fmt.Fprintf(o.writer, "\n")
93120
}
94121

95-
func (o *output) Print(event *Event) {
96-
if o.flags.OutputTS == "absolute" {
97-
fmt.Fprintf(o.writer, "%12s ", time.Now().Format(absoluteTS))
122+
// PrintJson prints the event in JSON format
123+
func (o *output) PrintJson(event *Event) {
124+
// crate an instance of the outputStructured struct to hold the data
125+
d := &jsonPrinter{}
126+
127+
// add the data to the struct
128+
d.Skb = fmt.Sprintf("%#x", event.SAddr)
129+
d.Cpu = event.CPU
130+
d.Process = getExecName(int(event.PID))
131+
d.Func = getOutFuncName(o, event, event.Addr)
132+
133+
o.lastSeenSkb[event.SAddr] = event.Timestamp
134+
135+
// add the timestamp to the struct if it is not set to none
136+
if o.flags.OutputTS != "none" {
137+
switch o.flags.OutputTS {
138+
case "absolute":
139+
d.Time = getAbsoluteTs()
140+
case "relative":
141+
d.Time = getRelativeTs(event, o)
142+
case "current":
143+
d.Time = event.Timestamp
144+
}
98145
}
99-
p, err := ps.FindProcess(int(event.PID))
100-
execName := fmt.Sprintf("<empty>(%d)", event.PID)
101-
if err == nil && p != nil {
102-
execName = fmt.Sprintf("%s(%d)", p.ExecutablePath(), event.PID)
146+
147+
if o.flags.OutputMeta {
148+
d.Netns = event.Meta.Netns
149+
d.Mark = event.Meta.Mark
150+
d.Iface = o.getIfaceName(event.Meta.Netns, event.Meta.Ifindex)
151+
d.Proto = byteorder.NetworkToHost16(event.Meta.Proto)
152+
d.Mtu = event.Meta.MTU
153+
d.Len = event.Meta.Len
154+
}
155+
156+
if o.flags.OutputTuple {
157+
t := &jsonTuple{}
158+
t.Saddr = addrToStr(event.Tuple.L3Proto, event.Tuple.Saddr)
159+
t.Daddr = addrToStr(event.Tuple.L3Proto, event.Tuple.Daddr)
160+
t.Sport = byteorder.NetworkToHost16(event.Tuple.Sport)
161+
t.Dport = byteorder.NetworkToHost16(event.Tuple.Dport)
162+
t.Proto = event.Tuple.L4Proto
163+
d.Tuple = t
164+
}
165+
166+
if o.flags.OutputStack && event.PrintStackId > 0 {
167+
d.Stack = getStackData(event, o)
168+
}
169+
170+
if o.flags.OutputSkb {
171+
d.SkbMetadata = getSkbData(event, o)
172+
}
173+
174+
// Create new encoder to write the json to stdout or file depending on the flags
175+
encoder := json.NewEncoder(o.writer)
176+
encoder.SetEscapeHTML(false)
177+
178+
err := encoder.Encode(d)
179+
180+
if err != nil {
181+
log.Fatalf("Error encoding JSON: %s", err)
103182
}
183+
}
184+
185+
func getAbsoluteTs() string {
186+
return time.Now().Format(absoluteTS)
187+
}
188+
189+
func getRelativeTs(event *Event, o *output) uint64 {
104190
ts := event.Timestamp
105-
if o.flags.OutputTS == "relative" {
106-
if last, found := o.lastSeenSkb[event.SAddr]; found {
107-
ts = ts - last
108-
} else {
109-
ts = 0
110-
}
191+
if last, found := o.lastSeenSkb[event.SAddr]; found {
192+
ts = ts - last
193+
} else {
194+
ts = 0
111195
}
112-
var addr uint64
113-
// XXX: not sure why the -1 offset is needed on x86 but not on arm64
196+
return ts
197+
}
198+
199+
func getExecName(pid int) string {
200+
p, err := ps.FindProcess(pid)
201+
execName := fmt.Sprintf("<empty>:(%d)", pid)
202+
if err == nil && p != nil {
203+
return fmt.Sprintf("%s:%d", p.ExecutablePath(), pid)
204+
}
205+
return execName
206+
}
207+
208+
func getAddrByArch(event *Event, o *output) (addr uint64) {
114209
switch runtime.GOARCH {
115210
case "amd64":
116211
addr = event.Addr
@@ -120,7 +215,50 @@ func (o *output) Print(event *Event) {
120215
case "arm64":
121216
addr = event.Addr
122217
}
218+
return addr
219+
}
220+
221+
func getTupleData(event *Event) (tupleData string) {
222+
tupleData = fmt.Sprintf("%s:%d->%s:%d(%s)",
223+
addrToStr(event.Tuple.L3Proto, event.Tuple.Saddr), byteorder.NetworkToHost16(event.Tuple.Sport),
224+
addrToStr(event.Tuple.L3Proto, event.Tuple.Daddr), byteorder.NetworkToHost16(event.Tuple.Dport),
225+
protoToStr(event.Tuple.L4Proto))
226+
return tupleData
227+
}
228+
229+
func getStackData(event *Event, o *output) (stackData string) {
230+
var stack StackData
231+
id := uint32(event.PrintStackId)
232+
if err := o.printStackMap.Lookup(&id, &stack); err == nil {
233+
for _, ip := range stack.IPs {
234+
if ip > 0 {
235+
stackData += fmt.Sprintf("\n%s", o.addr2name.findNearestSym(ip))
236+
}
237+
}
238+
}
239+
_ = o.printStackMap.Delete(&id)
240+
return stackData
241+
}
242+
243+
func getSkbData(event *Event, o *output) (skbData string) {
244+
id := uint32(event.PrintSkbId)
245+
if str, err := o.printSkbMap.LookupBytes(&id); err == nil {
246+
skbData = string(str)
247+
}
248+
return skbData
249+
}
250+
251+
func getMetaData(event *Event, o *output) (metaData string) {
252+
metaData = fmt.Sprintf("netns=%d mark=%#x iface=%s proto=%#04x mtu=%d len=%d",
253+
event.Meta.Netns, event.Meta.Mark,
254+
o.getIfaceName(event.Meta.Netns, event.Meta.Ifindex),
255+
byteorder.NetworkToHost16(event.Meta.Proto), event.Meta.MTU, event.Meta.Len)
256+
return metaData
257+
}
258+
259+
func getOutFuncName(o *output, event *Event, addr uint64) string {
123260
var funcName string
261+
124262
if ksym, ok := o.addr2name.Addr2NameMap[addr]; ok {
125263
funcName = ksym.name
126264
} else if ksym, ok := o.addr2name.Addr2NameMap[addr-4]; runtime.GOARCH == "amd64" && ok {
@@ -151,6 +289,26 @@ func (o *output) Print(event *Event) {
151289
}
152290
}
153291

292+
return outFuncName
293+
}
294+
295+
func (o *output) Print(event *Event) {
296+
if o.flags.OutputTS == "absolute" {
297+
fmt.Fprintf(o.writer, "%12s ", getAbsoluteTs())
298+
}
299+
300+
execName := getExecName(int(event.PID))
301+
302+
ts := event.Timestamp
303+
if o.flags.OutputTS == "relative" {
304+
ts = getRelativeTs(event, o)
305+
}
306+
307+
// XXX: not sure why the -1 offset is needed on x86 but not on arm64
308+
addr := getAddrByArch(event, o)
309+
310+
outFuncName := getOutFuncName(o, event, addr)
311+
154312
fmt.Fprintf(o.writer, "%18s %6s %16s %24s", fmt.Sprintf("%#x", event.SAddr),
155313
fmt.Sprintf("%d", event.CPU), fmt.Sprintf("[%s]", execName), outFuncName)
156314
if o.flags.OutputTS != "none" {
@@ -159,37 +317,19 @@ func (o *output) Print(event *Event) {
159317
o.lastSeenSkb[event.SAddr] = event.Timestamp
160318

161319
if o.flags.OutputMeta {
162-
fmt.Fprintf(o.writer, " netns=%d mark=%#x iface=%s proto=%#04x mtu=%d len=%d",
163-
event.Meta.Netns, event.Meta.Mark,
164-
o.getIfaceName(event.Meta.Netns, event.Meta.Ifindex),
165-
byteorder.NetworkToHost16(event.Meta.Proto), event.Meta.MTU, event.Meta.Len)
320+
fmt.Fprintf(o.writer, "%s", getMetaData(event, o))
166321
}
167322

168323
if o.flags.OutputTuple {
169-
fmt.Fprintf(o.writer, " %s:%d->%s:%d(%s)",
170-
addrToStr(event.Tuple.L3Proto, event.Tuple.Saddr), byteorder.NetworkToHost16(event.Tuple.Sport),
171-
addrToStr(event.Tuple.L3Proto, event.Tuple.Daddr), byteorder.NetworkToHost16(event.Tuple.Dport),
172-
protoToStr(event.Tuple.L4Proto))
324+
fmt.Fprintf(o.writer, "%s", getTupleData(event))
173325
}
174326

175327
if o.flags.OutputStack && event.PrintStackId > 0 {
176-
var stack StackData
177-
id := uint32(event.PrintStackId)
178-
if err := o.printStackMap.Lookup(&id, &stack); err == nil {
179-
for _, ip := range stack.IPs {
180-
if ip > 0 {
181-
fmt.Fprintf(o.writer, "\n%s", o.addr2name.findNearestSym(ip))
182-
}
183-
}
184-
}
185-
_ = o.printStackMap.Delete(&id)
328+
fmt.Fprintf(o.writer, "%s", getStackData(event, o))
186329
}
187330

188331
if o.flags.OutputSkb {
189-
id := uint32(event.PrintSkbId)
190-
if str, err := o.printSkbMap.LookupBytes(&id); err == nil {
191-
fmt.Fprintf(o.writer, "\n%s", string(str))
192-
}
332+
fmt.Fprintf(o.writer, "%s", getSkbData(event, o))
193333
}
194334

195335
fmt.Fprintln(o.writer)

internal/pwru/types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type Flags struct {
4040
OutputStack bool
4141
OutputLimitLines uint64
4242
OutputFile string
43+
OutputJson bool
4344

4445
KMods []string
4546
AllKMods bool
@@ -70,6 +71,8 @@ func (f *Flags) SetFlags() {
7071

7172
flag.StringVar(&f.OutputFile, "output-file", "", "write traces to file")
7273

74+
flag.BoolVar(&f.OutputJson, "output-json", false, "output traces in JSON format")
75+
7376
flag.StringVar(&f.ReadyFile, "ready-file", "", "create file after all BPF progs are attached")
7477
flag.Lookup("ready-file").Hidden = true
7578

0 commit comments

Comments
 (0)