#include "basic_types.h"
#define MAX_STACK_DEPTH_PER_PROGRAM 7
#define MAX_TAIL_CALLS 30
#define MAX_STACK_DEPTH 200
_Static_assert(MAX_TAIL_CALLS* MAX_STACK_DEPTH_PER_PROGRAM >= MAX_STACK_DEPTH,
"enough iterations to traverse the whole stack");
#define MAX_STACK_TRACES_ENTRIES 64000
#define MAX_STACK_COUNTS_ENTRIES 10240
#define MAX_PROCESSES 5000
#define MAX_MAPPINGS MAX_PROCESSES * 200
#define MAX_BINARY_SEARCH_DEPTH 17
#define MAX_OUTER_UNWIND_MAP_ENTRIES 3000
#define UNWIND_INFO_PAGE_BIT_LEN 16
#define UNWIND_INFO_PAGE_SIZE (1 << UNWIND_INFO_PAGE_BIT_LEN)
#define PREVIOUS_PAGE(address) (address - UNWIND_INFO_PAGE_SIZE)
_Static_assert(MAX_BINARY_SEARCH_DEPTH >= UNWIND_INFO_PAGE_BIT_LEN, "unwind table is big enough");
#define HIGH_PC_MASK 0x0000FFFFFFFF0000LLU
#define LOW_PC_MASK 0x000000000000FFFFLLU
#define HIGH_PC(addr) (addr & HIGH_PC_MASK)
#define LOW_PC(addr) (addr & LOW_PC_MASK)
#define MAX_EXECUTABLE_TO_PAGE_ENTRIES 500 * 1000
#define MAPPING_TYPE_FILE 0
#define MAPPING_TYPE_ANON 1
#define MAPPING_TYPE_VDSO 2
typedef struct {
u64 executable_id;
u64 file_offset;
} page_key_t;
typedef struct {
u32 low_index;
u32 high_index;
} page_value_t;
#define CFA_TYPE_RBP 1
#define CFA_TYPE_RSP 2
#define CFA_TYPE_CFA_TYPE_UNSUP_EXP 3
#define CFA_TYPE_PLT1 4
#define CFA_TYPE_PLT2 5
#define CFA_TYPE_DEREF_AND_ADD 6
#define CFA_TYPE_END_OF_FDE_MARKER 7
#define CFA_TYPE_UNSUP_REGISTER_OFFSET 8
#define CFA_TYPE_OFFSET_DID_NOT_FIT 9
#define RBP_TYPE_UNCHANGED 0
#define RBP_TYPE_OFFSET 1
#define RBP_TYPE_REGISTER 2
#define RBP_TYPE_EXPRESSION 3
#define RBP_TYPE_UNDEFINED_RETURN_ADDRESS 4
#define RBP_TYPE_OFFSET_DID_NOT_FIT 5
#define RBP_TYPE_ARM64_RETURN_ADDRESS_LR 6
#define RBP_TYPE_ARM64_RETURN_ADDRESS_FRAME 7
#define RBP_TYPE_ARM64_RETURN_ADDRESS_ELSEWHERE 8
#define BINARY_SEARCH_DEFAULT 0xFABADAFABADAULL
#define BINARY_SEARCH_SHOULD_NEVER_HAPPEN 0xDEADBEEFDEADBEEFULL
#define BINARY_SEARCH_EXHAUSTED_ITERATIONS 0xBADFADBADFADBADULL
struct lightswitch_config_t {
bool verbose_logging;
bool use_ring_buffers;
bool use_task_pt_regs_helper;
bool use_btf_helpers;
unsigned int userspace_pid_ns_level;
};
struct unwinder_stats_t {
u64 total;
u64 success_dwarf;
u64 error_truncated;
u64 error_unsupported_expression;
u64 error_unsupported_frame_pointer_action;
u64 error_unsupported_cfa_register;
u64 error_previous_rsp_read;
u64 error_previous_rsp_zero;
u64 error_previous_rip_read;
u64 error_previous_rip_zero;
u64 error_previous_rbp_read;
u64 error_mapping_not_found;
u64 error_mapping_does_not_contain_pc;
u64 error_page_not_found;
u64 error_unwind_info_not_found;
u64 error_binary_search_exhausted_iterations;
u64 error_sending_new_process_event;
u64 error_sending_need_unwind_info_event;
u64 error_cfa_offset_did_not_fit;
u64 error_rbp_offset_did_not_fit;
u64 error_failure_sending_stack;
u64 bp_non_zero_for_bottom_frame;
u64 vdso_encountered;
u64 jit_encountered;
};
const volatile struct lightswitch_config_t lightswitch_config = {
.verbose_logging = false,
.use_ring_buffers = false,
.use_task_pt_regs_helper = false,
.use_btf_helpers = false,
.userspace_pid_ns_level = -1,
};
#define LOG(fmt, ...) \
({ \
if (lightswitch_config.verbose_logging) { \
bpf_printk(fmt, ##__VA_ARGS__); \
} \
})
typedef struct {
u64 executable_id;
u64 load_address;
u64 begin;
u64 end;
u32 type;
} mapping_t;
struct exec_mappings_key {
u32 prefix_len;
s32 pid;
u64 data;
};
#define PREFIX_LEN (sizeof(struct exec_mappings_key) - sizeof(u32)) * 8;
typedef struct __attribute__((packed)) {
u16 pc_low;
u8 cfa_type;
u8 rbp_type;
u16 cfa_offset;
s16 rbp_offset;
} stack_unwind_row_t;
_Static_assert(sizeof(stack_unwind_row_t) == 8,
"unwind row has the expected size");
typedef struct {
u32 ulen;
u32 klen;
u64 addresses[MAX_STACK_DEPTH * 2];
} native_stack_t;
enum sample_result {
SAMPLE_STARTED = 0,
SAMPLE_SUCCESS = 1,
SAMPLE_TRUNCATED = 2,
SAMPLE_WAITING_FOR_USERSPACE = 3,
SAMPLE_RATE_LIMITED = 4,
SAMPLE_UNWINDING_ERROR = 5,
SAMPLE_MAPPING_NOT_FOUND = 6,
SAMPLE_MAPPING_DOES_NOT_CONTAIN_PC = 7,
SAMPLE_MAPPING_JIT = 8,
SAMPLE_UNWIND_INFO_NOT_FOUND = 9,
SAMPLE_MEM_READ_FAULT_ERROR = 10,
SAMPLE_MEM_READ_GENERIC_ERROR = 11,
SAMPLE_UNSUPPORTED_UNWIND_RULE = 12,
SAMPLE_PAGE_NOT_FOUND = 13,
};
typedef struct {
int pid;
int tid;
enum sample_result result;
u64 collected_at;
native_stack_t stack;
} sample_t;
typedef struct {
unsigned long long ip;
unsigned long long sp;
unsigned long long bp;
unsigned long long lr;
u64 tail_calls;
sample_t sample;
} unwind_state_t;
enum event_type {
EVENT_NEW_PROCESS = 1,
EVENT_NEED_UNWIND_INFO = 2,
EVENT_NEED_PROCESS_INFO_REFRESH = 3,
};
typedef struct {
enum event_type type;
int pid; u64 address;
u8 comm[16];
} Event;
enum program {
PROGRAM_NATIVE_UNWINDER = 0,
};