aboutsummaryrefslogtreecommitdiffstats
path: root/prog/decodeexec.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2024-04-15 12:36:36 +0200
committerDmitry Vyukov <dvyukov@google.com>2024-04-15 13:23:33 +0000
commitec1d9df37dba4a17065b091bbe9e03c9635cd0dc (patch)
treed1c9767c3330e3704efd1d4fac8a007dd8be7df4 /prog/decodeexec.go
parentb6f969371dc1e824c76636c0d3754a91b55d69b9 (diff)
prog: profile what consumes space in exec encoding
Allow to profile how many bytes are consumed for what in the exec encoding. The profile shows there are not many opportunities left. 53% are consumed by data blobs. 13% for const args. 18% for non-arg things (syscall number, copyout index, props, etc).
Diffstat (limited to 'prog/decodeexec.go')
-rw-r--r--prog/decodeexec.go69
1 files changed, 42 insertions, 27 deletions
diff --git a/prog/decodeexec.go b/prog/decodeexec.go
index 2113d24c7..2356e051b 100644
--- a/prog/decodeexec.go
+++ b/prog/decodeexec.go
@@ -7,6 +7,7 @@ import (
"encoding/binary"
"fmt"
"reflect"
+ "strings"
)
type ExecProg struct {
@@ -71,8 +72,8 @@ type ExecCsumChunk struct {
Size uint64
}
-func (target *Target) DeserializeExec(exec []byte) (ExecProg, error) {
- dec := &execDecoder{target: target, data: exec}
+func (target *Target) DeserializeExec(exec []byte, stats map[string]int) (ExecProg, error) {
+ dec := &execDecoder{target: target, data: exec, stats: stats}
dec.parse()
if dec.err != nil {
return ExecProg{}, dec.err
@@ -96,22 +97,23 @@ type execDecoder struct {
vars []uint64
call ExecCall
calls []ExecCall
+ stats map[string]int
}
func (dec *execDecoder) parse() {
for dec.err == nil {
- switch instr := dec.read(); instr {
+ switch instr := dec.read("instr/opcode"); instr {
case execInstrCopyin:
dec.commitCall()
dec.call.Copyin = append(dec.call.Copyin, ExecCopyin{
- Addr: dec.read() + dec.target.DataOffset,
+ Addr: dec.read("instr/copyin") + dec.target.DataOffset,
Arg: dec.readArg(),
})
case execInstrCopyout:
dec.call.Copyout = append(dec.call.Copyout, ExecCopyout{
- Index: dec.read(),
- Addr: dec.read() + dec.target.DataOffset,
- Size: dec.read(),
+ Index: dec.read("instr/copyout/index"),
+ Addr: dec.read("instr/copyout/addr") + dec.target.DataOffset,
+ Size: dec.read("instr/copyout/size"),
})
case execInstrEOF:
dec.commitCall()
@@ -126,8 +128,8 @@ func (dec *execDecoder) parse() {
return
}
dec.call.Meta = dec.target.Syscalls[instr]
- dec.call.Index = dec.read()
- for i := dec.read(); i > 0; i-- {
+ dec.call.Index = dec.read("instr/index")
+ for i := dec.read("instr/nargs"); i > 0; i-- {
switch arg := dec.readArg(); arg.(type) {
case ExecArgConst, ExecArgResult:
dec.call.Args = append(dec.call.Args, arg)
@@ -142,7 +144,7 @@ func (dec *execDecoder) parse() {
func (dec *execDecoder) readCallProps(props *CallProps) {
props.ForeachProp(func(_, _ string, value reflect.Value) {
- arg := dec.read()
+ arg := dec.read("call prop")
switch kind := value.Kind(); kind {
case reflect.Int:
value.SetInt(int64(arg))
@@ -157,11 +159,11 @@ func (dec *execDecoder) readCallProps(props *CallProps) {
}
func (dec *execDecoder) readArg() ExecArg {
- switch typ := dec.read(); typ {
+ switch typ := dec.read("arg/type"); typ {
case execArgConst:
- meta := dec.read()
+ meta := dec.read("arg/const/meta")
return ExecArgConst{
- Value: dec.read(),
+ Value: dec.read("arg/const/value"),
Size: meta & 0xff,
Format: BinaryFormat((meta >> 8) & 0xff),
BitfieldOffset: (meta >> 16) & 0xff,
@@ -176,18 +178,18 @@ func (dec *execDecoder) readArg() ExecArg {
size = 8
}
return ExecArgConst{
- Value: dec.read() + dec.target.DataOffset,
+ Value: dec.read("arg/addr") + dec.target.DataOffset,
Size: uint64(size),
}
case execArgResult:
- meta := dec.read()
+ meta := dec.read("arg/result/meta")
arg := ExecArgResult{
Size: meta & 0xff,
Format: BinaryFormat((meta >> 8) & 0xff),
- Index: dec.read(),
- DivOp: dec.read(),
- AddOp: dec.read(),
- Default: dec.read(),
+ Index: dec.read("arg/result/index"),
+ DivOp: dec.read("arg/result/divop"),
+ AddOp: dec.read("arg/result/addop"),
+ Default: dec.read("arg/result/default"),
}
for uint64(len(dec.vars)) <= arg.Index {
dec.vars = append(dec.vars, 0)
@@ -195,22 +197,23 @@ func (dec *execDecoder) readArg() ExecArg {
dec.vars[arg.Index] = arg.Default
return arg
case execArgData:
- flags := dec.read()
+ flags := dec.read("arg/data/size")
size := flags & ^execArgDataReadable
+ dec.addStat("arg/data/blob", int(size))
readable := flags&execArgDataReadable != 0
return ExecArgData{
Data: dec.readBlob(size),
Readable: readable,
}
case execArgCsum:
- size := dec.read()
- switch kind := dec.read(); kind {
+ size := dec.read("arg/csum/size")
+ switch kind := dec.read("arg/csum/kind"); kind {
case ExecArgCsumInet:
- chunks := make([]ExecCsumChunk, dec.read())
+ chunks := make([]ExecCsumChunk, dec.read("arg/csum/chunks"))
for i := range chunks {
- kind := dec.read()
- addr := dec.read()
- size := dec.read()
+ kind := dec.read("arg/csum/chunk/kind")
+ addr := dec.read("arg/csum/chunk/addr")
+ size := dec.read("arg/csum/chunk/size")
if kind == ExecArgCsumChunkData {
addr += dec.target.DataOffset
}
@@ -235,7 +238,7 @@ func (dec *execDecoder) readArg() ExecArg {
}
}
-func (dec *execDecoder) read() uint64 {
+func (dec *execDecoder) read(stat string) uint64 {
if dec.err != nil {
return 0
}
@@ -244,6 +247,7 @@ func (dec *execDecoder) read() uint64 {
dec.setErr(fmt.Errorf("exec program overflow"))
return 0
}
+ dec.addStat(stat, n)
dec.data = dec.data[n:]
return uint64(v)
}
@@ -281,3 +285,14 @@ func (dec *execDecoder) commitCall() {
dec.calls = append(dec.calls, dec.call)
dec.call = ExecCall{}
}
+
+func (dec *execDecoder) addStat(stat string, n int) {
+ if dec.stats == nil {
+ return
+ }
+ prefix := ""
+ for _, part := range strings.Split(stat, "/") {
+ dec.stats[prefix+part] += n
+ prefix += part + "/"
+ }
+}