Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[lldb-server] jThreadsInfo returns stack memory
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. Differential Revision: https://reviews.llvm.org/D74398
- Loading branch information
Showing
5 changed files
with
317 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
CXX_SOURCES := main.cpp | ||
|
||
include Makefile.rules |
99 changes: 99 additions & 0 deletions
99
lldb/test/API/tools/lldb-server/threads-info/TestGdbRemoteThreadsInfoMemory.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
|
||
import json | ||
|
||
import gdbremote_testcase | ||
from lldbsuite.test.decorators import * | ||
from lldbsuite.test.lldbtest import * | ||
|
||
def invert_byte_order(a): | ||
return "".join(reversed([a[i:i+2] for i in range(0, len(a),2)])) | ||
|
||
def decode_hex(a): | ||
return int(invert_byte_order(a), 16) | ||
|
||
def encode_hex(a): | ||
return invert_byte_order("%016x" % a) | ||
|
||
class TestGdbRemoteThreadsInfoMemory(gdbremote_testcase.GdbRemoteTestCaseBase): | ||
|
||
mydir = TestBase.compute_mydir(__file__) | ||
|
||
@skipIf(archs=no_match(["x86_64"])) | ||
def threadsInfoStackCorrect(self): | ||
procs = self.prep_debug_monitor_and_inferior() | ||
|
||
self.add_register_info_collection_packets() | ||
context = self.expect_gdbremote_sequence() | ||
self.assertIsNotNone(context) | ||
|
||
# Gather register info. | ||
reg_infos = self.parse_register_info_packets(context) | ||
self.assertIsNotNone(reg_infos) | ||
self.add_lldb_register_index(reg_infos) | ||
# Index register info entries by name. | ||
reg_infos = {info['name']: info for info in reg_infos} | ||
|
||
# Send vCont packet to resume the inferior. | ||
self.test_sequence.add_log_lines(["read packet: $vCont;c#a8", | ||
{"direction": "send", | ||
"regex": r"^\$T([0-9a-fA-F]{2}).*#[0-9a-fA-F]{2}$", | ||
"capture": {1: "hex_exit_code"}}, | ||
], | ||
True) | ||
|
||
# Send g packet to retrieve the register bank | ||
self.test_sequence.add_log_lines( | ||
[ | ||
"read packet: $jThreadsInfo#c1", | ||
{ | ||
"direction": "send", | ||
"regex": r"^\$(.*)#[0-9a-fA-F]{2}$", | ||
"capture": { | ||
1: "threads_info"}}, | ||
], | ||
True) | ||
|
||
context = self.expect_gdbremote_sequence() | ||
threads_info = context["threads_info"] | ||
threads_info = json.loads(self.decode_gdbremote_binary(threads_info)) | ||
self.assertEqual(1, len(threads_info)) | ||
thread = threads_info[0] | ||
|
||
# Read the stack pointer and the frame pointer from the jThreadsInfo | ||
# reply. | ||
rsp_id = reg_infos["rsp"]["lldb_register_index"] | ||
sp = decode_hex(thread["registers"][str(rsp_id)]) | ||
rbp_id = reg_infos["rbp"]["lldb_register_index"] | ||
fp = decode_hex(thread["registers"][str(rbp_id)]) | ||
|
||
# The top frame size is 3 words. | ||
self.assertEqual(sp + 3 * 8, fp) | ||
|
||
# Check the memory chunks. | ||
chunks = thread["memory"] | ||
self.assertEqual(3, len(chunks)) | ||
# First memory chunk should contain everything between sp and fp. | ||
self.assertEqual(sp, chunks[0]["address"]) | ||
self.assertEqual(encode_hex(6) + encode_hex(5) + encode_hex(4), | ||
chunks[0]["bytes"]) | ||
# Second chunk should be at |fp|, its return address should be 0xfeed, | ||
# and the next fp should 5 words away (3 values, ra and fp). | ||
self.assertEqual(fp, chunks[1]["address"]) | ||
next_fp = fp + 5 * 8 | ||
self.assertEqual(encode_hex(next_fp) + encode_hex(0xfeed), | ||
chunks[1]["bytes"]) | ||
# Third chunk at |next_fp|, the next fp is 0x1008 bytes away and | ||
# the ra is 0xf00d. | ||
self.assertEqual(next_fp, chunks[2]["address"]) | ||
next_fp = next_fp + 0x1008 | ||
self.assertEqual(encode_hex(next_fp) + encode_hex(0xf00d), | ||
chunks[2]["bytes"]) | ||
|
||
@expectedFailureAll(oslist=["windows"]) | ||
@skipIfNetBSD | ||
@llgs_test | ||
def test_g_returns_correct_data_with_suffix_llgs(self): | ||
self.init_llgs_test() | ||
self.build() | ||
self.set_inferior_startup_launch() | ||
self.threadsInfoStackCorrect() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
int main() { | ||
#if defined(__x86_64__) | ||
// We setup two fake frames with frame pointer linking. The test will then | ||
// check that lldb-server's jThreadsInfo reply includes the top frame's | ||
// contents and the linked list of (frame-pointer, return-address) pairs. We | ||
// pretend the next frame is too large to stop the frame walk. | ||
asm volatile("movabsq $0xf00d, %rax\n\t" | ||
"pushq %rax\n\t" // fake return address | ||
"leaq 0x1000(%rsp), %rbp\n\t" // larger than kMaxFrameSize | ||
"pushq %rbp\n\t" | ||
"movq %rsp, %rbp\n\t" | ||
"pushq $1\n\t" // fake frame contents | ||
"pushq $2\n\t" | ||
"pushq $3\n\t" | ||
"\n\t" | ||
"movabsq $0xfeed, %rax\n\t" | ||
"push %rax\n\t" // second fake return address | ||
"pushq %rbp\n\t" | ||
"movq %rsp, %rbp\n\t" | ||
"pushq $4\n\t" // fake frame contents | ||
"pushq $5\n\t" | ||
"pushq $6\n\t" | ||
"\n\t" | ||
"int3\n\t"); | ||
#endif | ||
return 0; | ||
} |