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 +GetRegisterValue(NativeRegisterContext ®_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("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 : "", + reg_num, error.AsCString()); + return llvm::make_error("failed to read register value", + llvm::inconvertibleErrorCode()); + } + return reg_value; +} + +static void AddMemoryChunk(json::Array &stack_memory_chunks, addr_t address, + std::vector &bytes) { + if (bytes.empty()) + return; + json::Object chunk; + chunk.try_emplace("address", static_cast(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 ®_ctx = thread.GetRegisterContext(); + + json::Array stack_memory_chunks; + + lldb::addr_t sp_value; + if (llvm::Expected 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 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(fp_value - sp_value)); + std::vector 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 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(tid));