// Copyright (c) 2016-2021 Apple Inc. All rights reserved. // // @APPLE_OSREFERENCE_LICENSE_HEADER_START@ // // This file contains Original Code and/or Modifications of Original Code // as defined in and that are subject to the Apple Public Source License // Version 2.0 (the 'License'). You may not use this file except in // compliance with the License. The rights granted to you under the License // may not be used to create, or enable the creation or redistribution of, // unlawful or unlicensed copies of an Apple operating system, or to // circumvent, violate, or enable the circumvention or violation of, any // terms of an Apple operating system software license agreement. // // Please obtain a copy of the License at // http://www.opensource.apple.com/apsl/ and read it before using this file. // // The Original Code and all software distributed under the License are // distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER // EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, // INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. // Please see the License for the specific language governing rights and // limitations under the License. // // @APPLE_OSREFERENCE_LICENSE_HEADER_END@ #ifndef KERN_BACKTRACE_H #define KERN_BACKTRACE_H // Kernel and user space backtracing (call stack walking) functions. // // For the current kernel stack, call backtrace from any context: // // ```c // #define MAX_STK_LEN (8) // // void *ret_addrs[MAX_STK_LEN] = { 0 }; // backtrace_info_t info = BTI_NONE; // unsigned int stk_len = backtrace(ret_addrs, MAX_STK_LEN, NULL, &info); // for (unsigned int i = 0; i < stk_len; i++) { // printf("%p -> ", ret_addrs[i]); // } // printf("%s\n", (info & BTI_TRUNCATED) ? "TRUNC" : "NULL"); // ``` // // For user stacks, call backtrace_user from a faultable context: // // ```c // uintptr_t ret_addrs[MAX_STK_LEN] = { 0 }; // struct backtrace_user_info info = BTUINFO_INIT; // unsigned int stk_len = backtrace_user(ret_addrs, MAX_STK_LEN, NULL, &info); // if (info.btui_error != 0) { // printf("user space%s stack is %u frames deep\n", // (info->btui_info & BTI_TRUNCATED) ? " truncated" : "", stk_len); // } // ``` // // Refer to documentation in backtrace(9) for more information. #include #include #include #include // XXX Surgically include just the errno_t definition, so this can still be used // by Mach. #include __BEGIN_DECLS // backtrace_flags_t change how the backtraces are recorded. __options_decl(backtrace_flags_t, uint32_t, { BTF_NONE = 0x0, // BTF_KERN_INTERRUPTED backtraces the interrupted kernel stack. BTF_KERN_INTERRUPTED = 0x1, }); // The copy function is used to copy call stack frame and other information from // the target call stack. If an error is returned, the backtrace is aborted. typedef errno_t (*backtrace_user_copy_fn)(void *ctx, void *dst, user_addr_t src, size_t size); // This copy function returns an error when a copy attempt is made, effectively // limiting the user backtrace to the PC. errno_t backtrace_user_copy_error(void *ctx, void *dst, user_addr_t src, size_t size); // Parameters that control how the backtrace is taken. struct backtrace_control { backtrace_flags_t btc_flags; // The frame address to start backtracing from; set to 0 to start from the // calling frame. uintptr_t btc_frame_addr; // A thread to backtrace user stacks of; must be either the current thread // or one which has been suspended. void *btc_user_thread; // A functions to call instead of the default copyin routine for // user space backtracing. backtrace_user_copy_fn btc_user_copy; // A context to pass to the user copy routine. void *btc_user_copy_context; // Apply an offset to each address stored by the backtracer. int64_t btc_addr_offset; }; // Use this offset when walking an async stack, so symbolicators that subtract 1 // from each address to find the call site see valid symbols, instead of // whatever function is at a lower address than the function pointer. #define BTCTL_ASYNC_ADDR_OFFSET ((int64_t)1) #define BTCTL_INIT \ ((struct backtrace_control){ \ .btc_flags = BTF_NONE, \ .btc_frame_addr = 0, \ .btc_user_thread = NULL, \ .btc_user_copy = NULL, \ .btc_user_copy_context = NULL, \ .btc_addr_offset = 0, \ }) // backtrace_info_t provides information about the backtrace. __options_decl(backtrace_info_t, uint32_t, { BTI_NONE = 0x0, // BTI_64_BIT is set when the backtrace is made up of 64-bit addresses. BTI_64_BIT = 0x1, // BTI_TRUNCATED is set when the backtrace has been truncated, either due // to an error copying data, an invalid frame pointer, or running out of // buffer space. BTI_TRUNCATED = 0x2, }); // Backtrace the current thread's kernel stack. unsigned int backtrace(uintptr_t *bt, unsigned int btlen, struct backtrace_control *ctl, backtrace_info_t *info_out) __attribute__((noinline)); // backtrace_pack_t changes the packing scheme for backtraces. __enum_decl(backtrace_pack_t, uint32_t, { // Leave the addresses alone. BTP_NONE = 0x0, // Subtract the kernel base address and store each offset in 4 bytes. BTP_KERN_OFFSET_32 = 0x01, }); // Backtrace the current thread's kernel stack and store in a packed // representation. size_t backtrace_packed(backtrace_pack_t packing, uint8_t *bt, size_t btsize, struct backtrace_control *ctl, backtrace_info_t *info_out) __attribute__((noinline)); // Convert an array of addresses to a packed representation. size_t backtrace_pack(backtrace_pack_t packing, uint8_t *dst, size_t dst_size, const uintptr_t *src, unsigned int src_len); // Convert a packed backtrace to an array of addresses. unsigned int backtrace_unpack(backtrace_pack_t packing, uintptr_t *dst, unsigned int dst_len, const uint8_t *src, size_t src_size); // backtrace_user_info describes a user backtrace. struct backtrace_user_info { backtrace_info_t btui_info; errno_t btui_error; // The index where the start of the async call stack was found. unsigned int btui_async_start_index; // The frame address that can be backtraced to follow the async call stack. uintptr_t btui_async_frame_addr; // The frame address to use to resume the backtrace when the call stack is // truncated by the size of the passed-in buffer. uintptr_t btui_next_frame_addr; }; #define BTUINFO_INIT \ ((struct backtrace_user_info){ \ .btui_error = 0, \ .btui_info = BTI_NONE, \ .btui_async_start_index = 0, \ .btui_async_frame_addr = 0, \ .btui_next_frame_addr = 0, \ }) // Backtrace a thread's user stack. unsigned int backtrace_user(uintptr_t *bt, unsigned int btlen, const struct backtrace_control *ctl, struct backtrace_user_info *info_out); __END_DECLS #endif // !defined(KERN_BACKTRACE_H)