jarin created this revision.
jarin added a reviewer: labath.
jarin added a project: LLDB.
Herald added a subscriber: lldb-commits.

This patch adds parts of the stack that should be useful for unwinding to the 
jThreadsInfo reply from lldb-server. We return the top of the stack (12 words), 
and we also try to walk the frame pointer linked list and return the memory 
containing frame pointer and return address pairs. The idea is to cover the 
cases with and without frame pointer omission.

Here are some questions:

- Does this approach sound reasonable?
- How do we test this?
- Is it fine if we do not handle the big-endian and 32-bit word cases? (There 
we will be basically never generate the frame list.)


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D74398

Files:
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp

Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -469,6 +469,118 @@
   return register_object;
 }
 
+static llvm::Expected<RegisterValue>
+GetRegisterValue(NativeRegisterContext &reg_ctx, uint32_t generic_regnum) {
+  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
+  uint32_t reg_num = reg_ctx.ConvertRegisterKindToRegisterNumber(
+      eRegisterKindGeneric, generic_regnum);
+  const RegisterInfo *const reg_info_p =
+      reg_ctx.GetRegisterInfoAtIndex(reg_num);
+
+  if (reg_info_p == nullptr || reg_info_p->value_regs != nullptr) {
+    LLDB_LOGF(log, "%s failed to get register info for register index %" PRIu32,
+              __FUNCTION__, reg_num);
+    return llvm::make_error<llvm::StringError>("failed to obtain register info",
+                                               llvm::inconvertibleErrorCode());
+  }
+
+  RegisterValue reg_value;
+  Status error = reg_ctx.ReadRegister(reg_info_p, reg_value);
+  if (error.Fail()) {
+    LLDB_LOGF(log, "%s failed to read register '%s' index %" PRIu32 ": %s",
+              __FUNCTION__,
+              reg_info_p->name ? reg_info_p->name : "<unnamed-register>",
+              reg_num, error.AsCString());
+    return llvm::make_error<llvm::StringError>("failed to read register value",
+                                               llvm::inconvertibleErrorCode());
+  }
+  return reg_value;
+}
+
+static void AddMemoryChunk(json::Array &stack_memory_chunks, addr_t address,
+                           std::vector<int8_t> &bytes) {
+  if (bytes.empty())
+    return;
+  json::Object chunk;
+  chunk.try_emplace("address", static_cast<int64_t>(address));
+  StreamString stream;
+  for (int8_t b : bytes)
+    stream.PutHex8(b);
+  chunk.try_emplace("bytes", stream.GetString().str());
+  stack_memory_chunks.push_back(std::move(chunk));
+}
+
+static json::Array GetStackMemoryAsJSON(NativeProcessProtocol &process,
+                                        NativeThreadProtocol &thread) {
+  const size_t kStackTopMemoryInfoWordSize = 12;
+  const size_t kStackTopMemoryInfoByteSize =
+      kStackTopMemoryInfoWordSize * sizeof(addr_t);
+  const size_t kMaxStackSize = 128 * 1024;
+  const size_t kMaxFrameSize = 4 * 1024;
+  const size_t kFpAndRaSize = 2 * sizeof(addr_t);
+  const size_t kMaxFrameCount = 128;
+
+  NativeRegisterContext &reg_ctx = thread.GetRegisterContext();
+
+  json::Array stack_memory_chunks;
+
+  lldb::addr_t sp_value;
+  if (llvm::Expected<RegisterValue> expected_sp_value =
+          GetRegisterValue(reg_ctx, LLDB_REGNUM_GENERIC_SP)) {
+    sp_value = expected_sp_value->GetAsUInt64();
+  } else {
+    return stack_memory_chunks;
+  }
+  lldb::addr_t fp_value;
+  if (llvm::Expected<RegisterValue> expected_fp_value =
+          GetRegisterValue(reg_ctx, LLDB_REGNUM_GENERIC_FP)) {
+    fp_value = expected_fp_value->GetAsUInt64();
+  } else {
+    return stack_memory_chunks;
+  }
+
+  // First, make sure we copy the top kStackTopMemoryInfoSize bytes from the
+  // stack.
+  size_t byte_count = std::min(kStackTopMemoryInfoByteSize,
+                               static_cast<size_t>(fp_value - sp_value));
+  std::vector<int8_t> buf(byte_count, 0);
+
+  size_t bytes_read = 0;
+  Status error = process.ReadMemoryWithoutTrap(sp_value, buf.data(), byte_count,
+                                               bytes_read);
+  if (error.Success() && bytes_read > 0) {
+    buf.resize(bytes_read);
+    AddMemoryChunk(stack_memory_chunks, sp_value, buf);
+  }
+
+  // Additionally, try to walk the frame pointer link chain. If the frame
+  // is too big or if the frame pointer points too far, stop the walk.
+  addr_t max_frame_pointer = sp_value + kMaxStackSize;
+  for (size_t i = 0; i < kMaxFrameCount; i++) {
+    if (fp_value < sp_value || fp_value > sp_value + kMaxFrameSize ||
+        fp_value > max_frame_pointer)
+      break;
+
+    std::vector<int8_t> fp_ra_buf(kFpAndRaSize, 0);
+    bytes_read = 0;
+    error = process.ReadMemoryWithoutTrap(fp_value, fp_ra_buf.data(),
+                                          kFpAndRaSize, bytes_read);
+    if (error.Fail() || bytes_read != kFpAndRaSize)
+      break;
+
+    AddMemoryChunk(stack_memory_chunks, fp_value, fp_ra_buf);
+
+    // Advance the stack pointer and the frame pointer.
+    sp_value = fp_value;
+    // This assumes little-endian 64-bit pointers in the target. If the target
+    // has different config, the checks in the beginning of this loop will
+    // very likely fail.
+    memcpy(&fp_value, &fp_ra_buf[0], sizeof(addr_t));
+  }
+
+  return stack_memory_chunks;
+}
+
 static const char *GetStopReasonString(StopReason stop_reason) {
   switch (stop_reason) {
   case eStopReasonTrace:
@@ -533,6 +645,9 @@
       } else {
         return registers.takeError();
       }
+      json::Array stack_memory = GetStackMemoryAsJSON(process, *thread);
+      if (!stack_memory.empty())
+        thread_obj.try_emplace("memory", std::move(stack_memory));
     }
 
     thread_obj.try_emplace("tid", static_cast<int64_t>(tid));
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to