byrnesj1
byrnesj1

Reputation: 311

process name (comm) as key for BPF map

I've been trying to design a tool wherein I can do per-process tracing, but this implies that I need a key for each process so that I can store key-value map pairings for each process. I instinctually don't like using structs or strings as keys, and for a while I was considering how to access inode values for their use as keys. However there are numerous examples that use structures or strings as hashmap keys, and Alexei suggested that process names will be commonly used as a key. That said, I am unable to get a basic implementation of such a hashmap to work. Within the BPF program, the tracepoint isn't able to find the associated value with the process_name key. Perhaps I'm comparing memory locations and not the string literals as intended? Is there something going on under the hood with c_types that creates a mismatch between the keys?

from bcc import BPF
from bcc.utils import printb
from bcc.syscall import syscall_name, syscalls
from ctypes import *

b = BPF(text = """

struct procName {
    char name[16];
};

BPF_HASH(attempt, struct procName, u32);

TRACEPOINT_PROBE(raw_syscalls, sys_exit)
{
    u32 *val;
    struct procName hKey;
    bpf_get_current_comm(hKey.name,16);
    val = attempt.lookup(&hKey);

    if (val)
    {
        bpf_trace_printk("Hello world, I have value %d!\\n", *val);
    }

    return 0;
}
""")

class procName(Structure):
    _fields_ = [("name", (c_char_p*16))]

myFirst = procName(('p','y','t','h','o','n','\0'))
trialUpload[myFirst] = c_int(10)

while 1:
    try:
        (task, pid, cpu, flags, ts, msg) = b.trace_fields()
    except KeyboardInterrupt:
        print("Detaching")
        exit()
    print("%-18.9f %-16s %-6d %s" % (ts, task, pid, msg))

Upvotes: 0

Views: 1481

Answers (1)

byrnesj1
byrnesj1

Reputation: 311

The error in the original code has nothing to do with BCC & BPF and lies within my implemention of ctypes. For starters --

class procName(Structure):
    _fields_ = [("name", (c_char_p*16))]

creates a structure with the field "name". In the above definition, name will be of type *char[16] when I want a char[16]. Second, while this

myFirst = procName(('p','y','t','h','o','n','\0'))

might work, its not the best practice initialization. This is the correct approach --

class procName(Structure):
    _fields_ = [("name", (c_char*16))]

s = "python"

mySecond = procName()
mySecond.name = s

Thus, the full program incorporating a process_name based key and implementation of such to pass data from python is then..

from bcc import BPF
from bcc.utils import printb
from bcc.syscall import syscall_name, syscalls
import ctypes
from ctypes import *

b = BPF(text = """

#include <linux/string.h>

struct procName {
    char name[16];
};

BPF_HASH(attempt, struct procName, u32);

TRACEPOINT_PROBE(raw_syscalls, sys_exit)
{
    u32 *myVal;
    struct procName key;
    bpf_get_current_comm(&(key.name),16);

    myVal = attempt.lookup(&key);

    if (myVal)
    {
        bpf_trace_printk("values: %d\\n", *myVal);
    }

    return 0;

}
""")

class procName(Structure):
    _fields_ = [("name", (c_char*16))]

trialUpload = b["attempt"]

s = "python"

mySecond = procName()
mySecond.name = s
trialUpload[mySecond] = c_int(5)

while 1:
    try:
        (task, pid, cpu, flags, ts, msg) = b.trace_fields()
    except KeyboardInterrupt:
        print("Detaching")
        exit()
    print("%-18.9f %-16s %-6d %s" % (ts, task, pid, msg))

Upvotes: 0

Related Questions