vlib: stack trace and signal handler improvements
- use libunwrap which seems to be industry standard - display traceback on console if running interactive or with syslog disabled (color output unless nocolor specified) - print hexdump of offending code - print library filename for each stack frame Type: improvement Change-Id: I61d3056251b87076be0578ccda300aa311c222ef Signed-off-by: Damjan Marion <damarion@cisco.com>
This commit is contained in:
@ -27,6 +27,7 @@ ForEachMacros:
|
||||
- 'foreach_vlib_frame_bitmap_set_bit_index'
|
||||
- 'FOREACH_ARRAY_ELT'
|
||||
- 'RTE_ETH_FOREACH_DEV'
|
||||
- 'foreach_clib_stack_frame'
|
||||
- 'foreach_vnet_dev_rx_queue_runtime'
|
||||
- 'foreach_vnet_dev_counter'
|
||||
- 'foreach_vnet_dev_port_rx_queue'
|
||||
|
2
Makefile
2
Makefile
@ -72,7 +72,7 @@ DEB_DEPENDS += debhelper dkms git libtool libapr1-dev dh-python
|
||||
DEB_DEPENDS += libconfuse-dev git-review exuberant-ctags cscope pkg-config
|
||||
DEB_DEPENDS += gcovr lcov chrpath autoconf libnuma-dev
|
||||
DEB_DEPENDS += python3-all python3-setuptools check
|
||||
DEB_DEPENDS += libffi-dev python3-ply
|
||||
DEB_DEPENDS += libffi-dev python3-ply libunwind-dev
|
||||
DEB_DEPENDS += cmake ninja-build python3-jsonschema python3-yaml
|
||||
DEB_DEPENDS += python3-venv # ensurepip
|
||||
DEB_DEPENDS += python3-dev python3-pip
|
||||
|
@ -40,6 +40,8 @@
|
||||
#include <vlib/unix/unix.h>
|
||||
#include <vlib/unix/plugin.h>
|
||||
#include <vppinfra/unix.h>
|
||||
#include <vppinfra/stack.h>
|
||||
#include <vppinfra/format_ansi.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
@ -96,21 +98,43 @@ static u8 *syslog_msg = 0;
|
||||
int vlib_last_signum = 0;
|
||||
uword vlib_last_faulting_address = 0;
|
||||
|
||||
static void
|
||||
log_one_line ()
|
||||
{
|
||||
vec_terminate_c_string (syslog_msg);
|
||||
if (unix_main.flags & (UNIX_FLAG_INTERACTIVE | UNIX_FLAG_NOSYSLOG))
|
||||
fprintf (stderr, "%s\n", syslog_msg);
|
||||
else
|
||||
syslog (LOG_ERR | LOG_DAEMON, "%s", syslog_msg);
|
||||
vec_reset_length (syslog_msg);
|
||||
}
|
||||
|
||||
static void
|
||||
unix_signal_handler (int signum, siginfo_t * si, ucontext_t * uc)
|
||||
{
|
||||
uword fatal = 0;
|
||||
int color =
|
||||
(unix_main.flags & (UNIX_FLAG_INTERACTIVE | UNIX_FLAG_NOSYSLOG)) &&
|
||||
(unix_main.flags & UNIX_FLAG_NOCOLOR) == 0;
|
||||
|
||||
/* These come in handy when looking at core files from optimized images */
|
||||
vlib_last_signum = signum;
|
||||
vlib_last_faulting_address = (uword) si->si_addr;
|
||||
|
||||
if (color)
|
||||
syslog_msg = format (syslog_msg, ANSI_FG_BR_RED);
|
||||
|
||||
syslog_msg = format (syslog_msg, "received signal %U, PC %U",
|
||||
format_signal, signum, format_ucontext_pc, uc);
|
||||
|
||||
if (signum == SIGSEGV)
|
||||
if (signum == SIGSEGV || signum == SIGBUS)
|
||||
syslog_msg = format (syslog_msg, ", faulting address %p", si->si_addr);
|
||||
|
||||
if (color)
|
||||
syslog_msg = format (syslog_msg, ANSI_FG_DEFAULT);
|
||||
|
||||
log_one_line ();
|
||||
|
||||
switch (signum)
|
||||
{
|
||||
/* these (caught) signals cause the application to exit */
|
||||
@ -120,11 +144,17 @@ unix_signal_handler (int signum, siginfo_t * si, ucontext_t * uc)
|
||||
*/
|
||||
if (unix_main.vlib_main && unix_main.vlib_main->main_loop_exit_set)
|
||||
{
|
||||
syslog (LOG_ERR | LOG_DAEMON, "received SIGTERM, exiting...");
|
||||
syslog_msg = format (
|
||||
syslog_msg, "received SIGTERM from PID %d UID %d, exiting...",
|
||||
si->si_pid, si->si_uid);
|
||||
log_one_line ();
|
||||
unix_main.vlib_main->main_loop_exit_now = 1;
|
||||
}
|
||||
else
|
||||
syslog (LOG_ERR | LOG_DAEMON, "IGNORE early SIGTERM...");
|
||||
{
|
||||
syslog_msg = format (syslog_msg, "IGNORE early SIGTERM...");
|
||||
log_one_line ();
|
||||
}
|
||||
break;
|
||||
/* fall through */
|
||||
case SIGQUIT:
|
||||
@ -144,26 +174,75 @@ unix_signal_handler (int signum, siginfo_t * si, ucontext_t * uc)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Null terminate. */
|
||||
vec_add1 (syslog_msg, 0);
|
||||
|
||||
if (fatal)
|
||||
{
|
||||
syslog (LOG_ERR | LOG_DAEMON, "%s", syslog_msg);
|
||||
int skip = 1, index = 0;
|
||||
|
||||
/* Address of callers: outer first, inner last. */
|
||||
uword callers[15];
|
||||
uword n_callers = clib_backtrace (callers, ARRAY_LEN (callers), 0);
|
||||
int i;
|
||||
for (i = 0; i < n_callers; i++)
|
||||
foreach_clib_stack_frame (sf)
|
||||
{
|
||||
vec_reset_length (syslog_msg);
|
||||
if (sf->is_signal_frame)
|
||||
{
|
||||
int pipefd[2];
|
||||
const int n_bytes = 20;
|
||||
u8 *ip = (void *) sf->ip;
|
||||
|
||||
syslog_msg =
|
||||
format (syslog_msg, "#%-2d 0x%016lx %U%c", i, callers[i],
|
||||
format_clib_elf_symbol_with_address, callers[i], 0);
|
||||
if (pipe (pipefd) == 0)
|
||||
{
|
||||
/* check PC points to valid memory */
|
||||
if (write (pipefd[1], ip, n_bytes) == n_bytes)
|
||||
{
|
||||
syslog_msg = format (syslog_msg, "Code: ");
|
||||
if (color)
|
||||
syslog_msg = format (syslog_msg, ANSI_FG_CYAN);
|
||||
for (int i = 0; i < n_bytes; i++)
|
||||
syslog_msg = format (syslog_msg, " %02x", ip[i]);
|
||||
if (color)
|
||||
syslog_msg = format (syslog_msg, ANSI_FG_DEFAULT);
|
||||
}
|
||||
else
|
||||
{
|
||||
syslog_msg = format (
|
||||
syslog_msg, "PC contains invalid memory address");
|
||||
}
|
||||
log_one_line ();
|
||||
foreach_int (i, 0, 1)
|
||||
close (pipefd[i]);
|
||||
}
|
||||
skip = 0;
|
||||
}
|
||||
|
||||
syslog (LOG_ERR | LOG_DAEMON, "%s", syslog_msg);
|
||||
if (skip)
|
||||
continue;
|
||||
|
||||
syslog_msg = format (syslog_msg, "#%-2d ", index++);
|
||||
if (color)
|
||||
syslog_msg = format (syslog_msg, ANSI_FG_BLUE);
|
||||
syslog_msg = format (syslog_msg, "0x%016lx", sf->ip);
|
||||
if (color)
|
||||
syslog_msg = format (syslog_msg, ANSI_FG_DEFAULT);
|
||||
|
||||
if (sf->name[0])
|
||||
{
|
||||
if (color)
|
||||
syslog_msg = format (syslog_msg, ANSI_FG_YELLOW);
|
||||
syslog_msg =
|
||||
format (syslog_msg, " %s + 0x%x", sf->name, sf->offset);
|
||||
if (color)
|
||||
syslog_msg = format (syslog_msg, ANSI_FG_DEFAULT);
|
||||
}
|
||||
|
||||
log_one_line ();
|
||||
|
||||
if (sf->file_name)
|
||||
{
|
||||
if (color)
|
||||
syslog_msg = format (syslog_msg, ANSI_FG_GREEN);
|
||||
syslog_msg = format (syslog_msg, " from %s", sf->file_name);
|
||||
if (color)
|
||||
syslog_msg = format (syslog_msg, ANSI_FG_DEFAULT);
|
||||
log_one_line ();
|
||||
}
|
||||
}
|
||||
|
||||
/* have to remove SIGABRT to avoid recursive - os_exit calling abort() */
|
||||
@ -175,9 +254,6 @@ unix_signal_handler (int signum, siginfo_t * si, ucontext_t * uc)
|
||||
else
|
||||
os_exit (1);
|
||||
}
|
||||
else
|
||||
clib_warning ("%s", syslog_msg);
|
||||
|
||||
}
|
||||
|
||||
static clib_error_t *
|
||||
|
@ -13,6 +13,34 @@
|
||||
|
||||
enable_language(ASM)
|
||||
|
||||
##############################################################################
|
||||
# find libdl
|
||||
##############################################################################
|
||||
vpp_find_path(LIBDL_INCLUDE_DIR dlfcn.h)
|
||||
vpp_find_library(LIBDL_LIB NAMES dl)
|
||||
|
||||
if (LIBDL_INCLUDE_DIR AND LIBDL_LIB)
|
||||
message(STATUS "libdl found at ${LIBDL_LIB}")
|
||||
list(APPEND VPPINFRA_LIBS ${LIBDL_LIB})
|
||||
else()
|
||||
message(FATAL_ERROR "libdl not found")
|
||||
endif()
|
||||
|
||||
##############################################################################
|
||||
# find libunwind
|
||||
##############################################################################
|
||||
vpp_find_path(LIBUNWIND_INCLUDE_DIR unwind.h)
|
||||
vpp_find_library(LIBUNWIND_LIB NAMES unwind libunwind)
|
||||
|
||||
if (LIBUNWIND_INCLUDE_DIR AND LIBUNWIND_LIB)
|
||||
message(STATUS "libunwind found at ${LIBUNWIND_LIB}")
|
||||
list(APPEND VPPINFRA_LIBS ${LIBUNWIND_LIB})
|
||||
add_definitions(-DHAVE_LIBUNWIND=1)
|
||||
else()
|
||||
message(WARNING "libunwind not found - stack traces disabled")
|
||||
add_definitions(-DHAVE_LIBUNWIND=0)
|
||||
endif()
|
||||
|
||||
##############################################################################
|
||||
# Generate vppinfra/config.h
|
||||
##############################################################################
|
||||
@ -42,12 +70,10 @@ add_definitions(-fvisibility=hidden)
|
||||
set_source_files_properties( cJSON.c jsonformat.c PROPERTIES
|
||||
COMPILE_DEFINITIONS " CJSON_API_VISIBILITY " )
|
||||
|
||||
|
||||
##############################################################################
|
||||
# vppinfra sources
|
||||
##############################################################################
|
||||
set(VPPINFRA_SRCS
|
||||
backtrace.c
|
||||
bitmap.c
|
||||
bihash_all_vector.c
|
||||
cpu.c
|
||||
@ -80,6 +106,7 @@ set(VPPINFRA_SRCS
|
||||
rbtree.c
|
||||
serialize.c
|
||||
socket.c
|
||||
stack.c
|
||||
std-formats.c
|
||||
string.c
|
||||
time.c
|
||||
@ -142,6 +169,7 @@ set(VPPINFRA_HEADERS
|
||||
fifo.h
|
||||
file.h
|
||||
format.h
|
||||
format_ansi.h
|
||||
format_table.h
|
||||
hash.h
|
||||
heap.h
|
||||
@ -175,6 +203,7 @@ set(VPPINFRA_HEADERS
|
||||
smp.h
|
||||
socket.h
|
||||
sparse_vec.h
|
||||
stack.h
|
||||
string.h
|
||||
time.h
|
||||
time_range.h
|
||||
@ -229,22 +258,9 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD")
|
||||
)
|
||||
endif()
|
||||
|
||||
if("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD")
|
||||
option(VPP_USE_EXTERNAL_LIBEXECINFO "Use external libexecinfo (useful for non-glibc targets)." ON)
|
||||
else()
|
||||
option(VPP_USE_EXTERNAL_LIBEXECINFO "Use external libexecinfo (useful for non-glibc targets)." OFF)
|
||||
endif()
|
||||
option(VPP_USE_LIBUNWIND "Use libunwind for backtrace." OFF)
|
||||
|
||||
if(VPP_USE_EXTERNAL_LIBEXECINFO)
|
||||
set(EXECINFO_LIB execinfo)
|
||||
elseif(VPP_USE_LIBUNWIND)
|
||||
set(EXECINFO_LIB unwind)
|
||||
add_compile_definitions(USE_LIBUNWIND)
|
||||
endif()
|
||||
add_vpp_library(vppinfra
|
||||
SOURCES ${VPPINFRA_SRCS}
|
||||
LINK_LIBRARIES m ${EXECINFO_LIB}
|
||||
LINK_LIBRARIES m ${VPPINFRA_LIBS}
|
||||
INSTALL_HEADERS ${VPPINFRA_HEADERS}
|
||||
COMPONENT libvppinfra
|
||||
LTO
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Cisco and/or its affiliates.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef included_asm_x86_h
|
||||
#define included_asm_x86_h
|
||||
|
||||
#include <vppinfra/format.h>
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u8 code;
|
||||
u8 type;
|
||||
};
|
||||
u8 data[2];
|
||||
} x86_insn_operand_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* Instruction name. */
|
||||
char *name;
|
||||
|
||||
/* X86 instructions may have up to 3 operands. */
|
||||
x86_insn_operand_t operands[3];
|
||||
|
||||
u16 flags;
|
||||
#define X86_INSN_FLAG_DEFAULT_64_BIT (1 << 0)
|
||||
#define X86_INSN_FLAG_SET_SSE_GROUP(n) ((n) << 5)
|
||||
#define X86_INSN_FLAG_GET_SSE_GROUP(f) (((f) >> 5) & 0x1f)
|
||||
#define X86_INSN_FLAG_SET_MODRM_REG_GROUP(n) (((n) & 0x3f) << 10)
|
||||
#define X86_INSN_FLAG_GET_MODRM_REG_GROUP(f) (((f) >> 10) & 0x3f)
|
||||
} x86_insn_t;
|
||||
|
||||
always_inline uword
|
||||
x86_insn_operand_is_valid (x86_insn_t * i, uword o)
|
||||
{
|
||||
ASSERT (o < ARRAY_LEN (i->operands));
|
||||
return i->operands[o].code != '_';
|
||||
}
|
||||
|
||||
#define foreach_x86_legacy_prefix \
|
||||
_ (OPERAND_SIZE, 0x66) \
|
||||
_ (ADDRESS_SIZE, 0x67) \
|
||||
_ (SEGMENT_CS, 0x2e) \
|
||||
_ (SEGMENT_DS, 0x3e) \
|
||||
_ (SEGMENT_ES, 0x26) \
|
||||
_ (SEGMENT_FS, 0x64) \
|
||||
_ (SEGMENT_GS, 0x65) \
|
||||
_ (SEGMENT_SS, 0x36) \
|
||||
_ (LOCK, 0xf0) \
|
||||
_ (REPZ, 0xf3) \
|
||||
_ (REPNZ, 0xf2)
|
||||
|
||||
#define foreach_x86_insn_parse_flag \
|
||||
/* Parse in 32/64-bit mode. */ \
|
||||
_ (PARSE_32_BIT, 0) \
|
||||
_ (PARSE_64_BIT, 0) \
|
||||
_ (IS_ADDRESS, 0) \
|
||||
/* regs[1/2] is a valid base/index register */ \
|
||||
_ (HAS_BASE, 0) \
|
||||
_ (HAS_INDEX, 0) \
|
||||
/* rex w bit */ \
|
||||
_ (OPERAND_SIZE_64, 0)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
#define _(f,o) X86_INSN_FLAG_BIT_##f,
|
||||
foreach_x86_insn_parse_flag foreach_x86_legacy_prefix
|
||||
#undef _
|
||||
} x86_insn_parse_flag_bit_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
#define _(f,o) X86_INSN_##f = 1 << X86_INSN_FLAG_BIT_##f,
|
||||
foreach_x86_insn_parse_flag foreach_x86_legacy_prefix
|
||||
#undef _
|
||||
} x86_insn_parse_flag_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* Registers in instruction.
|
||||
[0] is modrm reg field
|
||||
[1] is base reg
|
||||
[2] is index reg. */
|
||||
u8 regs[3];
|
||||
|
||||
/* Scale for index register. */
|
||||
u8 log2_index_scale:2;
|
||||
u8 log2_effective_operand_bytes:3;
|
||||
u8 log2_effective_address_bytes:3;
|
||||
|
||||
i32 displacement;
|
||||
|
||||
/* Parser flags: set of x86_insn_parse_flag_t enums. */
|
||||
u32 flags;
|
||||
|
||||
i64 immediate;
|
||||
|
||||
x86_insn_t insn;
|
||||
} x86_insn_parse_t;
|
||||
|
||||
u8 *x86_insn_parse (x86_insn_parse_t * p, u8 * code_start);
|
||||
format_function_t format_x86_insn_parse;
|
||||
|
||||
#endif /* included_asm_x86_h */
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
@ -1,269 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Cisco and/or its affiliates.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*
|
||||
Copyright (c) 2004 Eliot Dresselhaus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <vppinfra/clib.h>
|
||||
#include <vppinfra/error.h>
|
||||
|
||||
#ifdef __mips__
|
||||
|
||||
/* Let code below know we've defined _clib_backtrace */
|
||||
#define clib_backtrace_defined
|
||||
|
||||
#include <vppinfra/asm_mips.h>
|
||||
|
||||
__clib_export uword
|
||||
clib_backtrace (uword * callers, uword max_callers, uword n_frames_to_skip)
|
||||
{
|
||||
u32 *pc;
|
||||
void *sp;
|
||||
uword i, saved_pc;
|
||||
|
||||
/* Figure current PC, saved PC and stack pointer. */
|
||||
asm volatile (".set push\n"
|
||||
".set noat\n" "move %[saved_pc], $31\n" "move %[sp], $29\n"
|
||||
/* Fetches current PC. */
|
||||
"la $at, 1f\n"
|
||||
"jalr %[pc], $at\n"
|
||||
"nop\n"
|
||||
"1:\n"
|
||||
".set pop\n":[pc] "=r" (pc),
|
||||
[saved_pc] "=r" (saved_pc),[sp] "=r" (sp));
|
||||
|
||||
/* Also skip current frame. */
|
||||
n_frames_to_skip += 1;
|
||||
|
||||
for (i = 0; i < max_callers + n_frames_to_skip; i++)
|
||||
{
|
||||
mips_insn_opcode_t op;
|
||||
mips_insn_special_funct_t funct;
|
||||
i32 insn, rs, rt, rd, immediate, found_saved_pc;
|
||||
u32 *start_pc;
|
||||
|
||||
/* Parse instructions until we reach prologue for this
|
||||
stack frame. We'll need to figure out where saved
|
||||
PC is and where previous stack frame lives. */
|
||||
start_pc = pc;
|
||||
found_saved_pc = 0;
|
||||
while (1)
|
||||
{
|
||||
insn = *--pc;
|
||||
op = mips_insn_get_op (insn);
|
||||
funct = mips_insn_get_funct (insn);
|
||||
rs = mips_insn_get_rs (insn);
|
||||
rt = mips_insn_get_rt (insn);
|
||||
rd = mips_insn_get_rd (insn);
|
||||
immediate = mips_insn_get_immediate (insn);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
|
||||
case MIPS_OPCODE_sd:
|
||||
case MIPS_OPCODE_sw:
|
||||
/* Trace stores of return address. */
|
||||
if (rt == MIPS_REG_RA)
|
||||
{
|
||||
void *addr = sp + immediate;
|
||||
|
||||
/* If RA is stored somewhere other than in the
|
||||
stack frame, give up. */
|
||||
if (rs != MIPS_REG_SP)
|
||||
goto backtrace_done;
|
||||
|
||||
ASSERT (immediate % 4 == 0);
|
||||
if (op == MIPS_OPCODE_sw)
|
||||
saved_pc = ((u32 *) addr)[0];
|
||||
else
|
||||
saved_pc = ((u64 *) addr)[0];
|
||||
found_saved_pc = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case MIPS_OPCODE_addiu:
|
||||
case MIPS_OPCODE_daddiu:
|
||||
case MIPS_OPCODE_addi:
|
||||
case MIPS_OPCODE_daddi:
|
||||
if (rt == MIPS_REG_SP)
|
||||
{
|
||||
if (rs != MIPS_REG_SP)
|
||||
goto backtrace_done;
|
||||
|
||||
ASSERT (immediate % 4 == 0);
|
||||
|
||||
/* Assume positive offset is part of the epilogue.
|
||||
E.g.
|
||||
jr ra
|
||||
add sp,sp,100
|
||||
*/
|
||||
if (immediate > 0)
|
||||
continue;
|
||||
|
||||
/* Negative offset means allocate stack space.
|
||||
This could either be the prologue or could be due to
|
||||
alloca. */
|
||||
sp -= immediate;
|
||||
|
||||
/* This frame will not save RA. */
|
||||
if (i == 0)
|
||||
goto found_prologue;
|
||||
|
||||
/* Assume that addiu sp,sp,-N without store of ra means
|
||||
that we have not found the prologue yet. */
|
||||
if (found_saved_pc)
|
||||
goto found_prologue;
|
||||
}
|
||||
break;
|
||||
|
||||
case MIPS_OPCODE_slti:
|
||||
case MIPS_OPCODE_sltiu:
|
||||
case MIPS_OPCODE_andi:
|
||||
case MIPS_OPCODE_ori:
|
||||
case MIPS_OPCODE_xori:
|
||||
case MIPS_OPCODE_lui:
|
||||
case MIPS_OPCODE_ldl:
|
||||
case MIPS_OPCODE_ldr:
|
||||
case MIPS_OPCODE_lb:
|
||||
case MIPS_OPCODE_lh:
|
||||
case MIPS_OPCODE_lwl:
|
||||
case MIPS_OPCODE_lw:
|
||||
case MIPS_OPCODE_lbu:
|
||||
case MIPS_OPCODE_lhu:
|
||||
case MIPS_OPCODE_lwr:
|
||||
case MIPS_OPCODE_lwu:
|
||||
case MIPS_OPCODE_ld:
|
||||
/* Give up when we find anyone setting the stack pointer. */
|
||||
if (rt == MIPS_REG_SP)
|
||||
goto backtrace_done;
|
||||
break;
|
||||
|
||||
case MIPS_OPCODE_SPECIAL:
|
||||
if (rd == MIPS_REG_SP)
|
||||
switch (funct)
|
||||
{
|
||||
default:
|
||||
/* Give up when we find anyone setting the stack pointer. */
|
||||
goto backtrace_done;
|
||||
|
||||
case MIPS_SPECIAL_FUNCT_break:
|
||||
case MIPS_SPECIAL_FUNCT_jr:
|
||||
case MIPS_SPECIAL_FUNCT_sync:
|
||||
case MIPS_SPECIAL_FUNCT_syscall:
|
||||
case MIPS_SPECIAL_FUNCT_tge:
|
||||
case MIPS_SPECIAL_FUNCT_tgeu:
|
||||
case MIPS_SPECIAL_FUNCT_tlt:
|
||||
case MIPS_SPECIAL_FUNCT_tltu:
|
||||
case MIPS_SPECIAL_FUNCT_teq:
|
||||
case MIPS_SPECIAL_FUNCT_tne:
|
||||
/* These instructions can validly have rd == MIPS_REG_SP */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
found_prologue:
|
||||
/* Check sanity of saved pc. */
|
||||
if (saved_pc & 3)
|
||||
goto backtrace_done;
|
||||
if (saved_pc == 0)
|
||||
goto backtrace_done;
|
||||
|
||||
if (i >= n_frames_to_skip)
|
||||
callers[i - n_frames_to_skip] = saved_pc;
|
||||
pc = uword_to_pointer (saved_pc, u32 *);
|
||||
}
|
||||
|
||||
backtrace_done:
|
||||
if (i < n_frames_to_skip)
|
||||
return 0;
|
||||
else
|
||||
return i - n_frames_to_skip;
|
||||
}
|
||||
#endif /* __mips__ */
|
||||
|
||||
#ifndef clib_backtrace_defined
|
||||
#define clib_backtrace_defined
|
||||
|
||||
#ifndef USE_LIBUNWIND
|
||||
/* use glibc backtrace for stack trace */
|
||||
#include <execinfo.h>
|
||||
#else
|
||||
#include <libunwind.h>
|
||||
static int
|
||||
backtrace (void **buffer, int size)
|
||||
{
|
||||
return unw_backtrace (buffer, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
__clib_export uword
|
||||
clib_backtrace (uword * callers, uword max_callers, uword n_frames_to_skip)
|
||||
{
|
||||
int size;
|
||||
void *array[20];
|
||||
/* Also skip current frame. */
|
||||
n_frames_to_skip += 1;
|
||||
|
||||
size = clib_min (ARRAY_LEN (array), max_callers + n_frames_to_skip);
|
||||
|
||||
size = backtrace (array, size);
|
||||
|
||||
uword i;
|
||||
|
||||
for (i = 0; i < max_callers + n_frames_to_skip && i < size; i++)
|
||||
{
|
||||
if (i >= n_frames_to_skip)
|
||||
callers[i - n_frames_to_skip] = pointer_to_uword (array[i]);
|
||||
}
|
||||
|
||||
if (i < n_frames_to_skip)
|
||||
return 0;
|
||||
else
|
||||
return i - n_frames_to_skip;
|
||||
}
|
||||
|
||||
|
||||
#endif /* clib_backtrace_defined */
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
* Local Variables:
|
||||
* eval: (c-set-style "gnu")
|
||||
* End:
|
||||
*/
|
@ -385,10 +385,6 @@ void qsort (void *base, uword n, uword size,
|
||||
int (*)(const void *, const void *));
|
||||
#endif
|
||||
|
||||
/* Stack backtrace. */
|
||||
uword
|
||||
clib_backtrace (uword * callers, uword max_callers, uword n_frames_to_skip);
|
||||
|
||||
#include <vppinfra/byte_order.h>
|
||||
#endif /* included_clib_h */
|
||||
|
||||
|
48
src/vppinfra/format_ansi.h
Normal file
48
src/vppinfra/format_ansi.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* SPDX-License-Identifier: Apache-2.0
|
||||
* Copyright (c) 2024 Cisco Systems, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __FORMAT_ANSI_H__
|
||||
#define __FORMAT_ANSI_H__
|
||||
|
||||
#define ANSI_RESET "\x1b[0m"
|
||||
#define ANSI_BOLD "\x1b[1m"
|
||||
#define ANSI_ITALIC "\x1b[3m"
|
||||
#define ANSI_UNDERLINE "\x1b[4m"
|
||||
#define ANSI_BLINK "\x1b[5m"
|
||||
#define ANSI_FG_BLACK "\x1b[30m"
|
||||
#define ANSI_FG_RED "\x1b[31m"
|
||||
#define ANSI_FG_GREEN "\x1b[32m"
|
||||
#define ANSI_FG_YELLOW "\x1b[33m"
|
||||
#define ANSI_FG_BLUE "\x1b[34m"
|
||||
#define ANSI_FG_MAGENTA "\x1b[35m"
|
||||
#define ANSI_FG_CYAN "\x1b[36m"
|
||||
#define ANSI_FG_WHITE "\x1b[37m"
|
||||
#define ANSI_FG_DEFAULT "\x1b[39m"
|
||||
#define ANSI_BG_BLACK "\x1b[40m"
|
||||
#define ANSI_BG_RED "\x1b[41m"
|
||||
#define ANSI_BG_GREEN "\x1b[42m"
|
||||
#define ANSI_BG_YELLOW "\x1b[43m"
|
||||
#define ANSI_BG_BLUE "\x1b[44m"
|
||||
#define ANSI_BG_MAGENTA "\x1b[45m"
|
||||
#define ANSI_BG_CYAN "\x1b[46m"
|
||||
#define ANSI_BG_WHITE "\x1b[47m"
|
||||
#define ANSI_BG_DEFAULT "\x1b[49m"
|
||||
#define ANSI_FG_BR_BLACK "\x1b[90m"
|
||||
#define ANSI_FG_BR_RED "\x1b[91m"
|
||||
#define ANSI_FG_BR_GREEN "\x1b[92m"
|
||||
#define ANSI_FG_BR_YELLOW "\x1b[93m"
|
||||
#define ANSI_FG_BR_BLUE "\x1b[94m"
|
||||
#define ANSI_FG_BR_MAGENTA "\x1b[95m"
|
||||
#define ANSI_FG_BR_CYAN "\x1b[96m"
|
||||
#define ANSI_FG_BR_WHITE "\x1b[97m"
|
||||
#define ANSI_BG_BR_BLACK "\x1b[100m"
|
||||
#define ANSI_BG_BR_RED "\x1b[101m"
|
||||
#define ANSI_BG_BR_GREEN "\x1b[102m"
|
||||
#define ANSI_BG_BR_YELLOW "\x1b[103m"
|
||||
#define ANSI_BG_BR_BLUE "\x1b[104m"
|
||||
#define ANSI_BG_BR_MAGENTA "\x1b[105m"
|
||||
#define ANSI_BG_BR_CYAN "\x1b[106m"
|
||||
#define ANSI_BG_BR_WHITE "\x1b[107m"
|
||||
|
||||
#endif /* __FORMAT_ANSI_H__ */
|
@ -19,6 +19,7 @@
|
||||
#include <vppinfra/lock.h>
|
||||
#include <vppinfra/hash.h>
|
||||
#include <vppinfra/elf_clib.h>
|
||||
#include <vppinfra/stack.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -65,15 +66,13 @@ mheap_get_trace_internal (const clib_mem_heap_t *heap, uword offset,
|
||||
{
|
||||
mheap_trace_main_t *tm = &mheap_trace_main;
|
||||
mheap_trace_t *t;
|
||||
uword i, n_callers, trace_index, *p;
|
||||
mheap_trace_t trace;
|
||||
uword i, trace_index, *p;
|
||||
mheap_trace_t trace = {};
|
||||
int index;
|
||||
|
||||
if (heap != tm->current_traced_mheap || mheap_trace_thread_disable)
|
||||
return;
|
||||
|
||||
/* Spurious Coverity warnings be gone. */
|
||||
clib_memset (&trace, 0, sizeof (trace));
|
||||
|
||||
clib_spinlock_lock (&tm->lock);
|
||||
|
||||
/* heap could have changed while we were waiting on the lock */
|
||||
@ -83,9 +82,19 @@ mheap_get_trace_internal (const clib_mem_heap_t *heap, uword offset,
|
||||
/* Turn off tracing for this thread to avoid embarrassment... */
|
||||
mheap_trace_thread_disable = 1;
|
||||
|
||||
/* Skip our frame and mspace_get_aligned's frame */
|
||||
n_callers = clib_backtrace (trace.callers, ARRAY_LEN (trace.callers), 2);
|
||||
if (n_callers == 0)
|
||||
index = -2; /* skip first 2 stack frames */
|
||||
foreach_clib_stack_frame (sf)
|
||||
{
|
||||
if (index >= 0)
|
||||
{
|
||||
if (index == ARRAY_LEN (trace.callers))
|
||||
break;
|
||||
trace.callers[index] = sf->ip;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
if (index < 1)
|
||||
goto out;
|
||||
|
||||
if (!tm->trace_by_callers)
|
||||
|
75
src/vppinfra/stack.c
Normal file
75
src/vppinfra/stack.c
Normal file
@ -0,0 +1,75 @@
|
||||
/* SPDX-License-Identifier: Apache-2.0
|
||||
* Copyright (c) 2024 Cisco Systems, Inc.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <vppinfra/clib.h>
|
||||
#include <vppinfra/stack.h>
|
||||
#include <vppinfra/error.h>
|
||||
|
||||
#if HAVE_LIBUNWIND == 1
|
||||
|
||||
#define UNW_LOCAL_ONLY
|
||||
#include <libunwind.h>
|
||||
|
||||
static __thread unw_cursor_t cursor;
|
||||
static __thread unw_context_t context;
|
||||
|
||||
#endif
|
||||
|
||||
__clib_export clib_stack_frame_t *
|
||||
clib_stack_frame_get (clib_stack_frame_t *sf)
|
||||
{
|
||||
#if HAVE_LIBUNWIND == 1
|
||||
Dl_info info = {};
|
||||
|
||||
if (sf->index == 0)
|
||||
{
|
||||
if (unw_getcontext (&context) < 0)
|
||||
{
|
||||
clib_warning ("libunwind: cannot get local machine state\n");
|
||||
return 0;
|
||||
}
|
||||
if (unw_init_local (&cursor, &context) < 0)
|
||||
{
|
||||
clib_warning (
|
||||
"libunwind: cannot initialize cursor for local unwinding\n");
|
||||
return 0;
|
||||
}
|
||||
if (unw_step (&cursor) < 1)
|
||||
return 0;
|
||||
}
|
||||
else if (unw_step (&cursor) < 1)
|
||||
return 0;
|
||||
|
||||
if (unw_get_reg (&cursor, UNW_REG_IP, &sf->ip))
|
||||
{
|
||||
clib_warning ("libunwind: cannot read IP\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (unw_get_reg (&cursor, UNW_REG_SP, &sf->sp))
|
||||
{
|
||||
clib_warning ("libunwind: cannot read SP\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (unw_get_proc_name (&cursor, sf->name, sizeof (sf->name), &sf->offset) <
|
||||
0)
|
||||
sf->name[0] = sf->offset = 0;
|
||||
|
||||
sf->is_signal_frame = unw_is_signal_frame (&cursor) ? 1 : 0;
|
||||
|
||||
if (dladdr ((void *) sf->ip, &info))
|
||||
sf->file_name = info.dli_fname;
|
||||
else
|
||||
sf->file_name = 0;
|
||||
|
||||
sf->index++;
|
||||
return sf;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
26
src/vppinfra/stack.h
Normal file
26
src/vppinfra/stack.h
Normal file
@ -0,0 +1,26 @@
|
||||
/* SPDX-License-Identifier: Apache-2.0
|
||||
* Copyright (c) 2024 Cisco Systems, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __STACK_H__
|
||||
#define __STACK_H__
|
||||
|
||||
#include <vppinfra/clib.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uword ip, sp;
|
||||
uword offset;
|
||||
char name[64];
|
||||
const char *file_name;
|
||||
u32 index;
|
||||
u8 is_signal_frame;
|
||||
} clib_stack_frame_t;
|
||||
|
||||
clib_stack_frame_t *clib_stack_frame_get (clib_stack_frame_t *);
|
||||
|
||||
#define foreach_clib_stack_frame(sf) \
|
||||
for (clib_stack_frame_t _sf = {}, *sf = clib_stack_frame_get (&_sf); sf; \
|
||||
sf = clib_stack_frame_get (sf))
|
||||
|
||||
#endif /* __STACK_H__ */
|
Reference in New Issue
Block a user