Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9ccf627e authored by Christopher Ferris's avatar Christopher Ferris Committed by Gerrit Code Review
Browse files

Merge "Add support for the new unwind method."

parents e4bd153e 5b460d13
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -53,6 +53,8 @@ libbacktrace_sources = [
    "UnwindCurrent.cpp",
    "UnwindMap.cpp",
    "UnwindPtrace.cpp",
    "UnwindStack.cpp",
    "UnwindStackMap.cpp",
]

cc_library_headers {
@@ -84,6 +86,7 @@ cc_library {
                "libbase",
                "liblog",
                "libunwind",
                "libunwindstack",
            ],

            static_libs: ["libcutils"],
@@ -97,6 +100,7 @@ cc_library {
                "libbase",
                "liblog",
                "libunwind",
                "libunwindstack",
            ],

            static_libs: ["libcutils"],
@@ -108,6 +112,7 @@ cc_library {
                "libbase",
                "liblog",
                "libunwind",
                "libunwindstack",
            ],

            static_libs: ["libasync_safe", "libcutils"],
@@ -130,11 +135,13 @@ cc_library_shared {
        linux: {
            shared_libs: [
                "libunwind",
                "libunwindstack",
            ],
        },
        android: {
            shared_libs: [
                "libunwind",
                "libunwindstack",
            ],
        },
    }
@@ -161,6 +168,7 @@ cc_library_static {
    shared_libs = [
        "libbase",
        "libunwind",
        "libunwindstack",
        "libziparchive",
    ],
}
@@ -192,6 +200,7 @@ cc_test {
        "libcutils",
        "liblog",
        "libunwind",
        "libunwindstack",
    ],

    group_static_libs: true,
+220 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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.
 */

#define _GNU_SOURCE 1
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>

#include <memory>
#include <string>

#if !defined(__ANDROID__)
#include <cutils/threads.h>
#endif

#include <backtrace/Backtrace.h>
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/RegsGetLocal.h>

#include "BacktraceLog.h"
#include "UnwindStack.h"
#include "UnwindStackMap.h"

static std::string GetFunctionName(pid_t pid, BacktraceMap* back_map, uintptr_t pc,
                                   uintptr_t* offset) {
  *offset = 0;
  unwindstack::Maps* maps = reinterpret_cast<UnwindStackMap*>(back_map)->stack_maps();

  // Get the map for this
  unwindstack::MapInfo* map_info = maps->Find(pc);
  if (map_info == nullptr || map_info->flags & PROT_DEVICE_MAP) {
    return "";
  }

  unwindstack::Elf* elf = map_info->GetElf(pid, true);

  std::string name;
  uint64_t func_offset;
  if (!elf->GetFunctionName(elf->GetRelPc(pc, map_info), &name, &func_offset)) {
    return "";
  }
  *offset = func_offset;
  return name;
}

static bool IsUnwindLibrary(const std::string& map_name) {
  const std::string library(basename(map_name.c_str()));
  return library == "libunwindstack.so" || library == "libbacktrace.so";
}

static bool Unwind(pid_t pid, unwindstack::Memory* memory, unwindstack::Regs* regs,
                   BacktraceMap* back_map, std::vector<backtrace_frame_data_t>* frames,
                   size_t num_ignore_frames) {
  unwindstack::Maps* maps = reinterpret_cast<UnwindStackMap*>(back_map)->stack_maps();
  bool adjust_rel_pc = false;
  size_t num_frames = 0;
  frames->clear();
  while (num_frames < MAX_BACKTRACE_FRAMES) {
    if (regs->pc() == 0) {
      break;
    }
    unwindstack::MapInfo* map_info = maps->Find(regs->pc());
    if (map_info == nullptr) {
      break;
    }

    unwindstack::Elf* elf = map_info->GetElf(pid, true);
    uint64_t rel_pc = regs->pc();
    if (map_info != nullptr) {
      rel_pc = elf->GetRelPc(regs->pc(), map_info);
    }

    bool skip_frame = num_frames == 0 && IsUnwindLibrary(map_info->name);
    if (num_ignore_frames == 0 && !skip_frame) {
      uint64_t adjusted_rel_pc = rel_pc;
      if (map_info != nullptr && adjust_rel_pc) {
        adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
      }
      frames->resize(num_frames + 1);
      backtrace_frame_data_t* frame = &frames->at(num_frames);
      frame->num = num_frames;
      if (map_info != nullptr) {
        // This will point to the adjusted absolute pc. regs->pc() is
        // unaltered.
        frame->pc = map_info->start + adjusted_rel_pc;
      } else {
        frame->pc = rel_pc;
      }
      frame->sp = regs->sp();
      frame->rel_pc = adjusted_rel_pc;
      frame->stack_size = 0;

      frame->map.start = map_info->start;
      frame->map.end = map_info->end;
      frame->map.offset = map_info->offset;
      frame->map.load_bias = elf->GetLoadBias();
      frame->map.flags = map_info->flags;
      frame->map.name = map_info->name;

      uint64_t func_offset = 0;
      if (!elf->GetFunctionName(adjusted_rel_pc, &frame->func_name, &func_offset)) {
        frame->func_name = "";
      }
      frame->func_offset = func_offset;
      if (num_frames > 0) {
        // Set the stack size for the previous frame.
        backtrace_frame_data_t* prev = &frames->at(num_frames - 1);
        prev->stack_size = frame->sp - prev->sp;
      }
      num_frames++;
    } else if (!skip_frame && num_ignore_frames > 0) {
      num_ignore_frames--;
    }
    adjust_rel_pc = true;

    // Do not unwind through a device map.
    if (map_info->flags & PROT_DEVICE_MAP) {
      break;
    }
    unwindstack::MapInfo* sp_info = maps->Find(regs->sp());
    if (sp_info->flags & PROT_DEVICE_MAP) {
      break;
    }

    if (!elf->Step(rel_pc + map_info->elf_offset, regs, memory)) {
      break;
    }
  }

  return true;
}

UnwindStackCurrent::UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map)
    : BacktraceCurrent(pid, tid, map), memory_(new unwindstack::MemoryLocal) {}

std::string UnwindStackCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
  return ::GetFunctionName(Pid(), GetMap(), pc, offset);
}

bool UnwindStackCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) {
  std::unique_ptr<unwindstack::Regs> regs;
  if (ucontext == nullptr) {
    regs.reset(unwindstack::Regs::CreateFromLocal());
    // Fill in the registers from this function. Do it here to avoid
    // one extra function call appearing in the unwind.
    unwindstack::RegsGetLocal(regs.get());
  } else {
    regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::GetMachineType(), ucontext));
  }

  error_ = BACKTRACE_UNWIND_NO_ERROR;
  return ::Unwind(getpid(), memory_.get(), regs.get(), GetMap(), &frames_, num_ignore_frames);
}

UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
    : BacktracePtrace(pid, tid, map), memory_(new unwindstack::MemoryRemote(pid)) {}

std::string UnwindStackPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
  return ::GetFunctionName(Pid(), GetMap(), pc, offset);
}

bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, ucontext_t* context) {
  std::unique_ptr<unwindstack::Regs> regs;
  if (context == nullptr) {
    uint32_t machine_type;
    regs.reset(unwindstack::Regs::RemoteGet(Tid(), &machine_type));
  } else {
    regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::GetMachineType(), context));
  }

  error_ = BACKTRACE_UNWIND_NO_ERROR;
  return ::Unwind(Pid(), memory_.get(), regs.get(), GetMap(), &frames_, num_ignore_frames);
}

Backtrace* Backtrace::CreateNew(pid_t pid, pid_t tid, BacktraceMap* map) {
  if (pid == BACKTRACE_CURRENT_PROCESS) {
    pid = getpid();
    if (tid == BACKTRACE_CURRENT_THREAD) {
      tid = gettid();
    }
  } else if (tid == BACKTRACE_CURRENT_THREAD) {
    tid = pid;
  }

  if (map == nullptr) {
// This would cause the wrong type of map object to be created, so disallow.
#if defined(__ANDROID__)
    __assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__,
              "Backtrace::CreateNew() must be called with a real map pointer.");
#else
    BACK_LOGE("Backtrace::CreateNew() must be called with a real map pointer.");
    abort();
#endif
  }

  if (pid == getpid()) {
    return new UnwindStackCurrent(pid, tid, map);
  } else {
    return new UnwindStackPtrace(pid, tid, map);
  }
}
+56 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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 _LIBBACKTRACE_UNWIND_STACK_H
#define _LIBBACKTRACE_UNWIND_STACK_H

#include <stdint.h>

#include <string>

#include <backtrace/BacktraceMap.h>
#include <unwindstack/Memory.h>

#include "BacktraceCurrent.h"
#include "BacktracePtrace.h"

class UnwindStackCurrent : public BacktraceCurrent {
 public:
  UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map);
  virtual ~UnwindStackCurrent() = default;

  std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;

  bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) override;

 private:
  std::unique_ptr<unwindstack::Memory> memory_;
};

class UnwindStackPtrace : public BacktracePtrace {
 public:
  UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map);
  virtual ~UnwindStackPtrace() = default;

  bool Unwind(size_t num_ignore_frames, ucontext_t* context) override;

  std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);

 private:
  std::unique_ptr<unwindstack::Memory> memory_;
};

#endif  // _LIBBACKTRACE_UNWIND_STACK_H
+94 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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.
 */

#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>

#include <backtrace/BacktraceMap.h>
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>

#include "UnwindStackMap.h"

//-------------------------------------------------------------------------
UnwindStackMap::UnwindStackMap(pid_t pid) : BacktraceMap(pid) {}

bool UnwindStackMap::Build() {
  if (pid_ == 0) {
    pid_ = getpid();
    stack_maps_.reset(new unwindstack::LocalMaps);
  } else {
    stack_maps_.reset(new unwindstack::RemoteMaps(pid_));
  }

  if (!stack_maps_->Parse()) {
    return false;
  }

  // Iterate through the maps and fill in the backtrace_map_t structure.
  for (auto& map_info : *stack_maps_) {
    backtrace_map_t map;
    map.start = map_info.start;
    map.end = map_info.end;
    map.offset = map_info.offset;
    // Set to -1 so that it is demand loaded.
    map.load_bias = static_cast<uintptr_t>(-1);
    map.flags = map_info.flags;
    map.name = map_info.name;

    maps_.push_back(map);
  }

  return true;
}

void UnwindStackMap::FillIn(uintptr_t addr, backtrace_map_t* map) {
  BacktraceMap::FillIn(addr, map);
  if (map->load_bias != static_cast<uintptr_t>(-1)) {
    return;
  }

  // Fill in the load_bias.
  unwindstack::MapInfo* map_info = stack_maps_->Find(addr);
  if (map_info == nullptr) {
    return;
  }
  unwindstack::Elf* elf = map_info->GetElf(pid_, true);
  map->load_bias = elf->GetLoadBias();
}

//-------------------------------------------------------------------------
// BacktraceMap create function.
//-------------------------------------------------------------------------
BacktraceMap* BacktraceMap::CreateNew(pid_t pid, bool uncached) {
  BacktraceMap* map;

  if (uncached) {
    // Force use of the base class to parse the maps when this call is made.
    map = new BacktraceMap(pid);
  } else if (pid == getpid()) {
    map = new UnwindStackMap(0);
  } else {
    map = new UnwindStackMap(pid);
  }
  if (!map->Build()) {
    delete map;
    return nullptr;
  }
  return map;
}
+41 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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 _LIBBACKTRACE_UNWINDSTACK_MAP_H
#define _LIBBACKTRACE_UNWINDSTACK_MAP_H

#include <stdint.h>
#include <sys/types.h>

#include <backtrace/BacktraceMap.h>
#include <unwindstack/Maps.h>

class UnwindStackMap : public BacktraceMap {
 public:
  explicit UnwindStackMap(pid_t pid);
  ~UnwindStackMap() = default;

  bool Build() override;

  void FillIn(uintptr_t addr, backtrace_map_t* map) override;

  unwindstack::Maps* stack_maps() { return stack_maps_.get(); }

 protected:
  std::unique_ptr<unwindstack::Maps> stack_maps_;
};

#endif  // _LIBBACKTRACE_UNWINDSTACK_MAP_H
Loading