text: /*!The Treasure Box Library
   *
   * 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) 2009-present, TBOOX Open Source Group.
   *
   * @author      ruki
   * @file        backtrace.c
   * @ingroup     platform
   *
   */
  
  /* //////////////////////////////////////////////////////////////////////////////////////
   * includes
   */
  #include "prefix.h"
  #include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include "../arch/frame.h"
  #include "../../libc/libc.h"
  #if defined(TB_CONFIG_LIBC_HAVE_BACKTRACE)
  #   include <execinfo.h>
  #elif 0/*defined(TB_FIRST_FRAME_POINTER) \
      &&  defined(TB_CURRENT_STACK_FRAME) \
      &&  defined(TB_ADVANCE_STACK_FRAME) \
      &&  defined(TB_STACK_INNER_THAN)
  #   include <dlfcn.h>*/
  #endif
  
  /* //////////////////////////////////////////////////////////////////////////////////////
   * backtrace implementation
   */
  #if defined(TB_CONFIG_LIBC_HAVE_BACKTRACE)
  tb_size_t tb_backtrace_frames(tb_pointer_t* frames, tb_size_t nframe, tb_size_t nskip)
  {
      // note: cannot use assert
      tb_check_return_val(frames && nframe, 0);
  
      // skip some frames?
      if (nskip)
      {
          // init temp frames
          tb_pointer_t    temp[256] = {0};
          tb_check_return_val(nframe + nskip < 256, 0);
  
          // done backtrace
          tb_size_t       size = backtrace(temp, nframe + nskip);
          tb_check_return_val(nskip < size, 0);
  
          // update nframe
          nframe = tb_min(nframe, size - nskip);
  
          // save to frames
          tb_memcpy_(frames, temp + nskip, nframe * sizeof(tb_pointer_t));
      }
      // backtrace
      else nframe = backtrace(frames, nframe);
  
      // ok?
      return nframe;
  }
  // FIXME
  #elif 0/*defined(TB_FIRST_FRAME_POINTER) \
      &&  defined(TB_CURRENT_STACK_FRAME) \
      &&  defined(TB_ADVANCE_STACK_FRAME) \
      &&  defined(TB_STACK_INNER_THAN) */
  //extern tb_pointer_t __libc_stack_end;
  tb_size_t tb_backtrace_frames(tb_pointer_t* frames, tb_size_t nframe, tb_size_t nskip)
  {
      // the libc stack end pointer
      tb_pointer_t* __plibc_stack_end = dlsym(tb_null, "__libc_stack_end");
      tb_pointer_t __libc_stack_end = __plibc_stack_end? *__plibc_stack_end : tb_null;
  
      // trace
  //  tb_trace_d("__libc_stack_end: %p", __libc_stack_end);
  
      // the top frame and stack address
      tb_pointer_t top_frame = TB_FIRST_FRAME_POINTER;
      tb_pointer_t top_stack = TB_CURRENT_STACK_FRAME;
  
      // trace
  //  tb_trace_d("top_frame: %p", top_frame);
  //  tb_trace_d("top_stack: %p", top_stack);
  
      // the current frame
      tb_frame_layout_t* current = ((tb_frame_layout_t*)top_frame);
  
      // the top frame not contain this func, nskip--
      if (nskip) nskip--;
  
      // walk frames
      tb_size_t n = 0;
      tb_size_t m = __libc_stack_end? 100 : 16;
      while (n < nframe && m--)
      {
          // trace
  //      tb_trace_d("current: %p", current);
  
          // out of range?
          if (    (tb_pointer_t)current TB_STACK_INNER_THAN top_stack
              ||  (__libc_stack_end && !((tb_pointer_t)current TB_STACK_INNER_THAN __libc_stack_end)))
          {
              break;
          }
  
  #if 1
          // save the return address
          if (!nskip) frames[n++] = current->return_address;
          // skip this frame
          else nskip--;
  #else
          frames[n++] = current->return_address;
  #endif
  
          // the next frame address
          current = TB_ADVANCE_STACK_FRAME(current->next);
      }
  
      // ok?
      return n;
  }
  #else
  tb_size_t tb_backtrace_frames(tb_pointer_t* frames, tb_size_t nframe, tb_size_t nskip)
  {
      tb_trace_noimpl();
      return 0;
  }
  #endif
  
  /* //////////////////////////////////////////////////////////////////////////////////////
   * symbols implementation
   */
  #if defined(TB_CONFIG_LIBC_HAVE_BACKTRACE)
  tb_handle_t tb_backtrace_symbols_init(tb_pointer_t* frames, tb_size_t nframe)
  {
      tb_check_return_val(frames && nframe, tb_null);
      return (tb_handle_t)backtrace_symbols(frames, nframe);
  }
  tb_char_t const* tb_backtrace_symbols_name(tb_handle_t symbols, tb_pointer_t* frames, tb_size_t nframe, tb_size_t iframe)
  {
      tb_check_return_val(symbols && frames && nframe && iframe < nframe, tb_null);
      return ((tb_char_t const**)symbols)[iframe];
  }
  tb_void_t tb_backtrace_symbols_exit(tb_handle_t symbols)
  {
      if (symbols) free(symbols);
  }
  #elif 0
  tb_handle_t tb_backtrace_symbols_init(tb_pointer_t* frames, tb_size_t nframe)
  {
      // check
      tb_check_return_val(frames && nframe, tb_null);
  
      // init symbols
      return malloc(8192);
  }
  tb_char_t const* tb_backtrace_symbols_name(tb_handle_t symbols, tb_pointer_t* frames, tb_size_t nframe, tb_size_t iframe)
  {
      // check
      tb_check_return_val(symbols && frames && nframe && iframe < nframe, tb_null);
  
      // the frame address
      tb_pointer_t frame = frames[iframe];
      tb_check_return_val(frame, tb_null);
  
      // the frame dlinfo
      Dl_info dlinfo = {0};
      if (!dladdr(frame, &dlinfo)) return tb_null;
  
      // format
      tb_long_t size = 0;
      tb_size_t maxn = 8192;
      if (dlinfo.dli_fname) size = tb_snprintf((tb_char_t*)symbols, maxn, "%s(", dlinfo.dli_fname);
      if (dlinfo.dli_sname && size >= 0) size += tb_snprintf((tb_char_t*)symbols + size, maxn - size, "%s", dlinfo.dli_sname);
      if (dlinfo.dli_sname && frame >= dlinfo.dli_saddr && size >= 0) size += tb_snprintf((tb_char_t*)symbols + size, maxn - size, "+%#lx", (tb_size_t)(frame - dlinfo.dli_saddr));
      if (size >= 0) size += tb_snprintf((tb_char_t*)symbols + size, maxn - size, ") [%p]", frame);
      if (size >= 0) ((tb_char_t*)symbols)[size] = '\0';
  
      // ok
      return symbols;
  }
  tb_void_t tb_backtrace_symbols_exit(tb_handle_t symbols)
  {
      // exit symbols
      if (symbols) free(symbols);
  }
  #else
  tb_handle_t tb_backtrace_symbols_init(tb_pointer_t* frames, tb_size_t nframe)
  {
      tb_trace_noimpl();
      return tb_null;
  }
  tb_char_t const* tb_backtrace_symbols_name(tb_handle_t symbols, tb_pointer_t* frames, tb_size_t nframe, tb_size_t iframe)
  {
      tb_trace_noimpl();
      return tb_null;
  }
  tb_void_t tb_backtrace_symbols_exit(tb_handle_t symbols)
  {
      tb_trace_noimpl();
  }
  #endif
  