From f35b934aad0681756e7ce367d312b0247e64da00 Mon Sep 17 00:00:00 2001 From: Tommy Reilly Date: Mon, 23 Mar 2026 12:34:54 -0400 Subject: [PATCH] Move native unwinder impl to a .h This is a prep the patient PR to make room for a hybrid python/native unwinder that we found necessary to unwind large pytorch stacks that go back and forth from python to native more times than the tail call limit will allow. This change is pure code motion and changes nothing functionally. --- support/ebpf/native_stack_trace.ebpf.c | 448 +----------------------- support/ebpf/native_stack_trace.h | 452 +++++++++++++++++++++++++ support/ebpf/tracer.ebpf.amd64 | Bin 805936 -> 805960 bytes support/ebpf/tracer.ebpf.arm64 | Bin 797184 -> 797208 bytes 4 files changed, 453 insertions(+), 447 deletions(-) create mode 100644 support/ebpf/native_stack_trace.h diff --git a/support/ebpf/native_stack_trace.ebpf.c b/support/ebpf/native_stack_trace.ebpf.c index 5fd291634..f20ee5aad 100644 --- a/support/ebpf/native_stack_trace.ebpf.c +++ b/support/ebpf/native_stack_trace.ebpf.c @@ -61,10 +61,6 @@ STACK_DELTA_BUCKET(21); STACK_DELTA_BUCKET(22); STACK_DELTA_BUCKET(23); -// Unwind info value for invalid stack delta -#define STACK_DELTA_INVALID (STACK_DELTA_COMMAND_FLAG | UNWIND_COMMAND_INVALID) -#define STACK_DELTA_STOP (STACK_DELTA_COMMAND_FLAG | UNWIND_COMMAND_STOP) - // An array of unwind info contains the all the different UnwindInfo instances // needed system wide. Individual stack delta entries refer to this array. struct unwind_info_array_t { @@ -74,9 +70,6 @@ struct unwind_info_array_t { __uint(max_entries, UNWIND_INFO_MAX_ENTRIES); } unwind_info_array SEC(".maps"); -// The number of native frames to unwind per frame-unwinding eBPF program. -#define NATIVE_FRAMES_PER_PROGRAM 5 - // The decision whether to unwind native stacks or interpreter stacks is made by checking if a given // PC address falls into the "interpreter loop" of an interpreter. This map helps identify such // loops: The keys are those executable section IDs that contain interpreter loops, the values @@ -96,446 +89,7 @@ struct stack_delta_page_to_info_t { __uint(max_entries, 40000); } stack_delta_page_to_info SEC(".maps"); -// Record a native frame -static EBPF_INLINE ErrorCode -push_native(UnwindState *state, Trace *trace, u64 file, u64 line, bool return_address) -{ - const u8 ra_flag = return_address ? FRAME_FLAG_RETURN_ADDRESS : 0; - - u64 *data = push_frame(state, trace, FRAME_MARKER_NATIVE, ra_flag, line, 1); - if (!data) { - return ERR_STACK_LENGTH_EXCEEDED; - } - data[0] = file; - return ERR_OK; -} - -// A single step for the bsearch into the big_stack_deltas array. This is really a textbook bsearch -// step, built in a way to update the value of *lo and *hi. This function will be called repeatedly -// (since we cannot do loops). The return value signals whether the bsearch came to an end / found -// the right element or whether it needs to continue. -static EBPF_INLINE bool bsearch_step(void *inner_map, u32 *lo, u32 *hi, u16 page_offset) -{ - u32 pivot = (*lo + *hi) >> 1; - StackDelta *delta = bpf_map_lookup_elem(inner_map, &pivot); - if (!delta) { - *hi = 0; - return false; - } - if (page_offset >= delta->addrLow) { - *lo = pivot + 1; - } else { - *hi = pivot; - } - return *lo < *hi; -} - -// Get the outer map based on the number of stack delta entries. -static EBPF_INLINE void *get_stack_delta_map(int mapID) -{ - switch (mapID) { - case 8: return &exe_id_to_8_stack_deltas; - case 9: return &exe_id_to_9_stack_deltas; - case 10: return &exe_id_to_10_stack_deltas; - case 11: return &exe_id_to_11_stack_deltas; - case 12: return &exe_id_to_12_stack_deltas; - case 13: return &exe_id_to_13_stack_deltas; - case 14: return &exe_id_to_14_stack_deltas; - case 15: return &exe_id_to_15_stack_deltas; - case 16: return &exe_id_to_16_stack_deltas; - case 17: return &exe_id_to_17_stack_deltas; - case 18: return &exe_id_to_18_stack_deltas; - case 19: return &exe_id_to_19_stack_deltas; - case 20: return &exe_id_to_20_stack_deltas; - case 21: return &exe_id_to_21_stack_deltas; - case 22: return &exe_id_to_22_stack_deltas; - case 23: return &exe_id_to_23_stack_deltas; - default: return NULL; - } -} - -// Get the stack offset of the given instruction. -static EBPF_INLINE ErrorCode get_stack_delta(UnwindState *state, int *addrDiff, u32 *unwindInfo) -{ - u64 exe_id = state->text_section_id; - - // Look up the stack delta page information for this address. - StackDeltaPageKey key = {}; - key.fileID = state->text_section_id; - key.page = state->text_section_offset & ~STACK_DELTA_PAGE_MASK; - DEBUG_PRINT( - "Look up stack delta for %lx:%lx", - (unsigned long)state->text_section_id, - (unsigned long)state->text_section_offset); - StackDeltaPageInfo *info = bpf_map_lookup_elem(&stack_delta_page_to_info, &key); - if (!info) { - DEBUG_PRINT( - "Failure to look up stack delta page fileID %lx, page %lx", - (unsigned long)key.fileID, - (unsigned long)key.page); - state->error_metric = metricID_UnwindNativeErrLookupTextSection; - return ERR_NATIVE_LOOKUP_TEXT_SECTION; - } - - void *outer_map = get_stack_delta_map(info->mapID); - if (!outer_map) { - DEBUG_PRINT( - "Failure to look up outer map for text section %lx in mapID %d", - (unsigned long)exe_id, - (int)info->mapID); - state->error_metric = metricID_UnwindNativeErrLookupStackDeltaOuterMap; - return ERR_NATIVE_LOOKUP_STACK_DELTA_OUTER_MAP; - } - - void *inner_map = bpf_map_lookup_elem(outer_map, &exe_id); - if (!inner_map) { - DEBUG_PRINT("Failure to look up inner map for text section %lx", (unsigned long)exe_id); - state->error_metric = metricID_UnwindNativeErrLookupStackDeltaInnerMap; - return ERR_NATIVE_LOOKUP_STACK_DELTA_INNER_MAP; - } - - // Preinitialize the idx for the index to use for page without any deltas. - u32 idx = info->firstDelta; - u16 page_offset = state->text_section_offset & STACK_DELTA_PAGE_MASK; - if (info->numDeltas) { - // Page has deltas, so find the correct one to use using binary search. - u32 lo = info->firstDelta; - u32 hi = lo + info->numDeltas; - - DEBUG_PRINT( - "Intervals should be from %lu to %lu (mapID %d)", - (unsigned long)lo, - (unsigned long)hi, - (int)info->mapID); - - // Do the binary search, up to 16 iterations. Deltas are paged to 64kB pages. - // They can contain at most 64kB deltas even if everything is single byte opcodes. - int i; - for (i = 0; i < 16; i++) { - if (!bsearch_step(inner_map, &lo, &hi, page_offset)) { - break; - } - } - if (i >= 16 || hi == 0) { - DEBUG_PRINT("Failed bsearch in 16 steps. Corrupt data?"); - state->error_metric = metricID_UnwindNativeErrLookupIterations; - return ERR_NATIVE_EXCEEDED_DELTA_LOOKUP_ITERATIONS; - } - // After bsearch, 'hi' points to the first entry greater than the requested. - idx = hi; - } - - // The code above found the first entry with greater address than requested, - // so it needs to be decremented by one to get the entry with equal-or-less. - // This makes also the logic work cross-pages: if the first entry in within - // the page is too large, this actually gets the entry from the previous page. - idx--; - - StackDelta *delta = bpf_map_lookup_elem(inner_map, &idx); - if (!delta) { - state->error_metric = metricID_UnwindNativeErrLookupRange; - return ERR_NATIVE_LOOKUP_RANGE; - } - - DEBUG_PRINT( - "delta index %d, addrLow 0x%x, unwindInfo %d", idx, delta->addrLow, delta->unwindInfo); - - // Calculate PC delta from stack delta for merged delta comparison - int deltaOffset = (int)page_offset - (int)delta->addrLow; - if (idx < info->firstDelta) { - // PC is below the first delta of the corresponding page. This means that - // delta->addrLow contains address relative to one page before the page_offset. - // Fix up the deltaOffset with this difference of base pages. - deltaOffset += 1 << STACK_DELTA_PAGE_BITS; - } - - *addrDiff = deltaOffset; - *unwindInfo = delta->unwindInfo; - - if (delta->unwindInfo == STACK_DELTA_INVALID) { - state->error_metric = metricID_UnwindNativeErrStackDeltaInvalid; - return ERR_NATIVE_STACK_DELTA_INVALID; - } - if (delta->unwindInfo == STACK_DELTA_STOP) { - increment_metric(metricID_UnwindNativeStackDeltaStop); - } - - return ERR_OK; -} - -// unwind_calc_register calculates the given basic register expression of -// format "BASE_REG + param". -static EBPF_INLINE u64 unwind_calc_register(UnwindState *state, u8 baseReg, s32 param) -{ - return state->regs[baseReg % (sizeof(state->regs) / sizeof(state->regs[0]))] + param; -} - -#if defined(__x86_64__) - -// unwind_calc_register_with_deref calculates the expression as: -// - basic expression "BASE_REG + param" -// - expression with a dereference "*(BASE_REG + preDeref) + postDeref" -static EBPF_INLINE u64 -unwind_calc_register_with_deref(UnwindState *state, u8 baseReg, s32 param, bool deref) -{ - s32 preDeref = param, postDeref = 0; - - if (deref) { - // For expressions that dereference the base expression, the parameter is constructed - // of pre-dereference and post-derefence operands. Unpack those. - preDeref &= ~UNWIND_DEREF_MASK; - postDeref = (param & UNWIND_DEREF_MASK) * UNWIND_DEREF_MULTIPLIER; - } - - // Resolve the "BASE + param" before potential derereference - u64 addr = unwind_calc_register(state, baseReg, preDeref); - if (!deref) { - // All done: return "BASE + param" - return addr; - } - - // Dereference, and add the postDereference adder. - unsigned long val; - if (bpf_probe_read_user(&val, sizeof(val), (void *)addr)) { - DEBUG_PRINT("unwind failed to dereference address 0x%lx", (unsigned long)addr); - return 0; - } - // Return: "*(BASE + preDeref) + postDeref" - return val + postDeref; -} -#endif - -// Stack unwinding in the absence of frame pointers can be a bit involved, so -// this comment explains what the following code does. -// -// One begins unwinding a frame somewhere in the middle of execution. -// On x86_64, registers RIP (PC), RSP (SP), and RBP (FP) are available. -// -// This function resolves a "stack delta" command from from our internal maps. -// This stack delta refers to a rule on how to unwind the state. In the simple -// case it just provides SP delta and potentially offset from where to recover -// FP value. See unwind_calc_register[_with_deref]() on the expressions supported. -// -// The function sets the bool pointed to by the given `stop` pointer to `false` -// if the main ebpf unwinder should exit. This is the case if the current PC -// is marked with UNWIND_COMMAND_STOP which marks entry points (main function, -// thread spawn function, signal handlers, ...). -#if defined(__x86_64__) -static EBPF_INLINE ErrorCode unwind_one_frame(UnwindState *state, bool *stop) -{ - *stop = false; - - u32 unwindInfo = 0; - u64 rt_regs[18]; - int addrDiff = 0; - u64 cfa = 0; - - // The relevant executable is compiled with frame pointer omission, so - // stack deltas need to be retrieved from the relevant map. - ErrorCode error = get_stack_delta(state, &addrDiff, &unwindInfo); - if (error) { - return error; - } - - if (unwindInfo & STACK_DELTA_COMMAND_FLAG) { - switch (unwindInfo & ~STACK_DELTA_COMMAND_FLAG) { - case UNWIND_COMMAND_PLT: - // The toolchains routinely emit a fixed DWARF expression to unwind the full - // PLT table with one expression to reduce .eh_frame size. - // This is the hard coded implementation of this expression. For further details, - // see https://hal.inria.fr/hal-02297690/document, page 4. (DOI: 10.1145/3360572) - cfa = state->sp + 8 + ((((state->pc & 15) >= 11) ? 1 : 0) << 3); - DEBUG_PRINT("PLT, cfa=0x%lx", (unsigned long)cfa); - break; - case UNWIND_COMMAND_SIGNAL: - // The rt_sigframe is defined at: - // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/include/asm/sigframe.h?h=v6.4#n59 - // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/include/uapi/asm/sigcontext.h?h=v6.4#n238 - // offsetof(struct rt_sigframe, uc.uc_mcontext) = 40 - if (bpf_probe_read_user(&rt_regs, sizeof(rt_regs), (void *)(state->sp + 40))) { - goto err_native_pc_read; - } - state->rax = rt_regs[13]; - state->r9 = rt_regs[1]; - state->r11 = rt_regs[3]; - state->r13 = rt_regs[5]; - state->r15 = rt_regs[7]; - state->fp = rt_regs[10]; - state->sp = rt_regs[15]; - state->pc = rt_regs[16]; - - state->return_address = false; - DEBUG_PRINT("signal frame"); - goto frame_ok; - case UNWIND_COMMAND_STOP: *stop = true; return ERR_OK; - case UNWIND_COMMAND_FRAME_POINTER: - if (!unwinder_unwind_frame_pointer(state)) { - goto err_native_pc_read; - } - goto frame_ok; - default: return ERR_UNREACHABLE; - } - } else { - UnwindInfo *info = bpf_map_lookup_elem(&unwind_info_array, &unwindInfo); - if (!info) { - increment_metric(metricID_UnwindNativeErrBadUnwindInfoIndex); - return ERR_NATIVE_BAD_UNWIND_INFO_INDEX; - } - - s32 param = info->param; - if (info->mergeOpcode) { - DEBUG_PRINT("AddrDiff %d, merged delta %#02x", addrDiff, info->mergeOpcode); - if (addrDiff >= (info->mergeOpcode & ~MERGEOPCODE_NEGATIVE)) { - param += (info->mergeOpcode & MERGEOPCODE_NEGATIVE) ? -8 : 8; - DEBUG_PRINT("Merged delta match: cfaDelta=%d", unwindInfo); - } - } - - // Resolve the frame's CFA (previous PC is fixed to CFA) address, and - // the previous FP address if any. - state->cfa = cfa = unwind_calc_register_with_deref( - state, info->baseReg, param, (info->flags & UNWIND_FLAG_DEREF_CFA) != 0); - u64 fpa = unwind_calc_register(state, info->auxBaseReg, info->auxParam); - - if (fpa) { - bpf_probe_read_user(&state->fp, sizeof(state->fp), (void *)fpa); - } else if (info->baseReg == UNWIND_REG_FP) { - // FP used for recovery, but no new FP value received, clear FP - state->fp = 0; - } - } - - if (!cfa || bpf_probe_read_user(&state->pc, sizeof(state->pc), (void *)(cfa - 8))) { - err_native_pc_read: - increment_metric(metricID_UnwindNativeErrPCRead); - return ERR_NATIVE_PC_READ; - } - state->sp = cfa; - unwinder_mark_nonleaf_frame(state); -frame_ok: - increment_metric(metricID_UnwindNativeFrames); - return ERR_OK; -} -#elif defined(__aarch64__) -static EBPF_INLINE ErrorCode unwind_one_frame(struct UnwindState *state, bool *stop) -{ - *stop = false; - - u32 unwindInfo = 0; - int addrDiff = 0; - u64 rt_regs[34]; - - // The relevant executable is compiled with frame pointer omission, so - // stack deltas need to be retrieved from the relevant map. - ErrorCode error = get_stack_delta(state, &addrDiff, &unwindInfo); - if (error) { - return error; - } - - if (unwindInfo & STACK_DELTA_COMMAND_FLAG) { - switch (unwindInfo & ~STACK_DELTA_COMMAND_FLAG) { - case UNWIND_COMMAND_SIGNAL: - // On aarch64 the struct rt_sigframe is at: - // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/kernel/signal.c?h=v6.4#n39 - // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/include/uapi/asm/sigcontext.h?h=v6.4#n28 - // offsetof(struct rt_sigframe, uc.uc_mcontext.regs[0]) = 312 - // offsetof(struct rt_sigframe, uc) 128 + - // offsetof(struct ucontext, uc_mcontext) 176 + - // offsetof(struct sigcontext, regs[0]) 8 - if (bpf_probe_read_user(&rt_regs, sizeof(rt_regs), (void *)(state->sp + 312))) { - goto err_native_pc_read; - } - state->pc = normalize_pac_ptr(rt_regs[32]); - state->sp = rt_regs[31]; - state->fp = rt_regs[29]; - state->lr = normalize_pac_ptr(rt_regs[30]); - state->r20 = rt_regs[20]; - state->r22 = rt_regs[22]; - state->r28 = rt_regs[28]; - - state->return_address = false; - state->lr_invalid = false; - DEBUG_PRINT("signal frame"); - goto frame_ok; - case UNWIND_COMMAND_STOP: *stop = true; return ERR_OK; - case UNWIND_COMMAND_FRAME_POINTER: - if (!unwinder_unwind_frame_pointer(state)) { - goto err_native_pc_read; - } - goto frame_ok; - default: return ERR_UNREACHABLE; - } - } - - UnwindInfo *info = bpf_map_lookup_elem(&unwind_info_array, &unwindInfo); - if (!info) { - increment_metric(metricID_UnwindNativeErrBadUnwindInfoIndex); - DEBUG_PRINT("Giving up due to invalid unwind info array index"); - return ERR_NATIVE_BAD_UNWIND_INFO_INDEX; - } - - s32 param = info->param; - if (info->mergeOpcode) { - DEBUG_PRINT("AddrDiff %d, merged delta %#02x", addrDiff, info->mergeOpcode); - if (addrDiff >= (info->mergeOpcode & ~MERGEOPCODE_NEGATIVE)) { - param += (info->mergeOpcode & MERGEOPCODE_NEGATIVE) ? -8 : 8; - DEBUG_PRINT("Merged delta match: cfaDelta=%d", unwindInfo); - } - } - - // Resolve the frame CFA (previous PC is fixed to CFA) address - state->cfa = unwind_calc_register(state, info->baseReg, param); - - // Resolve Return Address, it is either the value of link register or - // stack address where RA is stored - u64 ra = unwind_calc_register(state, info->auxBaseReg, info->auxParam); - if (!ra) { - if (info->auxBaseReg == UNWIND_REG_LR) { - increment_metric(metricID_UnwindNativeLr0); - } else { - err_native_pc_read: - increment_metric(metricID_UnwindNativeErrPCRead); - } - // report failure to resolve RA and stop unwinding - DEBUG_PRINT("Giving up due to failure to resolve RA"); - return ERR_NATIVE_PC_READ; - } - - if (info->auxBaseReg == UNWIND_REG_LR) { - // Allow LR unwinding only if it's known to be valid: either because - // it's the topmost user-mode frame, or recovered by signal trampoline. - if (state->lr_invalid) { - increment_metric(metricID_UnwindNativeErrLrUnwindingMidTrace); - return ERR_NATIVE_LR_UNWINDING_MID_TRACE; - } - } else { - DEBUG_PRINT("RA: %016llX", (u64)ra); - - // read the value of RA from stack - int err; - u64 fpra[2]; - fpra[0] = state->fp; - if (info->flags & UNWIND_FLAG_FRAME) { - err = bpf_probe_read_user(fpra, sizeof(fpra), (void *)(ra - 8)); - } else { - err = bpf_probe_read_user(&fpra[1], sizeof(fpra[0]), (void *)ra); - } - if (err) { - goto err_native_pc_read; - } - state->fp = fpra[0]; - ra = fpra[1]; - } - state->pc = normalize_pac_ptr(ra); - state->sp = state->cfa; - unwinder_mark_nonleaf_frame(state); -frame_ok: - increment_metric(metricID_UnwindNativeFrames); - return ERR_OK; -} -#else - #error unsupported architecture -#endif +#include "native_stack_trace.h" // unwind_native is the tail call destination for PROG_UNWIND_NATIVE. static EBPF_INLINE int unwind_native(struct pt_regs *ctx) diff --git a/support/ebpf/native_stack_trace.h b/support/ebpf/native_stack_trace.h new file mode 100644 index 000000000..174218bf9 --- /dev/null +++ b/support/ebpf/native_stack_trace.h @@ -0,0 +1,452 @@ +#ifndef OPTI_NATIVE_STACK_TRACE_H +#define OPTI_NATIVE_STACK_TRACE_H + +// Unwind info value for invalid stack delta +#define STACK_DELTA_INVALID (STACK_DELTA_COMMAND_FLAG | UNWIND_COMMAND_INVALID) +#define STACK_DELTA_STOP (STACK_DELTA_COMMAND_FLAG | UNWIND_COMMAND_STOP) + +// The number of native frames to unwind per frame-unwinding eBPF program. +#define NATIVE_FRAMES_PER_PROGRAM 5 + +// Record a native frame +static EBPF_INLINE ErrorCode +push_native(UnwindState *state, Trace *trace, u64 file, u64 line, bool return_address) +{ + const u8 ra_flag = return_address ? FRAME_FLAG_RETURN_ADDRESS : 0; + + u64 *data = push_frame(state, trace, FRAME_MARKER_NATIVE, ra_flag, line, 1); + if (!data) { + return ERR_STACK_LENGTH_EXCEEDED; + } + data[0] = file; + return ERR_OK; +} + +// A single step for the bsearch into the big_stack_deltas array. This is really a textbook bsearch +// step, built in a way to update the value of *lo and *hi. This function will be called repeatedly +// (since we cannot do loops). The return value signals whether the bsearch came to an end / found +// the right element or whether it needs to continue. +static EBPF_INLINE bool bsearch_step(void *inner_map, u32 *lo, u32 *hi, u16 page_offset) +{ + u32 pivot = (*lo + *hi) >> 1; + StackDelta *delta = bpf_map_lookup_elem(inner_map, &pivot); + if (!delta) { + *hi = 0; + return false; + } + if (page_offset >= delta->addrLow) { + *lo = pivot + 1; + } else { + *hi = pivot; + } + return *lo < *hi; +} + +// Get the outer map based on the number of stack delta entries. +static EBPF_INLINE void *get_stack_delta_map(int mapID) +{ + switch (mapID) { + case 8: return &exe_id_to_8_stack_deltas; + case 9: return &exe_id_to_9_stack_deltas; + case 10: return &exe_id_to_10_stack_deltas; + case 11: return &exe_id_to_11_stack_deltas; + case 12: return &exe_id_to_12_stack_deltas; + case 13: return &exe_id_to_13_stack_deltas; + case 14: return &exe_id_to_14_stack_deltas; + case 15: return &exe_id_to_15_stack_deltas; + case 16: return &exe_id_to_16_stack_deltas; + case 17: return &exe_id_to_17_stack_deltas; + case 18: return &exe_id_to_18_stack_deltas; + case 19: return &exe_id_to_19_stack_deltas; + case 20: return &exe_id_to_20_stack_deltas; + case 21: return &exe_id_to_21_stack_deltas; + case 22: return &exe_id_to_22_stack_deltas; + case 23: return &exe_id_to_23_stack_deltas; + default: return NULL; + } +} + +// Get the stack offset of the given instruction. +static EBPF_INLINE ErrorCode get_stack_delta(UnwindState *state, int *addrDiff, u32 *unwindInfo) +{ + u64 exe_id = state->text_section_id; + + // Look up the stack delta page information for this address. + StackDeltaPageKey key = {}; + key.fileID = state->text_section_id; + key.page = state->text_section_offset & ~STACK_DELTA_PAGE_MASK; + DEBUG_PRINT( + "Look up stack delta for %lx:%lx", + (unsigned long)state->text_section_id, + (unsigned long)state->text_section_offset); + StackDeltaPageInfo *info = bpf_map_lookup_elem(&stack_delta_page_to_info, &key); + if (!info) { + DEBUG_PRINT( + "Failure to look up stack delta page fileID %lx, page %lx", + (unsigned long)key.fileID, + (unsigned long)key.page); + state->error_metric = metricID_UnwindNativeErrLookupTextSection; + return ERR_NATIVE_LOOKUP_TEXT_SECTION; + } + + void *outer_map = get_stack_delta_map(info->mapID); + if (!outer_map) { + DEBUG_PRINT( + "Failure to look up outer map for text section %lx in mapID %d", + (unsigned long)exe_id, + (int)info->mapID); + state->error_metric = metricID_UnwindNativeErrLookupStackDeltaOuterMap; + return ERR_NATIVE_LOOKUP_STACK_DELTA_OUTER_MAP; + } + + void *inner_map = bpf_map_lookup_elem(outer_map, &exe_id); + if (!inner_map) { + DEBUG_PRINT("Failure to look up inner map for text section %lx", (unsigned long)exe_id); + state->error_metric = metricID_UnwindNativeErrLookupStackDeltaInnerMap; + return ERR_NATIVE_LOOKUP_STACK_DELTA_INNER_MAP; + } + + // Preinitialize the idx for the index to use for page without any deltas. + u32 idx = info->firstDelta; + u16 page_offset = state->text_section_offset & STACK_DELTA_PAGE_MASK; + if (info->numDeltas) { + // Page has deltas, so find the correct one to use using binary search. + u32 lo = info->firstDelta; + u32 hi = lo + info->numDeltas; + + DEBUG_PRINT( + "Intervals should be from %lu to %lu (mapID %d)", + (unsigned long)lo, + (unsigned long)hi, + (int)info->mapID); + + // Do the binary search, up to 16 iterations. Deltas are paged to 64kB pages. + // They can contain at most 64kB deltas even if everything is single byte opcodes. + int i; + for (i = 0; i < 16; i++) { + if (!bsearch_step(inner_map, &lo, &hi, page_offset)) { + break; + } + } + if (i >= 16 || hi == 0) { + DEBUG_PRINT("Failed bsearch in 16 steps. Corrupt data?"); + state->error_metric = metricID_UnwindNativeErrLookupIterations; + return ERR_NATIVE_EXCEEDED_DELTA_LOOKUP_ITERATIONS; + } + // After bsearch, 'hi' points to the first entry greater than the requested. + idx = hi; + } + + // The code above found the first entry with greater address than requested, + // so it needs to be decremented by one to get the entry with equal-or-less. + // This makes also the logic work cross-pages: if the first entry in within + // the page is too large, this actually gets the entry from the previous page. + idx--; + + StackDelta *delta = bpf_map_lookup_elem(inner_map, &idx); + if (!delta) { + state->error_metric = metricID_UnwindNativeErrLookupRange; + return ERR_NATIVE_LOOKUP_RANGE; + } + + DEBUG_PRINT( + "delta index %d, addrLow 0x%x, unwindInfo %d", idx, delta->addrLow, delta->unwindInfo); + + // Calculate PC delta from stack delta for merged delta comparison + int deltaOffset = (int)page_offset - (int)delta->addrLow; + if (idx < info->firstDelta) { + // PC is below the first delta of the corresponding page. This means that + // delta->addrLow contains address relative to one page before the page_offset. + // Fix up the deltaOffset with this difference of base pages. + deltaOffset += 1 << STACK_DELTA_PAGE_BITS; + } + + *addrDiff = deltaOffset; + *unwindInfo = delta->unwindInfo; + + if (delta->unwindInfo == STACK_DELTA_INVALID) { + state->error_metric = metricID_UnwindNativeErrStackDeltaInvalid; + return ERR_NATIVE_STACK_DELTA_INVALID; + } + if (delta->unwindInfo == STACK_DELTA_STOP) { + increment_metric(metricID_UnwindNativeStackDeltaStop); + } + + return ERR_OK; +} + +// unwind_calc_register calculates the given basic register expression of +// format "BASE_REG + param". +static EBPF_INLINE u64 unwind_calc_register(UnwindState *state, u8 baseReg, s32 param) +{ + return state->regs[baseReg % (sizeof(state->regs) / sizeof(state->regs[0]))] + param; +} + +#if defined(__x86_64__) + +// unwind_calc_register_with_deref calculates the expression as: +// - basic expression "BASE_REG + param" +// - expression with a dereference "*(BASE_REG + preDeref) + postDeref" +static EBPF_INLINE u64 +unwind_calc_register_with_deref(UnwindState *state, u8 baseReg, s32 param, bool deref) +{ + s32 preDeref = param, postDeref = 0; + + if (deref) { + // For expressions that dereference the base expression, the parameter is constructed + // of pre-dereference and post-derefence operands. Unpack those. + preDeref &= ~UNWIND_DEREF_MASK; + postDeref = (param & UNWIND_DEREF_MASK) * UNWIND_DEREF_MULTIPLIER; + } + + // Resolve the "BASE + param" before potential derereference + u64 addr = unwind_calc_register(state, baseReg, preDeref); + if (!deref) { + // All done: return "BASE + param" + return addr; + } + + // Dereference, and add the postDereference adder. + unsigned long val; + if (bpf_probe_read_user(&val, sizeof(val), (void *)addr)) { + DEBUG_PRINT("unwind failed to dereference address 0x%lx", (unsigned long)addr); + return 0; + } + // Return: "*(BASE + preDeref) + postDeref" + return val + postDeref; +} +#endif + +// Stack unwinding in the absence of frame pointers can be a bit involved, so +// this comment explains what the following code does. +// +// One begins unwinding a frame somewhere in the middle of execution. +// On x86_64, registers RIP (PC), RSP (SP), and RBP (FP) are available. +// +// This function resolves a "stack delta" command from from our internal maps. +// This stack delta refers to a rule on how to unwind the state. In the simple +// case it just provides SP delta and potentially offset from where to recover +// FP value. See unwind_calc_register[_with_deref]() on the expressions supported. +// +// The function sets the bool pointed to by the given `stop` pointer to `false` +// if the main ebpf unwinder should exit. This is the case if the current PC +// is marked with UNWIND_COMMAND_STOP which marks entry points (main function, +// thread spawn function, signal handlers, ...). +#if defined(__x86_64__) +static EBPF_INLINE ErrorCode unwind_one_frame(UnwindState *state, bool *stop) +{ + *stop = false; + + u32 unwindInfo = 0; + u64 rt_regs[18]; + int addrDiff = 0; + u64 cfa = 0; + + // The relevant executable is compiled with frame pointer omission, so + // stack deltas need to be retrieved from the relevant map. + ErrorCode error = get_stack_delta(state, &addrDiff, &unwindInfo); + if (error) { + return error; + } + + if (unwindInfo & STACK_DELTA_COMMAND_FLAG) { + switch (unwindInfo & ~STACK_DELTA_COMMAND_FLAG) { + case UNWIND_COMMAND_PLT: + // The toolchains routinely emit a fixed DWARF expression to unwind the full + // PLT table with one expression to reduce .eh_frame size. + // This is the hard coded implementation of this expression. For further details, + // see https://hal.inria.fr/hal-02297690/document, page 4. (DOI: 10.1145/3360572) + cfa = state->sp + 8 + ((((state->pc & 15) >= 11) ? 1 : 0) << 3); + DEBUG_PRINT("PLT, cfa=0x%lx", (unsigned long)cfa); + break; + case UNWIND_COMMAND_SIGNAL: + // The rt_sigframe is defined at: + // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/include/asm/sigframe.h?h=v6.4#n59 + // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/include/uapi/asm/sigcontext.h?h=v6.4#n238 + // offsetof(struct rt_sigframe, uc.uc_mcontext) = 40 + if (bpf_probe_read_user(&rt_regs, sizeof(rt_regs), (void *)(state->sp + 40))) { + goto err_native_pc_read; + } + state->rax = rt_regs[13]; + state->r9 = rt_regs[1]; + state->r11 = rt_regs[3]; + state->r13 = rt_regs[5]; + state->r15 = rt_regs[7]; + state->fp = rt_regs[10]; + state->sp = rt_regs[15]; + state->pc = rt_regs[16]; + + state->return_address = false; + DEBUG_PRINT("signal frame"); + goto frame_ok; + case UNWIND_COMMAND_STOP: *stop = true; return ERR_OK; + case UNWIND_COMMAND_FRAME_POINTER: + if (!unwinder_unwind_frame_pointer(state)) { + goto err_native_pc_read; + } + goto frame_ok; + default: return ERR_UNREACHABLE; + } + } else { + UnwindInfo *info = bpf_map_lookup_elem(&unwind_info_array, &unwindInfo); + if (!info) { + increment_metric(metricID_UnwindNativeErrBadUnwindInfoIndex); + return ERR_NATIVE_BAD_UNWIND_INFO_INDEX; + } + + s32 param = info->param; + if (info->mergeOpcode) { + DEBUG_PRINT("AddrDiff %d, merged delta %#02x", addrDiff, info->mergeOpcode); + if (addrDiff >= (info->mergeOpcode & ~MERGEOPCODE_NEGATIVE)) { + param += (info->mergeOpcode & MERGEOPCODE_NEGATIVE) ? -8 : 8; + DEBUG_PRINT("Merged delta match: cfaDelta=%d", unwindInfo); + } + } + + // Resolve the frame's CFA (previous PC is fixed to CFA) address, and + // the previous FP address if any. + state->cfa = cfa = unwind_calc_register_with_deref( + state, info->baseReg, param, (info->flags & UNWIND_FLAG_DEREF_CFA) != 0); + u64 fpa = unwind_calc_register(state, info->auxBaseReg, info->auxParam); + + if (fpa) { + bpf_probe_read_user(&state->fp, sizeof(state->fp), (void *)fpa); + } else if (info->baseReg == UNWIND_REG_FP) { + // FP used for recovery, but no new FP value received, clear FP + state->fp = 0; + } + } + + if (!cfa || bpf_probe_read_user(&state->pc, sizeof(state->pc), (void *)(cfa - 8))) { + err_native_pc_read: + increment_metric(metricID_UnwindNativeErrPCRead); + return ERR_NATIVE_PC_READ; + } + state->sp = cfa; + unwinder_mark_nonleaf_frame(state); +frame_ok: + increment_metric(metricID_UnwindNativeFrames); + return ERR_OK; +} +#elif defined(__aarch64__) +static EBPF_INLINE ErrorCode unwind_one_frame(struct UnwindState *state, bool *stop) +{ + *stop = false; + + u32 unwindInfo = 0; + int addrDiff = 0; + u64 rt_regs[34]; + + // The relevant executable is compiled with frame pointer omission, so + // stack deltas need to be retrieved from the relevant map. + ErrorCode error = get_stack_delta(state, &addrDiff, &unwindInfo); + if (error) { + return error; + } + + if (unwindInfo & STACK_DELTA_COMMAND_FLAG) { + switch (unwindInfo & ~STACK_DELTA_COMMAND_FLAG) { + case UNWIND_COMMAND_SIGNAL: + // On aarch64 the struct rt_sigframe is at: + // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/kernel/signal.c?h=v6.4#n39 + // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/include/uapi/asm/sigcontext.h?h=v6.4#n28 + // offsetof(struct rt_sigframe, uc.uc_mcontext.regs[0]) = 312 + // offsetof(struct rt_sigframe, uc) 128 + + // offsetof(struct ucontext, uc_mcontext) 176 + + // offsetof(struct sigcontext, regs[0]) 8 + if (bpf_probe_read_user(&rt_regs, sizeof(rt_regs), (void *)(state->sp + 312))) { + goto err_native_pc_read; + } + state->pc = normalize_pac_ptr(rt_regs[32]); + state->sp = rt_regs[31]; + state->fp = rt_regs[29]; + state->lr = normalize_pac_ptr(rt_regs[30]); + state->r20 = rt_regs[20]; + state->r22 = rt_regs[22]; + state->r28 = rt_regs[28]; + + state->return_address = false; + state->lr_invalid = false; + DEBUG_PRINT("signal frame"); + goto frame_ok; + case UNWIND_COMMAND_STOP: *stop = true; return ERR_OK; + case UNWIND_COMMAND_FRAME_POINTER: + if (!unwinder_unwind_frame_pointer(state)) { + goto err_native_pc_read; + } + goto frame_ok; + default: return ERR_UNREACHABLE; + } + } + + UnwindInfo *info = bpf_map_lookup_elem(&unwind_info_array, &unwindInfo); + if (!info) { + increment_metric(metricID_UnwindNativeErrBadUnwindInfoIndex); + DEBUG_PRINT("Giving up due to invalid unwind info array index"); + return ERR_NATIVE_BAD_UNWIND_INFO_INDEX; + } + + s32 param = info->param; + if (info->mergeOpcode) { + DEBUG_PRINT("AddrDiff %d, merged delta %#02x", addrDiff, info->mergeOpcode); + if (addrDiff >= (info->mergeOpcode & ~MERGEOPCODE_NEGATIVE)) { + param += (info->mergeOpcode & MERGEOPCODE_NEGATIVE) ? -8 : 8; + DEBUG_PRINT("Merged delta match: cfaDelta=%d", unwindInfo); + } + } + + // Resolve the frame CFA (previous PC is fixed to CFA) address + state->cfa = unwind_calc_register(state, info->baseReg, param); + + // Resolve Return Address, it is either the value of link register or + // stack address where RA is stored + u64 ra = unwind_calc_register(state, info->auxBaseReg, info->auxParam); + if (!ra) { + if (info->auxBaseReg == UNWIND_REG_LR) { + increment_metric(metricID_UnwindNativeLr0); + } else { + err_native_pc_read: + increment_metric(metricID_UnwindNativeErrPCRead); + } + // report failure to resolve RA and stop unwinding + DEBUG_PRINT("Giving up due to failure to resolve RA"); + return ERR_NATIVE_PC_READ; + } + + if (info->auxBaseReg == UNWIND_REG_LR) { + // Allow LR unwinding only if it's known to be valid: either because + // it's the topmost user-mode frame, or recovered by signal trampoline. + if (state->lr_invalid) { + increment_metric(metricID_UnwindNativeErrLrUnwindingMidTrace); + return ERR_NATIVE_LR_UNWINDING_MID_TRACE; + } + } else { + DEBUG_PRINT("RA: %016llX", (u64)ra); + + // read the value of RA from stack + int err; + u64 fpra[2]; + fpra[0] = state->fp; + if (info->flags & UNWIND_FLAG_FRAME) { + err = bpf_probe_read_user(fpra, sizeof(fpra), (void *)(ra - 8)); + } else { + err = bpf_probe_read_user(&fpra[1], sizeof(fpra[0]), (void *)ra); + } + if (err) { + goto err_native_pc_read; + } + state->fp = fpra[0]; + ra = fpra[1]; + } + state->pc = normalize_pac_ptr(ra); + state->sp = state->cfa; + unwinder_mark_nonleaf_frame(state); +frame_ok: + increment_metric(metricID_UnwindNativeFrames); + return ERR_OK; +} +#else + #error unsupported architecture +#endif + +#endif diff --git a/support/ebpf/tracer.ebpf.amd64 b/support/ebpf/tracer.ebpf.amd64 index f24cb4dd0aa41a5ed84df393461ba9ac03708021..5419a11fbdae7bf9b5311c76bf5e5b2550ad5c40 100644 GIT binary patch delta 41457 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 Y!=~a@p`6-DUxVr;`WjHt&Bb&78}9i*tpET3 delta 41436 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 E3p{{Q6#xJL delta 41537 zcmeI5dw3khnXhXut?IEPYa~nd=wfRu-(*`@lCeQV7>~gQfguPm2L%j~vrZy5;Y955 z36l+*8NzJ{4v1h~jBoo@dWx`mx;e)_1Au>gw*gb#?w=OZH$(Ut=hH&GPG)hpt~9zJ7V+(dE&VuZKE@ zFMK`vU~A~t>Vt&0GW^He)rx9wLFhqsX0>;7?8RL|goG&BsamVNIcmJxi^djw(}dlk zHYB`xp>3+F#=ALmp1QZjs}HSIPZ3?9&Zz}GtFEZ^ZVvzD5%pdzoOY|~Iyl|=Q$btF z1IB58oflpA(TgT*0I~5F{yu&$J19g%h-f7OPr>g!EZ(71zKZKZVxsI9rg~h7jo9exHL!mY5nuXryLaE_DE%dhDIQ*%HyvX+9p3fpif&bv}*TGHZWRtqM75w)O-v-{Uj@sV=KKc7cS%E+8@LeI1 z5idP*RN-zYME4!#d%^z=g#$aieHTCOari-S+?-`Ce+c|phyM}$HqJJ7dV2&vuKmqX zel)a5h|A4s)(Ss{!U3FIYw#2N$lmyHS&@3UOiqS(#ni2Fc}h6zt6#?Df>ZOpfU$V< zdLfqHfIuq_3JQ;QtWxP>jhgc_YFne9_2!#b<;l-WO8$RLHd%QrH9q*l_?J1OXlla9@$}_g~B$Y7-IB z#rzn!6eqgJVF~VKJ_vbvQ$%E$bq=hFi2eW%1h}u*B;Z|2z@d;hcf<);gn&_pC3u|K z=Ewwd{nt=NP=cqKF9plPAyr=`FD#}*yn+Ju7pY8@T-$B~D)twN#@QwbtRPa%yJ0Z5 zONt(5eUi6I^_MESNf*=Cp^z9a64R_e?12+y#^L;%agugOkz{TIm!_p?bXbC0m@k0b zyIP920H*>xS;ocmJ{Z(xrI=y`;zsa{!xdmZZW2WD7&y-SIyinEB6PSEoMK+N6Iqs% zqBFoq4yB zBSrURjsZo`>o7%-a|}{2=y&84!2t7la3}|*7-7B)9NQ;VSwe1FNrmKQQkcX!sxU|N z*{hIcZz9Vaj=d>W1_qnS;D<0c>+K>j!V1LafX5sr`MATy;K^fIl~v0sU2aQn!JS=5 z)r^2kCwaH3d|ZAlF&p z5pobmt~*=-?qPnB2Fvcz&ez%$I(L4R(gFZ?Q_Ph1L)Gv5s^OZXz;a1}V| za17j7#aH4O3^vsHBE<^CrQlBHvOe%)Uvx9uyH_vsMUcmue31)qAMx!Ows8+~KhVNM@mxOe-|CS>Cd_;_(3PXwbD|k*Dml^Nw6XKF!TrK3(j83M+be+mWpeniY@R)1J5KXu022#a*YMCWpoNXy*;POLC;{{~clcf~{p z^Saw{d5=nU>k|2EeOJDa0>-5nY%&Ix$Bt!n>r%OPHI+5< zF8U7!R^H95n-_~iBFnrEI&+#rqM!L6z~wC=G2n0|c<=~!qK=J#Q4aVH3?gTT#3b_p za=0ZVrkMXi@-8&}>8SDBa^eW9Z(JbH3JLYf1+qCLhD*1|kBU7~y??m8TV7O@p<$pf zxuXr?CxIh}FucJ0n=gSU&G1kuh0z8*oteGIX08M$S+4UYD~{x49)4w?IFgsd^?k_j zZZ@zAJ;K2M`vS@2hBm|<>$eE9%?vw*l9j80#jAmaI8^xM6mElo+*2gFSiy3SWmSH?+`NtQbquO!Z!8kMzA+^(24@4D zV_tX{&O=9$=nwE+nhSzf*y$xI0`J`3lB6l}l*D_jJHk}X0^I$Q&ua(E7S z#$nP=6q^KJ4g1JeAzA{Q3h-p{MaJL-7_90RdH{?PL@dGalB06s1haoPB1j{G0H-Xg zD{qvWJvxWysi$s~pVRr=4J+?)oSxYxpDhmtn8vL1>BlA4v?eM#nV*9FX`NBg3vbrIM*beI09~A z{#)?EQ49k*90PYU?}mK+p|I$7_;_%dS@4#1UdA~11QcoxquZSoh-ZWQm>;+dx04BU zy9aoX`6lRl)97Un@OXfG6DGbRIg;UkKfpl@ToSEi;uRvklb$IYrn-mhaf)mU(#YsmVgS;icDdu0ne#Nec=wSW` ze8$d@=yte$XDB0j0|seFLBb#x;5_sB-$fO1H>$u6mxBi#mf#WQe}KN%j|mFLa3&-s zjtM{tYfKJ|z+l5jNTeNJ3(h%A@;-+pxIe%H%#r)>q}`7MIsCB<0!E#H6nNZWGMI2! zf~NvJ&Ab&6_=iGb#^DOEUuzDChsCtRP2d@a zSA%`rcIh5t_2Ujp$dgAoBTh#^8wV`AAJ=^%EII?+72q-E%b?%Z7#8CWp8=k9xE(z0 zFv(|*Fs^@z0DrzoF*ziTGY^6Nwy;PzTn=t@Sb|%a-+{ijF)TU)+!^3$=94lH2+_VZ zjQXufz)9dHunqCmojHmH@Xge+B;5?LdMYUIp%C&X7VY0=gXqGU#zwg3|%c zF+UA^c^#%w1$cn@0OVdSEJg!7X1GF>K8Om;eHeshg)gXkZomS+(wxcj>g- zoBGUBV`@(pjZRY**e%O^7o{zaoM*P@F3)VErqO7!w>%o)-bSG|-yye!u2K)*A?tNo zz7OAvPR1;e<(w9ZYYSSg%!k42w|XM!@bTa_<^zzEe#((c$Z1fU(n9tf;2!2p-@}#H zjh6Isu7prPGXin}g+7NRC2kKqPr7k^{V2^>CY+V9Z1P>#?bOufIBgoPL4l-{>n5u z85~{??qRl#fL@2G5kRBy)}HkHRymb^s-*Wj2Gj@`V785bL5HakFy=5d0>&MtM!+Pq zZ3Ij)+d95z=7MDd;>{+7mIuMy2)I*zS(h_gb8p0y06G}UZ2_KSwl(=Fhp8q%&1`G( zGY(TtUaU6$NlrC+f3=vaBeXU7q*YMoeM{D^qqNvtPA7}(Eho)P_Y%5y(2VIq#}exF z=;FzTO+Hh{Q=wz2(6Mw^p<@XbLU*BK36Ix8$5NqVsnD@h=vdM{QH74BLdQ~}W69@k z7V2m!bS&u}qC&@#ZkO7Aq(a9M8tjFRCF+l`9Z7|brTDgAKe*k|qTQIcB@`^q1*M*@~ z=EYzSBblQC9v9+#9idg|4w(d;4}*p@B6RpfaIeE1;H<+Jg9nM#^AAx!KGHMB`k(vo>R5m~n4f6JarQ?VfqX3`5?zjzZryAAKGGTB#sE(-Tl*P$xt<(Q zKp8pojKjMqL5nd$#JranV_sS2XTVh_o0sXyo;U&?VEISj4R2#=j>9K_$Cz=R^)~Kh zOoTg7MUm5(n!^f~F^x?pP`?~DgMqoCV;P-+$H?{cD8c zRG-)*|3;q#J@T|$h*4==ei0@cFs3r*@Co1vheTCV-V>)-v9fUF_uI5XO?l$5sjKVv-3_QsE5ajD{s~B;3 zHF(@%l20(3+`^!HfM-hF-1$6CWvNMk&Mj~vz*EfOAt6rBp=daKJb1?8cCbI&__O+P zhhvZ@XY=Jk3KY=73OWHhF=`s%&H(EvV8~s&=_z2~i{Y;wH^;6rlK|otu$}_u$kQ2C z&{?zx1C9aCGwTHH!zl>xNPu&36XA~#VcS7WtZ=vL##8d#9;RX5o z;H8H#63;v`T)tPn5!ysDx<&eNJkz+q>RTqbAM6hG>DfBaU-1ZLdnNIVWBnCs!#-Jy z^j^A;M)#m`^YFq)<;u_!`u%BW%Q{RRr%3}2>q!N~>dGI=+H**>8>%#eLr^Qv;dp@c zXg=h2qZM{uWgxr4bHDK1FFf~!C_MLf;5l7*?xUGQ)%)#uOc$Q}h3EeNGtYfmNpBsh z3$#_^cZ(YO1={=7`%HVEf^>WTK1}vzm5(E=?s2>!;IIUDGyhk}BR`MoH^g8i5ugJh7t-Qpp!R0q6>Ksg@OuP~9qVf=X$n1{y(ht+M*$<3Qc)bJ?cp2n0a zv|e4FV%`pU{B2C>bGQL4(3U3s?P}qE`Q=cxx_iH@$L@*!GP8sPx53cEgmEN8D_W*T zw3e%F&*KdY`l;LBhtUs!rk=~x6eRvRT-8Tmho&F#q93BUjs6hdVF{+DA;~vFzOf7b z9FBoI1Kj0{25q3L+vq^)Wd+OhMo+c6;syEp3vD_C(vdDU@$@4exf3(a1KiI%3Au;< zjgbJ4SyuP{5-nD8-8PIVjhNCoi8hn_au4pEN0?sj5oq#QIlbKDGuxMYs7Xa~`*Kes zvwfGRBfy=dXp@nFeYvNH6^LuL;Eg|r>E)iR!}M}bKeK(gXC%O50q#MQisE|#{#SJi zk)D0V(Ey2nyrV!&ujP<|eYs~Oz+;w&TYrV>zb@2YLT>u#&6!5SgmRZyf~lcGtiWDf zR5w&yPR?Puy7vHH_9D?JR8MKcwN`0F#B0EDX5DSE2A5WVTbOmv%gI>(Ex=vG>YNwR zg+iqJy@G&i5*D}PL-2Hg^M$1=^uCwUrj4~@*W0Cxmc` zL4ZIj)p9j>fO-C7Fla$9fWu3`qYhVtsj2GPH!_nw$@`c;4_<*Tw0wpG?nl7f4n)X| z`-|Rara3vBguE<|EOS_bY2rD_BahSkZuH?f90yN1EWy(bZw6-sMiH%p6awPRJ#aX8 z5J$-T2zb*zoI{7(!IR7-Pr%Ozs+i0vaKk8idXmQfXTh`@S{ri)T$&7LM3)oLkwi}* z^RuLYp4(oBKMqbaSN{ZZ^k3&3-URMr?k0H?dUly9LJ$52nIDE+rZRZ{;+TN2m|%Vv z3iLX}bb!S|b3|4?$lUp)5GUeoh7pICfX5uJ1t%Je{Q&7_y6`%L6R-r_;&3fE%lsY; zD$o~7lk@465w8OGIb02<$@(O}U=$H_BL^H_0v>cYQ;Ptx$QXQw47TI_4TqP2cr7^T=+{EO*O7NXo@Ra*{+Hc{C)ZKFSQI0m?`VLi0uL}ReF{fBW!CVcBeI1k zzStz#bf)7Spa5eHKioRky)4hw6`(8NCXz!&Eiq=6^On`n%d%aoJ`Gh5-MKwWjjH9e zWmWz=bj49(bXVL?EPJqwBD?f=@{@BZ_^1 zcCEF5N!|?k(#5DA5vwN->3ZBNhtRD?(iU~^EAqGHv|x0h+FPjhqC^y`y@hITq1s!h z_Hwn>R)Y)G-a@svpKGm!YA;oT3)NoK7XI(9_TGbP@0UVCZ+r3k8MW{o^!D!ksp;*d zAl=)W#U}-zMEn~5R_wxB^A1aJ53}}#|9Y9re}>-SJl2Y5-U62RLcjp?XTe^-6r%wi zM=M1~XcfAU&b{hS@1O&g((z5$#eR-YBZN)5hQa0grJjvN@?mw`yYhES>F1jiB|ZU= zj~G?U14mfBH;LIJq`UU#m@9)xH(i)oWO+6iukmXB6y(KNC%%{YM)1e6PJA{X&oS#! zq9oRV4{$&86zq>jPcG(0Wpo4-P>Fy+M`0Ow)L}B9$x@U+9l;uWQ-&GYsJAKR6;Gop zP2d|d4%dTc9Bu^rMd%$Qht_~5PwiE&{D)k>kb=JqKWA<6^%7G@;zgG;tthout$Ppe zBJWjKyoc7+UiJ0&(7Ga}XViKB6J5aca{~5$kFVEfvMj+gM~L{}Aur#7<91krX^s%d z=ll;LI(yJ};_yZ=)^pNwd)yPux(Y|bbF|(QIj{i&-P6Q>2Zv{;(GGTaEjaFQ3Y>J9 z^cx+P;8cJ+nDO_8#?sK^?fis zZl5n`Z7O0q`|^EstTO)@@-<1UN9J%0+|ArHhReSNtAjCH|Gfcux-m(hudi8z`N(!|ePtR)RynzU9-wG_YWqGY%)fJ|;+! zKfA@5bu#Y5mvPEOn9}i)%$!boM6joUV2#GGe77u-C9XqdlBjLDYt(H63QPTdyrKUd|t^7-x) zRpnvdlqnlaibXb-ZswNf5G&rhr>~ljtFyqf(pZ^{c{}*y*G2VLOh`_ADww`vLTv5v z6%)-*lfNtmA?Ao86QG_7`0@(&!QwgHx7OYsBBIkLZ;BxLNJNQU)yQ zQny9?cI_nxk%!U6M3wPk8DAyQ5$4oe5r3ujG7QxU41!_=LyLx0Yt*k@OX;@x(1EcG zV%=x1usNas;@)J>iz4q`LoX=0-Svk*W5XZo@S{7N9JWp%R z;Da>Gn^mjlx9gI9Ekp~NAW9fj;&L#JppfZvi2V2{ssj#3z`e{rfxbK&-IvV2Aw~x- zji`|QpNV_Wt;lRkdOm^05=f!w1t{#q+)`F3QlTQ>JYu#(w5T6lsny2U*T746qbrs9 z5%3v<=nH1H`fbem39$!D;{>>qx$KuXef!Xl8DP9eXdW?ELDZN8oC1T+3H0?ce+xYS zAQI@vlaMDJJ`vpFFxj^;KSB2UkwE5u2IC8JB3pBjiSXYLuwW3&v9Lm79M?1YKKmSw zfb+~3LLS4^eOi%?#Uqg3?5GjXOgF7s%g%$p0PphgcE6_b;tt7MtS?h9msoRN(qz)&+=#td2T&|vi zsrGWYdbPrD=bsDb_>avchjv5Lmy+%VdS18d&hF=9Y;V;ho z7=Ft`M{`Ci;y)?`q_1=8@X?&nIW=``&Xi={KQ?PB!QJeDva%U$bB0)gds(h?suGL0 zGyfI|n!VV3K#3AS+yu5cRs0eX--bSJM?bR!3V9A#28D)=7;bS`f=2@kzxoLO4*FgP zMo$7PI47+f^Mkd%&e^RYaR7GTLKwCZ@fzSA{E6#4RaReA6@Kj{GgM0g{pp~?=EWT1 z@2$a%#~Jftj%5k%hCOloWt8yu&5JoM(;>S&!;GETkQ$Po4g0DR{VAbSbwI~jqAqQ4INvu9t?f*VQ!9OCuYdkR-HEP5WFuhwt@;`ysjUXo-J`RjS zJ0hPC7M|hbeh2$8%ybWMGQb!@&1eUg!=P+G9J0c0us4pO!6M_(ax%b^%#+YRdkPc9 z9hP8PZ-Nehyds5J$qq|!miaZv8_veGYUX+W zgd^<4wCMm31UN%q38EwHg29u@%-viB%jN-ULoLO!5wg%fQ_Z)8{hM%*Rb)X2a)kiX5&0 z_c^>OgMhpfa4NXpVF{)Yd&*)xQM}iz+Tk*d$h%DA*Tgp<0mtXz-{IrHgAQj%fySrF zz-IASfN8v%jc_xf{bV4lf2Lj&cS=0=SGgpz2kW+J0On z0qzL!B=Z^2m;13|1he4HFEt6U@;I}V)5;)p0H1y}LpK^+r$+(|w*{D9J0pYp5#g$X z7*}9^3A_g53eyg+0{gR#zLnF`A!KiJIB7Y zMve%K>NuPLXB{Saj(OqfFz_m|PzDEB9zoB`E1I-EA6~ZGzeYoMN^K>L^DEa#PghC{Tj3%x@v{Vi@VmIeaFVhV;q) zWziAXEyuj-b=WszZiT}MaNc2()910s{$l7?pN$2^Mx20?5kTL^vI@(F~zK3svbl#5=P&$ALMEyube^x<54{qfc!Jph{k}zB=Zl{!u4nilkN}Hw)K3^ z`2^e)Pon))W88jGJ+~Qg2=;klxY@O7PUrom)el>Z{9$vKZ9y>9s{f_n)OH2 zH+RF7Ss%=O7+|e)4h|eA9xmPBKN&42Kc6JKE_|wUDZ3bzsPBn@thp9%9SaDQd0!})tYb-1Hy0Q_Z0t)oO z6E}ixhbe+Avn{qchpE^OI!wiS#9@+8tXO6uxaE(yi%sLYVFk-GmeqM3{v{_+TBe~I zNux1yoH4cB7~mP^cU~I)ShHUf*603|e?>eaxL#KqW6Kkk)xypGRr9h?w~KK^3(n>9SRxj6NUP+GlJRq|wLL=DX?$$(ntVb`XHXg8)>6WArx z5bXw3YjsH`JkzH3R+oGwJk_pN*OYvPq&1LssMcDoG(D~)T3ILm#hz+t*)e9P7Tp6tKM2( Hvh05W$Nns~