Merge "libbacktrace: export offline unwinding failures."
am: 742fc190c8

Change-Id: Ic0443e038996a0b8d88af3c03c196aca5df85fee
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index e18dbf3..5bb6edc 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -142,22 +142,33 @@
 }
 
 std::string Backtrace::GetErrorString(BacktraceUnwindError error) {
-  switch (error) {
-  case BACKTRACE_UNWIND_NO_ERROR:
-    return "No error";
-  case BACKTRACE_UNWIND_ERROR_SETUP_FAILED:
-    return "Setup failed";
-  case BACKTRACE_UNWIND_ERROR_MAP_MISSING:
-    return "No map found";
-  case BACKTRACE_UNWIND_ERROR_INTERNAL:
-    return "Internal libbacktrace error, please submit a bugreport";
-  case BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST:
-    return "Thread doesn't exist";
-  case BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT:
-    return "Thread has not responded to signal in time";
-  case BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION:
-    return "Attempt to use an unsupported feature";
-  case BACKTRACE_UNWIND_ERROR_NO_CONTEXT:
-    return "Attempt to do an offline unwind without a context";
+  switch (error.error_code) {
+    case BACKTRACE_UNWIND_NO_ERROR:
+      return "No error";
+    case BACKTRACE_UNWIND_ERROR_SETUP_FAILED:
+      return "Setup failed";
+    case BACKTRACE_UNWIND_ERROR_MAP_MISSING:
+      return "No map found";
+    case BACKTRACE_UNWIND_ERROR_INTERNAL:
+      return "Internal libbacktrace error, please submit a bugreport";
+    case BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST:
+      return "Thread doesn't exist";
+    case BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT:
+      return "Thread has not responded to signal in time";
+    case BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION:
+      return "Attempt to use an unsupported feature";
+    case BACKTRACE_UNWIND_ERROR_NO_CONTEXT:
+      return "Attempt to do an offline unwind without a context";
+    case BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT:
+      return "Exceed MAX_BACKTRACE_FRAMES limit";
+    case BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED:
+      return android::base::StringPrintf("Failed to read memory at addr 0x%" PRIx64,
+                                         error.error_info.addr);
+    case BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED:
+      return android::base::StringPrintf("Failed to read register %" PRIu64, error.error_info.regno);
+    case BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED:
+      return "Failed to find a function in debug sections";
+    case BACKTRACE_UNWIND_ERROR_EXECUTE_DWARF_INSTRUCTION_FAILED:
+      return "Failed to execute dwarf instructions in debug sections";
   }
 }
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index fb76b85..474d099 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -67,11 +67,11 @@
 bool BacktraceCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
   if (GetMap() == nullptr) {
     // Without a map object, we can't do anything.
-    error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
     return false;
   }
 
-  error_ = BACKTRACE_UNWIND_NO_ERROR;
+  error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
   if (ucontext) {
     return UnwindFromContext(num_ignore_frames, ucontext);
   }
@@ -163,7 +163,7 @@
     BACK_ASYNC_SAFE_LOGE("sigaction failed: %s", strerror(errno));
     ThreadEntry::Remove(entry);
     pthread_mutex_unlock(&g_sigaction_mutex);
-    error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_INTERNAL;
     return false;
   }
 
@@ -171,9 +171,9 @@
     // Do not emit an error message, this might be expected. Set the
     // error and let the caller decide.
     if (errno == ESRCH) {
-      error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
+      error_.error_code = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
     } else {
-      error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
+      error_.error_code = BACKTRACE_UNWIND_ERROR_INTERNAL;
     }
 
     sigaction(THREAD_SIGNAL, &oldact, nullptr);
@@ -218,9 +218,9 @@
   } else {
     // Check to see if the thread has disappeared.
     if (tgkill(Pid(), Tid(), 0) == -1 && errno == ESRCH) {
-      error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
+      error_.error_code = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
     } else {
-      error_ = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
+      error_.error_code = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
       BACK_ASYNC_SAFE_LOGE("Timed out waiting for signal handler to get ucontext data.");
     }
   }
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
index 641f712..e290b84 100644
--- a/libbacktrace/BacktraceOffline.cpp
+++ b/libbacktrace/BacktraceOffline.cpp
@@ -174,11 +174,11 @@
 bool BacktraceOffline::Unwind(size_t num_ignore_frames, ucontext_t* context) {
   if (context == nullptr) {
     BACK_LOGW("The context is needed for offline backtracing.");
-    error_ = BACKTRACE_UNWIND_ERROR_NO_CONTEXT;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_NO_CONTEXT;
     return false;
   }
   context_ = context;
-  error_ = BACKTRACE_UNWIND_NO_ERROR;
+  error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
 
   unw_addr_space_t addr_space = unw_create_addr_space(&accessors, 0);
   unw_cursor_t cursor;
@@ -186,11 +186,11 @@
   if (ret != 0) {
     BACK_LOGW("unw_init_remote failed %d", ret);
     unw_destroy_addr_space(addr_space);
-    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     return false;
   }
   size_t num_frames = 0;
-  do {
+  while (true) {
     unw_word_t pc;
     ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
     if (ret < 0) {
@@ -224,7 +224,17 @@
     }
     is_debug_frame_used_ = false;
     ret = unw_step(&cursor);
-  } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
+    if (ret <= 0) {
+      if (error_.error_code == BACKTRACE_UNWIND_NO_ERROR) {
+        error_.error_code = BACKTRACE_UNWIND_ERROR_EXECUTE_DWARF_INSTRUCTION_FAILED;
+      }
+      break;
+    }
+    if (num_frames == MAX_BACKTRACE_FRAMES) {
+      error_.error_code = BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT;
+      break;
+    }
+  }
 
   unw_destroy_addr_space(addr_space);
   context_ = nullptr;
@@ -259,7 +269,12 @@
     return read_size;
   }
   read_size = stack_space_.Read(addr, buffer, bytes);
-  return read_size;
+  if (read_size != 0) {
+    return read_size;
+  }
+  error_.error_code = BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED;
+  error_.error_info.addr = addr;
+  return 0;
 }
 
 bool BacktraceOffline::FindProcInfo(unw_addr_space_t addr_space, uint64_t ip,
@@ -267,13 +282,17 @@
   backtrace_map_t map;
   FillInMap(ip, &map);
   if (!BacktraceMap::IsValid(map)) {
+    error_.error_code = BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED;
     return false;
   }
   const std::string& filename = map.name;
   DebugFrameInfo* debug_frame = GetDebugFrameInFile(filename);
   if (debug_frame == nullptr) {
+    error_.error_code = BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED;
     return false;
   }
+  // Each FindProcInfo() is a new attempt to unwind, so reset the reason.
+  error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
 
   eh_frame_hdr_space_.Clear();
   eh_frame_space_.Clear();
@@ -367,6 +386,7 @@
       }
     }
   }
+  error_.error_code = BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED;
   return false;
 }
 
@@ -548,6 +568,10 @@
   UNUSED(value);
   result = false;
 #endif
+  if (!result) {
+    error_.error_code = BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED;
+    error_.error_info.regno = reg;
+  }
   return result;
 }
 
diff --git a/libbacktrace/BacktraceOffline.h b/libbacktrace/BacktraceOffline.h
index 70a9842..fcde379 100644
--- a/libbacktrace/BacktraceOffline.h
+++ b/libbacktrace/BacktraceOffline.h
@@ -32,9 +32,7 @@
   uint64_t end;
   const uint8_t* data;
 
-  Space() {
-    Clear();
-  }
+  Space() { Clear(); }
 
   void Clear();
   size_t Read(uint64_t addr, uint8_t* buffer, size_t size);
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index 2c87fa8..3ccf13c 100644
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -81,7 +81,7 @@
     int ret = unw_getcontext(&context_);
     if (ret < 0) {
       BACK_LOGW("unw_getcontext failed %d", ret);
-      error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+      error_.error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
       return false;
     }
   } else {
@@ -93,7 +93,7 @@
   int ret = unw_init_local(cursor.get(), &context_);
   if (ret < 0) {
     BACK_LOGW("unw_init_local failed %d", ret);
-    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     return false;
   }
   initialized_ = true;
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index 87282ef..2155b8a 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -62,7 +62,7 @@
   addr_space_ = unw_create_addr_space(&_UPT_accessors, 0);
   if (!addr_space_) {
     BACK_LOGW("unw_create_addr_space failed.");
-    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     return false;
   }
 
@@ -72,7 +72,7 @@
   upt_info_ = reinterpret_cast<struct UPT_info*>(_UPT_create(Tid()));
   if (!upt_info_) {
     BACK_LOGW("Failed to create upt info.");
-    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     return false;
   }
 
@@ -82,15 +82,15 @@
 bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
   if (GetMap() == nullptr) {
     // Without a map object, we can't do anything.
-    error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
     return false;
   }
 
-  error_ = BACKTRACE_UNWIND_NO_ERROR;
+  error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
 
   if (ucontext) {
     BACK_LOGW("Unwinding from a specified context not supported yet.");
-    error_ = BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION;
     return false;
   }
 
@@ -102,7 +102,7 @@
   int ret = unw_init_remote(&cursor, addr_space_, upt_info_);
   if (ret < 0) {
     BACK_LOGW("unw_init_remote failed %d", ret);
-    error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+    error_.error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
     return false;
   }
 
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 56a6c68..2a555af 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -102,7 +102,7 @@
     regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
   }
 
-  error_ = BACKTRACE_UNWIND_NO_ERROR;
+  error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
   std::vector<std::string> skip_names{"libunwindstack.so", "libbacktrace.so"};
   return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, &skip_names);
 }
@@ -122,7 +122,7 @@
     regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), context));
   }
 
-  error_ = BACKTRACE_UNWIND_NO_ERROR;
+  error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
   return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, nullptr);
 }
 
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
index 9ba2b1c..64172b5 100644
--- a/libbacktrace/backtrace_offline_test.cpp
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -397,6 +397,8 @@
     std::string name = FunctionNameForAddress(vaddr_in_file, testdata.symbols);
     ASSERT_EQ(name, testdata.symbols[i].name);
   }
+  ASSERT_EQ(BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED, backtrace->GetError().error_code);
+  ASSERT_NE(0u, backtrace->GetError().error_info.addr);
 }
 
 // This test tests the situation that ranges of functions covered by .eh_frame and .ARM.exidx
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 890ab3f..57b7553 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -189,7 +189,7 @@
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
   VerifyLevelDump(backtrace.get());
 }
@@ -211,7 +211,7 @@
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
   VerifyMaxDump(backtrace.get());
 }
@@ -241,7 +241,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
   ASSERT_TRUE(backtrace->NumFrames() != 0);
   for (const auto& frame : *backtrace ) {
@@ -292,19 +292,19 @@
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(all.get() != nullptr);
   ASSERT_TRUE(all->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError().error_code);
 
   std::unique_ptr<Backtrace> ign1(
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(ign1.get() != nullptr);
   ASSERT_TRUE(ign1->Unwind(1));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError().error_code);
 
   std::unique_ptr<Backtrace> ign2(
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(ign2.get() != nullptr);
   ASSERT_TRUE(ign2->Unwind(2));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError().error_code);
 
   VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames");
 }
@@ -340,7 +340,7 @@
       std::unique_ptr<Backtrace> backtrace(create_func(pid, tid, map.get()));
       ASSERT_TRUE(backtrace.get() != nullptr);
       ASSERT_TRUE(backtrace->Unwind(0));
-      ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+      ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
       if (ReadyFunc(backtrace.get())) {
         VerifyFunc(backtrace.get(), create_func, map_create_func);
         verified = true;
@@ -389,12 +389,12 @@
   std::unique_ptr<Backtrace> ign1(create_func(bt_all->Pid(), BACKTRACE_CURRENT_THREAD, map.get()));
   ASSERT_TRUE(ign1.get() != nullptr);
   ASSERT_TRUE(ign1->Unwind(1));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError().error_code);
 
   std::unique_ptr<Backtrace> ign2(create_func(bt_all->Pid(), BACKTRACE_CURRENT_THREAD, map.get()));
   ASSERT_TRUE(ign2.get() != nullptr);
   ASSERT_TRUE(ign2->Unwind(2));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError().error_code);
 
   VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), nullptr);
 }
@@ -480,7 +480,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
   VerifyLevelDump(backtrace.get());
 }
@@ -493,7 +493,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
   VerifyMaxDump(backtrace.get());
 }
@@ -535,7 +535,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
   VerifyLevelDump(backtrace.get());
 
@@ -575,17 +575,17 @@
   std::unique_ptr<Backtrace> all(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(all.get() != nullptr);
   ASSERT_TRUE(all->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError().error_code);
 
   std::unique_ptr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(ign1.get() != nullptr);
   ASSERT_TRUE(ign1->Unwind(1));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError().error_code);
 
   std::unique_ptr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(ign2.get() != nullptr);
   ASSERT_TRUE(ign2->Unwind(2));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError().error_code);
 
   VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), nullptr);
 
@@ -616,7 +616,7 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
   VerifyMaxDump(backtrace.get());
 
@@ -713,21 +713,21 @@
   Backtrace* back1 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map1);
   ASSERT_TRUE(back1 != nullptr);
   EXPECT_TRUE(back1->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back1->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back1->GetError().error_code);
   delete back1;
   delete map1;
 
   Backtrace* back2 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map2);
   ASSERT_TRUE(back2 != nullptr);
   EXPECT_TRUE(back2->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back2->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back2->GetError().error_code);
   delete back2;
   delete map2;
 
   Backtrace* back3 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map3);
   ASSERT_TRUE(back3 != nullptr);
   EXPECT_TRUE(back3->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back3->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back3->GetError().error_code);
   delete back3;
   delete map3;
 }
@@ -1331,7 +1331,7 @@
                                                          BACKTRACE_CURRENT_THREAD));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_TRUE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
   size_t frame_num;
   ASSERT_TRUE(FindFuncFrameInBacktrace(backtrace.get(), test_func, &frame_num));
@@ -1388,7 +1388,7 @@
     std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
     ASSERT_TRUE(backtrace.get() != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
-    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
 
     size_t frame_num;
     if (FindFuncFrameInBacktrace(backtrace.get(),
@@ -1417,7 +1417,7 @@
       Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 99999999));
   ASSERT_TRUE(backtrace.get() != nullptr);
   ASSERT_FALSE(backtrace->Unwind(0));
-  ASSERT_EQ(BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST, backtrace->GetError());
+  ASSERT_EQ(BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST, backtrace->GetError().error_code);
 }
 
 TEST(libbacktrace, local_get_function_name_before_unwind) {
@@ -1785,7 +1785,7 @@
     Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
     ASSERT_TRUE(backtrace != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
-    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
     delete backtrace;
   }
   size_t stable_pss = GetPssBytes();
@@ -1796,7 +1796,7 @@
     Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
     ASSERT_TRUE(backtrace != nullptr);
     ASSERT_TRUE(backtrace->Unwind(0));
-    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
+    ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
     delete backtrace;
   }
   size_t new_pss = GetPssBytes();
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index e073533..5922664 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -34,7 +34,7 @@
 typedef uint32_t word_t;
 #endif
 
-enum BacktraceUnwindError : uint32_t {
+enum BacktraceUnwindErrorCode : uint32_t {
   BACKTRACE_UNWIND_NO_ERROR,
   // Something failed while trying to perform the setup to begin the unwind.
   BACKTRACE_UNWIND_ERROR_SETUP_FAILED,
@@ -50,6 +50,29 @@
   BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION,
   // Attempt to do an offline unwind without a context.
   BACKTRACE_UNWIND_ERROR_NO_CONTEXT,
+  // The count of frames exceed MAX_BACKTRACE_FRAMES.
+  BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT,
+  // Failed to read memory.
+  BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED,
+  // Failed to read registers.
+  BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED,
+  // Failed to find a function in debug sections.
+  BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED,
+  // Failed to execute dwarf instructions in debug sections.
+  BACKTRACE_UNWIND_ERROR_EXECUTE_DWARF_INSTRUCTION_FAILED,
+};
+
+struct BacktraceUnwindError {
+  enum BacktraceUnwindErrorCode error_code;
+
+  union {
+    // for BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED
+    uint64_t addr;
+    // for BACKTRACE_UNWIND_ERROR_ACCESS_REG_FAILED
+    uint64_t regno;
+  } error_info;
+
+  BacktraceUnwindError() : error_code(BACKTRACE_UNWIND_NO_ERROR) {}
 };
 
 struct backtrace_frame_data_t {