Home | History | Annotate | Download | only in libbacktrace
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <stdint.h>
     18 #include <sys/types.h>
     19 #include <ucontext.h>
     20 
     21 #include <libunwind.h>
     22 #include <libunwind-ptrace.h>
     23 
     24 #include <backtrace/Backtrace.h>
     25 #include <backtrace/BacktraceMap.h>
     26 
     27 #include "BacktraceLog.h"
     28 #include "UnwindMap.h"
     29 #include "UnwindPtrace.h"
     30 
     31 UnwindPtrace::UnwindPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
     32     : BacktracePtrace(pid, tid, map), addr_space_(nullptr), upt_info_(nullptr) {
     33 }
     34 
     35 UnwindPtrace::~UnwindPtrace() {
     36   if (upt_info_) {
     37     _UPT_destroy(upt_info_);
     38     upt_info_ = nullptr;
     39   }
     40 
     41   if (addr_space_) {
     42     // Remove the map from the address space before destroying it.
     43     // It will be freed in the UnwindMap destructor.
     44     unw_map_set(addr_space_, nullptr);
     45 
     46     unw_destroy_addr_space(addr_space_);
     47     addr_space_ = nullptr;
     48   }
     49 }
     50 
     51 bool UnwindPtrace::Init() {
     52   if (upt_info_) {
     53     return true;
     54   }
     55 
     56   if (addr_space_) {
     57     // If somehow the addr_space_ gets initialized but upt_info_ doesn't,
     58     // then that indicates there is some kind of failure.
     59     return false;
     60   }
     61 
     62   addr_space_ = unw_create_addr_space(&_UPT_accessors, 0);
     63   if (!addr_space_) {
     64     BACK_LOGW("unw_create_addr_space failed.");
     65     error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     66     return false;
     67   }
     68 
     69   UnwindMap* map = static_cast<UnwindMap*>(GetMap());
     70   unw_map_set(addr_space_, map->GetMapCursor());
     71 
     72   upt_info_ = reinterpret_cast<struct UPT_info*>(_UPT_create(Tid()));
     73   if (!upt_info_) {
     74     BACK_LOGW("Failed to create upt info.");
     75     error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     76     return false;
     77   }
     78 
     79   return true;
     80 }
     81 
     82 bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
     83   if (GetMap() == nullptr) {
     84     // Without a map object, we can't do anything.
     85     error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
     86     return false;
     87   }
     88 
     89   error_ = BACKTRACE_UNWIND_NO_ERROR;
     90 
     91   if (ucontext) {
     92     BACK_LOGW("Unwinding from a specified context not supported yet.");
     93     error_ = BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION;
     94     return false;
     95   }
     96 
     97   if (!Init()) {
     98     return false;
     99   }
    100 
    101   unw_cursor_t cursor;
    102   int ret = unw_init_remote(&cursor, addr_space_, upt_info_);
    103   if (ret < 0) {
    104     BACK_LOGW("unw_init_remote failed %d", ret);
    105     error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
    106     return false;
    107   }
    108 
    109   size_t num_frames = 0;
    110   do {
    111     unw_word_t pc;
    112     ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
    113     if (ret < 0) {
    114       BACK_LOGW("Failed to read IP %d", ret);
    115       break;
    116     }
    117     unw_word_t sp;
    118     ret = unw_get_reg(&cursor, UNW_REG_SP, &sp);
    119     if (ret < 0) {
    120       BACK_LOGW("Failed to read SP %d", ret);
    121       break;
    122     }
    123 
    124     if (num_ignore_frames == 0) {
    125       frames_.resize(num_frames+1);
    126       backtrace_frame_data_t* frame = &frames_.at(num_frames);
    127       frame->num = num_frames;
    128       frame->pc = static_cast<uintptr_t>(pc);
    129       frame->sp = static_cast<uintptr_t>(sp);
    130       frame->stack_size = 0;
    131 
    132       if (num_frames > 0) {
    133         backtrace_frame_data_t* prev = &frames_.at(num_frames-1);
    134         prev->stack_size = frame->sp - prev->sp;
    135       }
    136 
    137       FillInMap(frame->pc, &frame->map);
    138 
    139       frame->func_name = GetFunctionName(frame->pc, &frame->func_offset, &frame->map);
    140 
    141       num_frames++;
    142       // If the pc is in a device map, then don't try to step.
    143       if (frame->map.flags & PROT_DEVICE_MAP) {
    144         break;
    145       }
    146     } else {
    147       // If the pc is in a device map, then don't try to step.
    148       backtrace_map_t map;
    149       FillInMap(pc, &map);
    150       if (map.flags & PROT_DEVICE_MAP) {
    151         break;
    152       }
    153       num_ignore_frames--;
    154     }
    155     // Verify the sp is not in a device map.
    156     backtrace_map_t map;
    157     FillInMap(sp, &map);
    158     if (map.flags & PROT_DEVICE_MAP) {
    159       break;
    160     }
    161     ret = unw_step (&cursor);
    162   } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
    163 
    164   return true;
    165 }
    166 
    167 std::string UnwindPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
    168   if (!Init()) {
    169     return "";
    170   }
    171 
    172   *offset = 0;
    173   char buf[512];
    174   unw_word_t value;
    175   if (unw_get_proc_name_by_ip(addr_space_, pc, buf, sizeof(buf), &value,
    176                               upt_info_) >= 0 && buf[0] != '\0') {
    177     *offset = static_cast<uintptr_t>(value);
    178     return buf;
    179   }
    180   return "";
    181 }
    182