mirror of
				https://github.com/ossrs/srs.git
				synced 2025-03-09 15:49:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			367 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			367 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| Michael Abd-El-Malek contributed this patch.  He wrote:
 | |
| ----------------------------------------
 | |
| Hello,
 | |
| 
 | |
| This is a patch that enables programmatically dumping the stack of  
 | |
| every thread.  This has been useful in debugging deadlocks, etc...   
 | |
| Our usage model is that the SIGUSR2 handler calls the new  
 | |
| _st_print_thread_stacks function, which dumps the stack for all  
 | |
| threads.  A convenient feature is that for thread stacks that are the  
 | |
| same (which is common for application with a lot of worker threads  
 | |
| waiting for work), only one stack trace is printed, along with a  
 | |
| count of how many threads have that same stack.
 | |
| 
 | |
| I use the glibc backtrace function to get the backtrace, and then use  
 | |
| popen to execute addr2line and convert memory addresses to file  
 | |
| names, function names, and line numbers.  If glibc isn't available,  
 | |
| _st_print_thread_stacks just prints a warning.  And this feature is  
 | |
| only available if DEBUG is turned on.
 | |
| 
 | |
| We've found this feature extremely helpful when debugging.
 | |
| 
 | |
| The patch can be a bit more robust (it assumes addr2line exists).   
 | |
| But I didn't want to go through the hassle of doing this, if the  
 | |
| StateThreads community doesn't want to use this patch.  (In our  
 | |
| environment, addr2line will always be there.)
 | |
| 
 | |
| Cheers,
 | |
| Mike
 | |
| ----------------------------------------
 | |
| Invoking complex functions from a signal handler is not recommended,
 | |
| plus this patch changes the behavior of existing API hooks.  It will
 | |
| not become part of State Threads proper but you may find it useful
 | |
| nonetheless.  This patch applies to st-1.5.2.
 | |
| 
 | |
| diff -Nur Makefile.1.5.2 Makefile
 | |
| --- Makefile.1.5.2	Wed Sep  7 14:19:50 2005
 | |
| +++ Makefile	Wed Sep  7 14:33:08 2005
 | |
| @@ -255,7 +255,8 @@
 | |
|                $(TARGETDIR)/stk.o   \
 | |
|                $(TARGETDIR)/sync.o  \
 | |
|                $(TARGETDIR)/key.o   \
 | |
| -              $(TARGETDIR)/io.o
 | |
| +              $(TARGETDIR)/io.o    \
 | |
| +	      $(TARGETDIR)/backtrace.o
 | |
|  OBJS        += $(EXTRA_OBJS)
 | |
|  HEADER      = $(TARGETDIR)/st.h
 | |
|  SLIBRARY    = $(TARGETDIR)/libst.a
 | |
| diff -Nur backtrace.c.1.5.2 backtrace.c
 | |
| --- backtrace.c.1.5.2	Wed Dec 31 16:00:00 1969
 | |
| +++ backtrace.c	Wed Sep  7 13:40:21 2005
 | |
| @@ -0,0 +1,211 @@
 | |
| +/*
 | |
| + * The contents of this file are subject to the Mozilla Public
 | |
| + * License Version 1.1 (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.mozilla.org/MPL/
 | |
| + *
 | |
| + * Software distributed under the License is distributed on an "AS
 | |
| + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 | |
| + * implied. See the License for the specific language governing
 | |
| + * rights and limitations under the License.
 | |
| + *
 | |
| + * Contributor(s):  Michael Abd-El-Malek (mabdelmalek@cmu.edu)
 | |
| + *                  Carnegie Mellon University
 | |
| + *
 | |
| + * Alternatively, the contents of this file may be used under the
 | |
| + * terms of the GNU General Public License Version 2 or later (the
 | |
| + * "GPL"), in which case the provisions of the GPL are applicable
 | |
| + * instead of those above.  If you wish to allow use of your
 | |
| + * version of this file only under the terms of the GPL and not to
 | |
| + * allow others to use your version of this file under the MPL,
 | |
| + * indicate your decision by deleting the provisions above and
 | |
| + * replace them with the notice and other provisions required by
 | |
| + * the GPL.  If you do not delete the provisions above, a recipient
 | |
| + * may use your version of this file under either the MPL or the
 | |
| + * GPL.
 | |
| + */
 | |
| +
 | |
| +
 | |
| +
 | |
| +/*
 | |
| + * This file contains routines for printing a stack trace of all threads.
 | |
| + * Only works when DEBUG is defined and where glibc is available, since it
 | |
| + * provides the backtrace() function.
 | |
| + */
 | |
| +
 | |
| +#define _GNU_SOURCE  /* to get program_invocation_name */
 | |
| +
 | |
| +#include <stdio.h>
 | |
| +#include <stdlib.h>
 | |
| +
 | |
| +
 | |
| +#if defined(DEBUG) && defined(__GLIBC__)
 | |
| +
 | |
| +#include <errno.h>
 | |
| +#include "common.h"
 | |
| +#include <execinfo.h>
 | |
| +#include <inttypes.h>
 | |
| +#include <string.h>
 | |
| +
 | |
| +
 | |
| +/* The maximum number of frames to get a stack trace for.  If a thread has more
 | |
| + * frames than this, then we only show the latest X frames. */
 | |
| +#define MAX_NUM_FRAMES 64
 | |
| +
 | |
| +
 | |
| +typedef struct thread_stack_s {
 | |
| +   uint32_t        num_frames;
 | |
| +   void*           addresses[MAX_NUM_FRAMES]; /* frame pointers */
 | |
| +   char*           locations[MAX_NUM_FRAMES]; /* file/function/line numbers  */
 | |
| +   uint32_t        num_matches;
 | |
| +
 | |
| +   struct thread_stack_s* next;
 | |
| +} thread_stack_t;
 | |
| +
 | |
| +static thread_stack_t* stacks = NULL;
 | |
| +
 | |
| +
 | |
| +/* Converts the function's memory addresses to function names, file names, and
 | |
| + * line numbers.  Calls binutil's addr2line program. */
 | |
| +static void get_symbol_names(thread_stack_t *stack)
 | |
| +{
 | |
| +     char program_to_run[1024], function[256], filename_lineno[256], temp[19];
 | |
| +     FILE* output;
 | |
| +     int num_bytes_left;
 | |
| +     uint32_t i;
 | |
| +
 | |
| +     /* Construct the arguments to addr2line */
 | |
| +     num_bytes_left = sizeof(program_to_run);
 | |
| +     num_bytes_left -= snprintf(program_to_run, sizeof(program_to_run),
 | |
| +                                "addr2line -fCe %s", program_invocation_name);
 | |
| +     for (i = 0; i < stack->num_frames && num_bytes_left > 0; ++i) {
 | |
| +         num_bytes_left -= snprintf(temp, sizeof(temp), " %p", stack->addresses[i]);
 | |
| +         strncat(program_to_run, temp, num_bytes_left);
 | |
| +     }
 | |
| +
 | |
| +     /* Use popen to execute addr2line and read its ouput */
 | |
| +     output = popen(program_to_run, "r");
 | |
| +     for (i = 0; i < stack->num_frames; ++i) {
 | |
| +         char* function_listing = (char*) malloc(512);
 | |
| +         fscanf(output, "%255s\n", function);
 | |
| +         fscanf(output, "%255s\n", filename_lineno);
 | |
| +         snprintf(function_listing, 512, "%s at %s", function, filename_lineno);
 | |
| +         stack->locations[i] = function_listing;
 | |
| +     }
 | |
| +     pclose(output);
 | |
| +}
 | |
| +
 | |
| +
 | |
| +static void print_stack(thread_stack_t* stack)
 | |
| +{
 | |
| +     int skip_offset = 0, cmp_len;
 | |
| +     uint32_t i;
 | |
| +
 | |
| +     /* Get the function names/filenames/line numbers */
 | |
| +     get_symbol_names(stack);
 | |
| +
 | |
| +     cmp_len = strlen("_st_iterate_threads_helper");
 | |
| +
 | |
| +     /* Print the backtrace */
 | |
| +     for (i = 0; i < stack->num_frames; ++i) {
 | |
| +         /* Skip frames we don't have location info for */
 | |
| +         if (!strncmp(stack->locations[i], "??", 2)) {
 | |
| +             continue;
 | |
| +         }
 | |
| +
 | |
| +         /* Skip the frames that are used for printing the stack trace */
 | |
| +         if (skip_offset) {
 | |
| +             printf("\t#%2d %s %p\n", i - skip_offset, stack->locations[i],
 | |
| +                    stack->addresses[i]);
 | |
| +         } else if (!strncmp(stack->locations[i], "_st_iterate_threads_helper",
 | |
| +                             cmp_len)) {
 | |
| +             skip_offset = i + 1;
 | |
| +         }
 | |
| +     }
 | |
| +}
 | |
| +
 | |
| +
 | |
| +static void add_current_thread_stack(void)
 | |
| +{
 | |
| +     thread_stack_t *new_stack = malloc(sizeof(thread_stack_t));
 | |
| +     thread_stack_t *search;
 | |
| +
 | |
| +     /* Call glibc function to get the backtrace */
 | |
| +     new_stack->num_frames = backtrace(new_stack->addresses, MAX_NUM_FRAMES);
 | |
| +
 | |
| +     /* Check if we have another stacks that is equivalent.  If so, then coaelsce
 | |
| +      *  two stacks into one, to minimize output to user. */
 | |
| +     search = stacks;
 | |
| +     while (search) {
 | |
| +         if (search->num_frames == new_stack->num_frames &&
 | |
| +             !memcmp(search->addresses, new_stack->addresses,
 | |
| +                     search->num_frames * sizeof(void*))) {
 | |
| +             /* Found an existing stack that is the same as this thread's stack */
 | |
| +             ++search->num_matches;
 | |
| +             free(new_stack);
 | |
| +             return;
 | |
| +         } else {
 | |
| +             search = search->next;
 | |
| +         }
 | |
| +     }
 | |
| +
 | |
| +     /* This is a new stack.  Add it to the list of stacks. */
 | |
| +     new_stack->num_matches = 1;
 | |
| +     new_stack->next = stacks;
 | |
| +     stacks = new_stack;
 | |
| +}
 | |
| +
 | |
| +static void print_stack_frames(void)
 | |
| +{
 | |
| +     while (stacks) {
 | |
| +         printf("\n%u thread(s) with this backtrace:\n", stacks->num_matches);
 | |
| +         print_stack(stacks);
 | |
| +         stacks = stacks->next;
 | |
| +     }
 | |
| +     printf("\n");
 | |
| +}
 | |
| +
 | |
| +static void free_stacks(void)
 | |
| +{
 | |
| +     uint32_t i;
 | |
| +     while (stacks) {
 | |
| +         thread_stack_t *next = stacks->next;
 | |
| +         for (i = 0; i < stacks->num_frames; ++i) {
 | |
| +             free(stacks->locations[i]);
 | |
| +         }
 | |
| +         free(stacks);
 | |
| +         stacks = next;
 | |
| +     }
 | |
| +     stacks = NULL;
 | |
| +}
 | |
| +
 | |
| +
 | |
| +static void st_print_thread_stack(_st_thread_t *thread, int start_flag,
 | |
| +                                   int end_flag)
 | |
| +{
 | |
| +     if (end_flag == 0) {
 | |
| +         add_current_thread_stack();
 | |
| +     } else {
 | |
| +         print_stack_frames();
 | |
| +     }
 | |
| +}
 | |
| +
 | |
| +
 | |
| +void _st_print_thread_stacks(int ignore)
 | |
| +{
 | |
| +     _st_iterate_threads_flag = 1;
 | |
| +     _st_iterate_threads_helper(st_print_thread_stack);
 | |
| +     _st_iterate_threads_flag = 0;
 | |
| +
 | |
| +     /* Deallocate memory */
 | |
| +     free_stacks();
 | |
| +}
 | |
| +
 | |
| +#else  /* defined(DEBUG) && defined(__GLIBC__) */
 | |
| +
 | |
| +void _st_print_thread_stacks(int ignore)
 | |
| +{
 | |
| +     printf("%s: need DEBUG mode and glibc-specific functions to read stack.\n",
 | |
| +            __FUNCTION__);
 | |
| +}
 | |
| +#endif /* defined(DEBUG) && defined(__GLIBC__) */
 | |
| diff -Nur common.h.1.5.2 common.h
 | |
| --- common.h.1.5.2	Wed Sep  7 14:18:37 2005
 | |
| +++ common.h	Wed Sep  7 14:35:36 2005
 | |
| @@ -371,8 +371,18 @@
 | |
|   */
 | |
|  
 | |
|  #ifdef DEBUG
 | |
| -void _st_iterate_threads(void);
 | |
| -#define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads()
 | |
| +typedef void(*_st_func_ptr_t)(_st_thread_t *thread,
 | |
| +                              int           start_flag,
 | |
| +                              int           end_flag);
 | |
| +/* Pointer to function that will be called on thread switch */
 | |
| +extern _st_func_ptr_t _st_iterate_func_ptr;
 | |
| +extern int _st_iterate_threads_flag;
 | |
| +/* Thread iteration function that will call an arbitrary function */
 | |
| +extern void _st_iterate_threads_helper(_st_func_ptr_t func);
 | |
| +#define ST_DEBUG_ITERATE_THREADS()                               \
 | |
| +          if (_st_iterate_func_ptr) {                            \
 | |
| +              _st_iterate_threads_helper(_st_iterate_func_ptr);  \
 | |
| +          }
 | |
|  #else
 | |
|  #define ST_DEBUG_ITERATE_THREADS()
 | |
|  #endif
 | |
| diff -Nur public.h.1.5.2 public.h
 | |
| --- public.h.1.5.2	Wed Sep  7 11:46:58 2005
 | |
| +++ public.h	Wed Sep  7 13:38:46 2005
 | |
| @@ -171,8 +171,10 @@
 | |
|  extern st_netfd_t st_open(const char *path, int oflags, mode_t mode);
 | |
|  
 | |
|  #ifdef DEBUG
 | |
| -extern void _st_show_thread_stack(st_thread_t thread, const char *messg);
 | |
| +extern void _st_show_thread_stack(st_thread_t thread, int start_flag,
 | |
| +				  int end_flag);
 | |
|  extern void _st_iterate_threads(void);
 | |
| +extern void _st_print_thread_stacks(int ignore);
 | |
|  #endif
 | |
|  
 | |
|  #ifdef __cplusplus
 | |
| diff -Nur sched.c.1.5.2 sched.c
 | |
| --- sched.c.1.5.2	Wed Sep  7 10:48:05 2005
 | |
| +++ sched.c	Wed Sep  7 13:38:46 2005
 | |
| @@ -919,16 +919,13 @@
 | |
|  
 | |
|  
 | |
|  #ifdef DEBUG
 | |
| -/* ARGSUSED */
 | |
| -void _st_show_thread_stack(_st_thread_t *thread, const char *messg)
 | |
| -{
 | |
| -
 | |
| -}
 | |
| -
 | |
|  /* To be set from debugger */
 | |
|  int _st_iterate_threads_flag = 0;
 | |
| +/* Thread iteration function that will call an arbitrary function */
 | |
| +_st_func_ptr_t _st_iterate_func_ptr = NULL;
 | |
|  
 | |
| -void _st_iterate_threads(void)
 | |
| +/* This function iterates over all threads, calling "func" for each thread. */
 | |
| +void _st_iterate_threads_helper(_st_func_ptr_t func)
 | |
|  {
 | |
|    static _st_thread_t *thread = NULL;
 | |
|    static jmp_buf orig_jb, save_jb;
 | |
| @@ -944,16 +941,20 @@
 | |
|  
 | |
|    if (thread) {
 | |
|      memcpy(thread->context, save_jb, sizeof(jmp_buf));
 | |
| -    _st_show_thread_stack(thread, NULL);
 | |
| +    func(thread, 0, 0);
 | |
|    } else {
 | |
|      if (MD_SETJMP(orig_jb)) {
 | |
|        _st_iterate_threads_flag = 0;
 | |
| +      _st_iterate_func_ptr = NULL;
 | |
|        thread = NULL;
 | |
| -      _st_show_thread_stack(thread, "Iteration completed");
 | |
| +      /* Last thread to iterate through  */
 | |
| +      func(thread, 0, 1);
 | |
|        return;
 | |
|      }
 | |
| +    /* First thread to iterate through  */
 | |
|      thread = _ST_CURRENT_THREAD();
 | |
| -    _st_show_thread_stack(thread, "Iteration started");
 | |
| +    _st_iterate_func_ptr = func;
 | |
| +    func(thread, 1, 0);
 | |
|    }
 | |
|  
 | |
|    q = thread->tlink.next;
 | |
| @@ -966,5 +967,17 @@
 | |
|    memcpy(save_jb, thread->context, sizeof(jmp_buf));
 | |
|    MD_LONGJMP(thread->context, 1);
 | |
|  }
 | |
| +
 | |
| +/* ARGSUSED */
 | |
| +void _st_show_thread_stack(_st_thread_t *thread, int start_flag, int end_flag)
 | |
| +{
 | |
| +}
 | |
| +
 | |
| +/* Iterate over threads inside debugger; see st/README */
 | |
| +void _st_iterate_threads(void)
 | |
| +{
 | |
| +  _st_iterate_threads_helper(_st_show_thread_stack);
 | |
| +}
 | |
| +
 | |
|  #endif /* DEBUG */
 | |
|  
 |