From e20f9912bdd5c90e82bccd14ace1fa6c67fb9724 Mon Sep 17 00:00:00 2001 From: Ivan Babrou Date: Tue, 24 Oct 2023 18:59:41 -0700 Subject: [PATCH] Add kstack decoder From the example: ivan@vm:~/projects/ebpf_exporter$ curl -s http://ip6-localhost:9435/metrics | fgrep accessed ebpf_exporter_ebpf_program_info{config="kstack",id="1803",program="mark_page_accessed",tag="4fe6d77dbc5615bc"} 1 # HELP ebpf_exporter_mark_page_accessed_total Total number of calls to mark_page_accessed by kstack # TYPE ebpf_exporter_mark_page_accessed_total counter ebpf_exporter_mark_page_accessed_total{kstack="mark_page_accessed\nfollow_page_pte\nfollow_page_mask\n__get_user_pages\nget_user_pages_remote\nget_arg_page\ncopy_string_kernel\ndo_execveat_common\n__arm64_sys_execve\ninvoke_syscall.constprop.0\ndo_el0_svc\nel0_svc\nel0t_64_sync_handler\nel0t_64_sync"} 5 ebpf_exporter_mark_page_accessed_total{kstack="mark_page_accessed\nfollow_page_pte\nfollow_page_mask\n__get_user_pages\nget_user_pages_remote\nget_arg_page\ncopy_strings\ndo_execveat_common\n__arm64_sys_execve\ninvoke_syscall.constprop.0\ndo_el0_svc\nel0_svc\nel0t_64_sync_handler\nel0t_64_sync"} 10 ebpf_exporter_mark_page_accessed_total{kstack="mark_page_accessed\nunmap_page_range\nunmap_vmas\nexit_mmap\n__mmput\nmmput\nbegin_new_exec\nload_elf_binary\nbprm_execve\ndo_execveat_common\n__arm64_sys_execve\ninvoke_syscall.constprop.0\ndo_el0_svc\nel0_svc\nel0t_64_sync_handler\nel0t_64_sync"} 914 ebpf_exporter_mark_page_accessed_total{kstack="mark_page_accessed\nunmap_page_range\nunmap_vmas\nunmap_region.constprop.0\ndo_vmi_align_munmap\ndo_vmi_munmap\n__vm_munmap\n__arm64_sys_munmap\ninvoke_syscall.constprop.0\ndo_el0_svc\nel0_svc\nel0t_64_sync_handler\nel0t_64_sync"} 15 This isn't as useful in metrics, but it would be more natural in tracing. --- .vscode/config-schema.yaml | 17 +++- decoder/decoder.go | 23 +++-- decoder/kstack.go | 38 ++++++++ decoder/kstack_test.go | 95 ++++++++++++++++++++ decoder/ksym.go | 22 ++--- decoder/ksym_test.go | 50 ++++++++--- examples/kstack.bpf.c | 35 ++++++++ examples/kstack.yaml | 9 ++ kallsyms/decoder.go | 175 +++++++++++++++++++++++++++++++++++++ kallsyms/decoder_test.go | 81 +++++++++++++++++ kallsyms/kallsyms.txt | 96 ++++++++++++++++++++ 11 files changed, 604 insertions(+), 37 deletions(-) create mode 100644 decoder/kstack.go create mode 100644 decoder/kstack_test.go create mode 100644 examples/kstack.bpf.c create mode 100644 examples/kstack.yaml create mode 100644 kallsyms/decoder.go create mode 100644 kallsyms/decoder_test.go create mode 100644 kallsyms/kallsyms.txt diff --git a/.vscode/config-schema.yaml b/.vscode/config-schema.yaml index 39baf7d9..81f1a319 100644 --- a/.vscode/config-schema.yaml +++ b/.vscode/config-schema.yaml @@ -94,7 +94,22 @@ definitions: - name properties: name: - type: string + enum: + - cgroup + - dname + - inet_ip + - kstack + - ksym + - majorminor + - pci_class + - pci_device + - pci_subclass + - pci_vendor + - regexp + - static_map + - string + - syscall + - uint static_map: type: object allow_unknown: diff --git a/decoder/decoder.go b/decoder/decoder.go index 0cbf6d54..ce081b7c 100644 --- a/decoder/decoder.go +++ b/decoder/decoder.go @@ -6,6 +6,7 @@ import ( "sync" "github.com/cloudflare/ebpf_exporter/v2/config" + "github.com/cloudflare/ebpf_exporter/v2/kallsyms" ) // ErrSkipLabelSet instructs exporter to skip label set @@ -31,22 +32,28 @@ func NewSet() (*Set, error) { return nil, fmt.Errorf("error creating cgroup decoder: %v", err) } + ksym, err := kallsyms.NewDecoder("/proc/kallsyms") + if err != nil { + return nil, fmt.Errorf("error creating ksym decoder: %v", err) + } + return &Set{ decoders: map[string]Decoder{ "cgroup": cgroup, - "ksym": &KSym{}, - "majorminor": &MajorMinor{}, - "regexp": &Regexp{}, - "static_map": &StaticMap{}, - "string": &String{}, "dname": &Dname{}, - "uint": &UInt{}, "inet_ip": &InetIP{}, - "pci_vendor": &PCIVendor{}, - "pci_device": &PCIDevice{}, + "kstack": &KStack{ksym}, + "ksym": &KSym{ksym}, + "majorminor": &MajorMinor{}, "pci_class": &PCIClass{}, + "pci_device": &PCIDevice{}, "pci_subclass": &PCISubClass{}, + "pci_vendor": &PCIVendor{}, + "regexp": &Regexp{}, + "static_map": &StaticMap{}, + "string": &String{}, "syscall": &Syscall{}, + "uint": &UInt{}, }, }, nil } diff --git a/decoder/kstack.go b/decoder/kstack.go new file mode 100644 index 00000000..dfeee16b --- /dev/null +++ b/decoder/kstack.go @@ -0,0 +1,38 @@ +package decoder + +import ( + "strings" + + "github.com/cloudflare/ebpf_exporter/v2/config" + "github.com/cloudflare/ebpf_exporter/v2/kallsyms" + "github.com/cloudflare/ebpf_exporter/v2/util" +) + +// KStack is a decoder that transforms an array of kernel frame addresses to a newline separated stack of symbols +type KStack struct { + decoder *kallsyms.Decoder +} + +// Decode transforms an array of kernel frame addresses to a newline separated stack of symbols +func (k *KStack) Decode(in []byte, _ config.Decoder) ([]byte, error) { + addrs := []uintptr{} + for off := 0; off < len(in); off += 8 { + ptr := util.GetHostByteOrder().Uint64(in[off : off+8]) + if ptr == 0 { + break + } + + addrs = append(addrs, uintptr(ptr)) + } + + stack := make([]string, len(addrs)) + for i, frame := range k.decoder.Stack(addrs) { + if frame.Sym == "" { + stack[i] = "??" + } else { + stack[i] = frame.Sym + } + } + + return []byte(strings.Join(stack, "\n")), nil +} diff --git a/decoder/kstack_test.go b/decoder/kstack_test.go new file mode 100644 index 00000000..2caeac41 --- /dev/null +++ b/decoder/kstack_test.go @@ -0,0 +1,95 @@ +package decoder + +import ( + "bytes" + "os" + "testing" + + "github.com/cloudflare/ebpf_exporter/v2/config" + "github.com/cloudflare/ebpf_exporter/v2/kallsyms" +) + +func TestKStackDecoder(t *testing.T) { + cases := []struct { + in []byte + out []byte + }{ + { + in: []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + out: []byte(""), + }, + { + in: []byte{ + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + out: []byte("one\none\ntwo"), + }, + { + in: []byte{ + 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + out: []byte("three\ntwo\ntwo"), + }, + { + in: []byte{ + 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + out: []byte("three\nzero"), + }, + { + in: []byte{ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + out: []byte("??\nthree\n??"), + }, + } + + fd, err := os.CreateTemp("", "kallsyms") + if err != nil { + t.Fatalf("Error creating temporary file for kallsyms: %v", err) + } + + defer os.Remove(fd.Name()) + + _, err = fd.WriteString("0000000000000004 T one\n0000000000000006 T two\n00000000000000aa T three\n") + if err != nil { + t.Fatalf("Error writing fake kallsyms data to %q: %v", fd.Name(), err) + } + + decoder, err := kallsyms.NewDecoder(fd.Name()) + if err != nil { + t.Fatalf("Error creating ksym decoder for %q: %v", fd.Name(), err) + } + + _, err = fd.WriteString("0000000000000002 T zero\n") + if err != nil { + t.Fatalf("Error writing additional fake kallsyms data to %q: %v", fd.Name(), err) + } + + d := KStack{decoder} + + for _, c := range cases { + out, err := d.Decode(c.in, config.Decoder{}) + if err != nil { + t.Errorf("Error decoding %#v: %s", c.in, err) + } + + if !bytes.Equal(out, c.out) { + t.Errorf("Expected %q, got %q", c.out, out) + } + } +} diff --git a/decoder/ksym.go b/decoder/ksym.go index 5ca36c83..e8031f0c 100644 --- a/decoder/ksym.go +++ b/decoder/ksym.go @@ -4,31 +4,23 @@ import ( "fmt" "github.com/cloudflare/ebpf_exporter/v2/config" + "github.com/cloudflare/ebpf_exporter/v2/kallsyms" "github.com/cloudflare/ebpf_exporter/v2/util" - "github.com/iovisor/gobpf/pkg/ksym" ) // KSym is a decoder that transforms kernel address to a function name type KSym struct { - cache map[string][]byte + decoder *kallsyms.Decoder } // Decode transforms kernel address to a function name func (k *KSym) Decode(in []byte, _ config.Decoder) ([]byte, error) { - if k.cache == nil { - k.cache = map[string][]byte{} - } - - addr := fmt.Sprintf("%x", util.GetHostByteOrder().Uint64(in)) - - if _, ok := k.cache[addr]; !ok { - name, err := ksym.Ksym(addr) - if err != nil { - return []byte(fmt.Sprintf("unknown_addr:0x%s", addr)), nil - } + ptr := util.GetHostByteOrder().Uint64(in) - k.cache[addr] = []byte(name) + sym := k.decoder.Sym(uintptr(ptr)) + if sym == "" { + sym = fmt.Sprintf("unknown_addr:0x%x", ptr) } - return k.cache[addr], nil + return []byte(sym), nil } diff --git a/decoder/ksym_test.go b/decoder/ksym_test.go index 6d7194b8..daa4ae50 100644 --- a/decoder/ksym_test.go +++ b/decoder/ksym_test.go @@ -2,39 +2,63 @@ package decoder import ( "bytes" + "os" "testing" "github.com/cloudflare/ebpf_exporter/v2/config" + "github.com/cloudflare/ebpf_exporter/v2/kallsyms" ) func TestKsymDecoder(t *testing.T) { cases := []struct { - in []byte - cache map[string][]byte - out []byte + in []byte + out []byte }{ { - in: []byte{0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - cache: map[string][]byte{"6": []byte("call_six")}, - out: []byte("call_six"), + in: []byte{0x28, 0x08, 0xd9, 0x19, 0xeb, 0xff, 0xff, 0xff}, + out: []byte("unknown_addr:0xffffffeb19d90828"), }, { - in: []byte{0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - cache: map[string][]byte{"7": []byte("call_seven")}, - out: []byte("unknown_addr:0x6"), + in: []byte{0x30, 0x08, 0xd9, 0x19, 0xeb, 0xff, 0xff, 0xff}, + out: []byte("pipe_lock"), + }, + { + in: []byte{0x70, 0x08, 0xd9, 0x19, 0xeb, 0xff, 0xff, 0xff}, + out: []byte("pipe_unlock"), + }, + { + in: []byte{0x78, 0x08, 0xd9, 0x19, 0xeb, 0xff, 0xff, 0xff}, + out: []byte("unknown_addr:0xffffffeb19d90878"), }, } - for _, c := range cases { - d := &KSym{cache: c.cache} + fd, err := os.CreateTemp("", "kallsyms") + if err != nil { + t.Fatalf("Error creating temporary file for kallsyms: %v", err) + } + + defer os.Remove(fd.Name()) + _, err = fd.WriteString("ffffffeb19d90830 T pipe_lock\nffffffeb19d90870 T pipe_unlock\n") + if err != nil { + t.Fatalf("Error writing fake kallsyms data to %q: %v", fd.Name(), err) + } + + decoder, err := kallsyms.NewDecoder(fd.Name()) + if err != nil { + t.Fatalf("Error creating ksym decoder for %q: %v", fd.Name(), err) + } + + d := KSym{decoder} + + for _, c := range cases { out, err := d.Decode(c.in, config.Decoder{}) if err != nil { - t.Errorf("Error decoding %#v with cache set to %#v: %s", c.in, c.cache, err) + t.Errorf("Error decoding %#v: %s", c.in, err) } if !bytes.Equal(out, c.out) { - t.Errorf("Expected %s, got %s", c.out, out) + t.Errorf("Expected %q, got %q", c.out, out) } } } diff --git a/examples/kstack.bpf.c b/examples/kstack.bpf.c new file mode 100644 index 00000000..23b8dd00 --- /dev/null +++ b/examples/kstack.bpf.c @@ -0,0 +1,35 @@ +#include +#include +#include "maps.bpf.h" + +#define MAX_STACK_DEPTH 20 + +// Skipping 3 frames off the top as they are just bpf trampoline +#define SKIP_FRAMES (3 & BPF_F_SKIP_FIELD_MASK) + +struct key_t { + u64 kstack[MAX_STACK_DEPTH]; +}; + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, 128); + __type(key, struct key_t); + __type(value, u64); +} mark_page_accessed_total SEC(".maps"); + +SEC("fentry/mark_page_accessed") +int mark_page_accessed(struct pt_regs *ctx) +{ + struct key_t key = {}; + + if (bpf_get_stack(ctx, &key.kstack, sizeof(key.kstack), SKIP_FRAMES) < 0) { + return 0; + } + + increment_map(&mark_page_accessed_total, &key, 1); + + return 0; +} + +char LICENSE[] SEC("license") = "GPL"; diff --git a/examples/kstack.yaml b/examples/kstack.yaml new file mode 100644 index 00000000..526dce27 --- /dev/null +++ b/examples/kstack.yaml @@ -0,0 +1,9 @@ +metrics: + counters: + - name: mark_page_accessed_total + help: Total number of calls to mark_page_accessed by kstack + labels: + - name: kstack + size: 160 + decoders: + - name: kstack diff --git a/kallsyms/decoder.go b/kallsyms/decoder.go new file mode 100644 index 00000000..011f796c --- /dev/null +++ b/kallsyms/decoder.go @@ -0,0 +1,175 @@ +package kallsyms + +import ( + "bufio" + "fmt" + "os" + "sort" + "strconv" + "strings" + "sync" +) + +// Addr represents a ksym addr pair of a pointer and its symbol +type Addr struct { + Ptr uintptr + Sym string +} + +// Decoder decodes ksym pointers to their symbols +type Decoder struct { + mu sync.Mutex + path string + addrs []Addr + found map[uintptr]string +} + +// NewDecoder creates a new kallsyms decoder for a given kallsyms path (usually /proc/kallsyms) +func NewDecoder(path string) (*Decoder, error) { + d := &Decoder{path: path, found: map[uintptr]string{}} + + err := d.refreshMapping() + if err != nil { + return nil, err + } + + return d, nil +} + +// refreshMapping re-reads kallsyms and rebuilds internal mapping +func (d *Decoder) refreshMapping() error { + d.addrs = []Addr{} + d.found = map[uintptr]string{} + + fd, err := os.Open(d.path) + if err != nil { + return fmt.Errorf("error opening kallsyms at %q: %v", d.path, err) + } + + defer fd.Close() + + scanner := bufio.NewScanner(fd) + for scanner.Scan() { + parts := strings.Split(scanner.Text(), " ") + if len(parts) < 3 { + continue + } + + ptr, err := strconv.ParseUint(parts[0], 16, 64) + if err != nil { + return fmt.Errorf("error parsing kallsyms addr %q: %v", parts[0], err) + } + + d.addrs = append(d.addrs, Addr{ + Ptr: uintptr(ptr), + Sym: parts[2], + }) + } + + err = scanner.Err() + if err != nil { + return fmt.Errorf("error scanning kallsyms from %q: %v", d.path, scanner.Err()) + } + + sort.Slice(d.addrs, func(i, j int) bool { + return d.addrs[i].Ptr < d.addrs[j].Ptr + }) + + return nil +} + +// findFirstBeforePtrLocked finds the the first addr that precedes the given pointer +func (d *Decoder) findFirstBeforePtrLocked(ptr uintptr) Addr { + start := 0 + end := len(d.addrs) - 1 + mid := 0 + + for { + if start >= end { + break + } + + mid = start + (end-start+1)/2 + + if d.addrs[mid].Ptr <= ptr { + start = mid + } else { + end = mid - 1 + } + } + + if start == end && d.addrs[start].Ptr <= ptr { + return d.addrs[start] + } + + return Addr{} +} + +// fillStackLocked fills the given stack of pointers with the corresponding addresses +func (d *Decoder) fillStackLocked(stack []Addr) bool { + filled := true + + for i := range stack { + stack[i].Sym = d.findFirstBeforePtrLocked(stack[i].Ptr).Sym + + if stack[i].Sym == "" { + filled = false + } + } + + return filled +} + +// Stack returns the decoded stack for a given array of frame pointers +func (d *Decoder) Stack(addrs []uintptr) []Addr { + d.mu.Lock() + defer d.mu.Unlock() + + stack := make([]Addr, len(addrs)) + for i, ptr := range addrs { + stack[i].Ptr = ptr + } + + if !d.fillStackLocked(stack) { + err := d.refreshMapping() + if err != nil { + panic(err) + } + + d.fillStackLocked(stack) + } + + return stack +} + +// saveSymLocked resolves the kernel symbol at the given address +func (d *Decoder) saveSymLookupLocked(ptr uintptr) bool { + addr := d.findFirstBeforePtrLocked(ptr) + if addr.Ptr == ptr { + d.found[ptr] = addr.Sym + return true + } + + return false +} + +// Sym returns the kernel symbol at the given address +func (d *Decoder) Sym(ptr uintptr) string { + d.mu.Lock() + defer d.mu.Unlock() + + if found, ok := d.found[ptr]; ok { + return found + } + + if !d.saveSymLookupLocked(ptr) { + err := d.refreshMapping() + if err != nil { + panic(err) + } + + d.saveSymLookupLocked(ptr) + } + + return d.found[ptr] +} diff --git a/kallsyms/decoder_test.go b/kallsyms/decoder_test.go new file mode 100644 index 00000000..07de9826 --- /dev/null +++ b/kallsyms/decoder_test.go @@ -0,0 +1,81 @@ +package kallsyms + +import ( + "reflect" + "testing" +) + +// kallsyms.txt is created from the stack below with the filter to wilt it down: +// +// sudo cat /proc/kallsyms | grep -C3 -E ' (el0t_64_sync|el0t_64_sync_handler|el0_svc|do_el0_svc|invoke_syscall.constprop.0|__arm64_sys_execve|do_execveat_common|bprm_execve|load_elf_binary|begin_new_exec|mmput|__mmput|exit_mmap|unmap_vmas|unmap_page_range|mark_page_accessed|bpf_trampoline_6442507883.*|bpf_prog_.*_mark_page_accessed)$' > ksym/kallsyms.txt + +func TestStack(t *testing.T) { + stacks := [][]Addr{ + { + {0xffffffc0801cee9c, "bpf_prog_9a4f2895a09f572a_mark_page_accessed\t[bpf]"}, + {0xffffffc0801cee9c, "bpf_prog_9a4f2895a09f572a_mark_page_accessed\t[bpf]"}, + {0xffffffc08012d06c, "bpf_trampoline_6442507883\t[bpf]"}, + {0xffffffeb19cadfc8, "mark_page_accessed"}, + {0xffffffeb19cf6148, "unmap_page_range"}, + {0xffffffeb19cf6694, "unmap_vmas"}, + {0xffffffeb19d04a60, "exit_mmap"}, + {0xffffffeb19a8a5fc, "__mmput"}, + {0xffffffeb19a8a7fc, "mmput"}, + {0xffffffeb19d900e4, "begin_new_exec"}, + {0xffffffeb19e06ba0, "load_elf_binary"}, + {0xffffffeb19d8f17c, "bprm_execve"}, + {0xffffffeb19d8f754, "do_execveat_common"}, + {0xffffffeb19d8f83c, "__arm64_sys_execve"}, + {0xffffffeb19a28a04, "invoke_syscall.constprop.0"}, + {0xffffffeb19a28afc, "do_el0_svc"}, + {0xffffffeb1a34d2d8, "el0_svc"}, + {0xffffffeb1a34d768, "el0t_64_sync_handler"}, + {0xffffffeb19a11558, "el0t_64_sync"}, + }, + } + + d, err := NewDecoder("kallsyms.txt") + if err != nil { + t.Fatalf("Error creating ksym decoder: %v", err) + } + + for i, stack := range stacks { + addrs := make([]uintptr, len(stack)) + for j, addr := range stack { + addrs[j] = addr.Ptr + } + + decoded := d.Stack(addrs) + + if !reflect.DeepEqual(stack, decoded) { + t.Errorf("expected decoded stack %#v, got %#v", stack, decoded) + + for j, addr := range stack { + if !reflect.DeepEqual(addr, decoded[j]) { + t.Errorf("Expected stack %d at position %d to be %#v, got %#v", i, j, addr, decoded[j]) + } + } + } + } +} + +func TestSymLookup(t *testing.T) { + addrs := []Addr{ + {0xffffffeb19a8a478, ""}, + {0xffffffeb19a8a480, "__pidfd_prepare"}, + {0xffffffeb19a8a482, ""}, + {0xffffffeb19cadfc0, "mark_page_accessed"}, + } + + d, err := NewDecoder("kallsyms.txt") + if err != nil { + t.Fatalf("Error creating ksym decoder: %v", err) + } + + for _, addr := range addrs { + sym := d.Sym(addr.Ptr) + if sym != addr.Sym { + t.Errorf("Expected addr 0x%x to resolve to %q, got %q instead", addr.Ptr, addr.Sym, sym) + } + } +} diff --git a/kallsyms/kallsyms.txt b/kallsyms/kallsyms.txt new file mode 100644 index 00000000..6322f89c --- /dev/null +++ b/kallsyms/kallsyms.txt @@ -0,0 +1,96 @@ +ffffffeb19a11288 t el1h_64_irq +ffffffeb19a112f0 t el1h_64_fiq +ffffffeb19a11358 t el1h_64_error +ffffffeb19a113c0 t el0t_64_sync +ffffffeb19a11560 t el0t_64_irq +ffffffeb19a11700 t el0t_64_fiq +ffffffeb19a118a0 t el0t_64_error +-- +ffffffeb19a28888 T __arm_smccc_hvc +ffffffeb19a288c8 T arm_smccc_1_2_hvc +ffffffeb19a28930 T arm_smccc_1_2_smc +ffffffeb19a289a8 t invoke_syscall.constprop.0 +ffffffeb19a28ab0 T do_el0_svc +ffffffeb19a28b90 t update_mitigation_state +ffffffeb19a28bf0 t spectre_v2_get_cpu_fw_mitigation_state +ffffffeb19a28c90 t spectre_v4_get_cpu_fw_mitigation_state +-- +ffffffeb19a8a320 t mmdrop_async_fn +ffffffeb19a8a358 t mm_release +ffffffeb19a8a480 t __pidfd_prepare +ffffffeb19a8a5c0 t __mmput +ffffffeb19a8a780 T mmput +ffffffeb19a8a818 t mmput_async_fn +ffffffeb19a8a850 t mm_init +ffffffeb19a8ab38 T nr_processes +-- +ffffffeb19cadee8 T end_page_writeback +ffffffeb19cadf30 T wait_on_page_writeback +ffffffeb19cadf78 T wait_for_stable_page +ffffffeb19cadfc0 T mark_page_accessed +ffffffeb19cae008 T set_page_writeback +ffffffeb19cae050 T set_page_dirty +ffffffeb19cae098 T clear_page_dirty_for_io +-- +ffffffeb19cf5af8 T vm_normal_page +ffffffeb19cf5bb8 T vm_normal_folio +ffffffeb19cf5c00 T vm_normal_page_pmd +ffffffeb19cf5cf8 T unmap_page_range +ffffffeb19cf65b0 T unmap_vmas +ffffffeb19cf6760 T zap_page_range_single +ffffffeb19cf6950 T zap_vma_ptes +ffffffeb19cf69b8 T unmap_mapping_pages +-- +ffffffeb19d04868 T __arm64_sys_munmap +ffffffeb19d048b0 T do_munmap +ffffffeb19d04948 T do_vma_munmap +ffffffeb19d04998 T exit_mmap +ffffffeb19d04d58 T insert_vm_struct +ffffffeb19d04e68 t __install_special_mapping +ffffffeb19d04fb0 T copy_vma +-- +ffffffeb19d8eaa0 T finalize_exec +ffffffeb19d8eb50 t alloc_bprm +ffffffeb19d8ee68 t percpu_up_read.constprop.0 +ffffffeb19d8ef00 t bprm_execve +ffffffeb19d8f5c8 t do_execveat_common +ffffffeb19d8f7f8 T __arm64_sys_execve +ffffffeb19d8f860 T __arm64_sys_execveat +ffffffeb19d8f8d8 T path_noexec +ffffffeb19d8f910 T __set_task_comm +ffffffeb19d8fa10 T kernel_execve +ffffffeb19d8fbe8 T set_dumpable +ffffffeb19d8fc60 T begin_new_exec +ffffffeb19d90830 T pipe_lock +ffffffeb19d90870 T pipe_unlock +ffffffeb19d908b0 T generic_pipe_buf_get +-- +ffffffeb19e058c8 t load_elf_phdrs +ffffffeb19e059c0 t padzero +ffffffeb19e05a60 t elf_core_dump +ffffffeb19e06870 t load_elf_binary +ffffffeb19e07d58 T posix_acl_init +ffffffeb19e07d78 T posix_acl_equiv_mode +ffffffeb19e07e68 t posix_acl_create_masq +-- +ffffffeb1a34d148 t el0_interrupt +ffffffeb1a34d228 t __el0_irq_handler_common +ffffffeb1a34d258 t __el0_fiq_handler_common +ffffffeb1a34d288 t el0_svc +ffffffeb1a34d3f0 T asm_exit_to_user_mode +ffffffeb1a34d430 T el1t_64_sync_handler +ffffffeb1a34d450 T el1t_64_irq_handler +-- +ffffffeb1a34d5b0 T el1h_64_irq_handler +ffffffeb1a34d5e0 T el1h_64_fiq_handler +ffffffeb1a34d610 T el1h_64_error_handler +ffffffeb1a34d660 T el0t_64_sync_handler +ffffffeb1a34d798 T el0t_64_irq_handler +ffffffeb1a34d7c0 T el0t_64_fiq_handler +ffffffeb1a34d7e8 T el0t_64_error_handler +-- +ffffffc08071046c t bpf_prog_ab4bc4523b7fe6b4 [bpf] +ffffffc0806499a8 t bpf_prog_ee0e253c78993a24_sd_devices [bpf] +ffffffc0801cedf4 t bpf_prog_9a4f2895a09f572a_mark_page_accessed [bpf] +ffffffc08012d000 t bpf_trampoline_6442507883 [bpf] +ffffffc080b89000 t kprobe_insn_page [__builtin__kprobes]