Skip to content

Commit c50cba6

Browse files
authored
[LLDB][SBSaveCore] Sbsavecore subregions bug (#138206)
Custom regions in Process::GetUserSpecifiedCoreFileSaveRanges originally used `FindEntryThatContains`. This made sense on my first attempt, but what we really want are *intersecting* regions. This is so the user can specify arbitrary memory, and if it's available we output it to the core (Minidump or MachO).
1 parent 1e353fa commit c50cba6

File tree

4 files changed

+325
-3
lines changed

4 files changed

+325
-3
lines changed

lldb/include/lldb/Utility/RangeMap.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,25 @@ template <typename B, typename S, unsigned N = 0> class RangeVector {
380380
return nullptr;
381381
}
382382

383+
const Entry *FindEntryThatIntersects(const Entry &range) const {
384+
#ifdef ASSERT_RANGEMAP_ARE_SORTED
385+
assert(IsSorted());
386+
#endif
387+
if (!m_entries.empty()) {
388+
typename Collection::const_iterator begin = m_entries.begin();
389+
typename Collection::const_iterator end = m_entries.end();
390+
typename Collection::const_iterator pos =
391+
std::lower_bound(begin, end, range, BaseLessThan);
392+
393+
while (pos != begin && pos[-1].DoesIntersect(range))
394+
--pos;
395+
396+
if (pos != end && pos->DoesIntersect(range))
397+
return &(*pos);
398+
}
399+
return nullptr;
400+
}
401+
383402
using const_iterator = typename Collection::const_iterator;
384403
const_iterator begin() const { return m_entries.begin(); }
385404
const_iterator end() const { return m_entries.end(); }

lldb/source/Target/Process.cpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6706,6 +6706,18 @@ static void GetCoreFileSaveRangesStackOnly(Process &process,
67066706
}
67076707
}
67086708

6709+
// TODO: We should refactor CoreFileMemoryRanges to use the lldb range type, and
6710+
// then add an intersect method on it, or MemoryRegionInfo.
6711+
static MemoryRegionInfo Intersect(const MemoryRegionInfo &lhs,
6712+
const MemoryRegionInfo::RangeType &rhs) {
6713+
6714+
MemoryRegionInfo region_info;
6715+
region_info.SetLLDBPermissions(lhs.GetLLDBPermissions());
6716+
region_info.GetRange() = lhs.GetRange().Intersect(rhs);
6717+
6718+
return region_info;
6719+
}
6720+
67096721
static void GetUserSpecifiedCoreFileSaveRanges(Process &process,
67106722
const MemoryRegionInfos &regions,
67116723
const SaveCoreOptions &options,
@@ -6715,9 +6727,15 @@ static void GetUserSpecifiedCoreFileSaveRanges(Process &process,
67156727
return;
67166728

67176729
for (const auto &range : regions) {
6718-
auto entry = option_ranges.FindEntryThatContains(range.GetRange());
6719-
if (entry)
6720-
AddRegion(range, true, ranges);
6730+
auto *entry = option_ranges.FindEntryThatIntersects(range.GetRange());
6731+
if (entry) {
6732+
if (*entry != range.GetRange()) {
6733+
AddRegion(Intersect(range, *entry), true, ranges);
6734+
} else {
6735+
// If they match, add the range directly.
6736+
AddRegion(range, true, ranges);
6737+
}
6738+
}
67216739
}
67226740
}
67236741

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
"""
2+
Test saving a mini dump, from yamilized examples.
3+
"""
4+
5+
import os
6+
import lldb
7+
from lldbsuite.test.decorators import *
8+
from lldbsuite.test.lldbtest import *
9+
from lldbsuite.test import lldbutil
10+
11+
12+
class AddressRange:
13+
begin: int
14+
end: int
15+
16+
def __init__(self, begin, end):
17+
self.begin = begin
18+
self.end = end
19+
20+
21+
# We skip all these tests on Windows because on Windows Minidumps
22+
# are not generated by LLDB.
23+
class ProcessSaveCoreMinidumpTestCaseYaml(TestBase):
24+
def process_from_yaml(self, yaml_file):
25+
minidump_path = self.getBuildArtifact(os.path.basename(yaml_file) + ".dmp")
26+
self.yaml2obj(yaml_file, minidump_path)
27+
self.target = self.dbg.CreateTarget(None)
28+
self.process = self.target.LoadCore(minidump_path)
29+
return self.process
30+
31+
@skipIfWindows
32+
def validate_regions_saved_correctly(
33+
self, core_process, expected_region, expected_invalid_region=None
34+
):
35+
"""Validate that the expected_region is saved in the core_proc, and that the expected invalid region is not saved, if not not none."""
36+
37+
# Validate we can read the entire expected_region
38+
error = lldb.SBError()
39+
core_process.ReadMemory(
40+
expected_region.begin, expected_region.end - expected_region.begin, error
41+
)
42+
self.assertTrue(error.Success(), error.GetCString())
43+
44+
# Validate we can't read before and after the expected_region
45+
core_process.ReadMemory(expected_region.begin - 1, 1, error)
46+
self.assertTrue(error.Fail(), error.GetCString())
47+
48+
core_process.ReadMemory(expected_region.end + 1, 1, error)
49+
self.assertTrue(error.Fail(), error.GetCString())
50+
51+
if expected_invalid_region is None:
52+
return
53+
54+
# Validate we can't read the original_region
55+
core_process.ReadMemory(
56+
expected_invalid_region.begin,
57+
expected_invalid_region.end - expected_invalid_region.begin,
58+
error,
59+
)
60+
self.assertTrue(error.Fail(), error.GetCString())
61+
62+
@skipIfWindows
63+
def test_saving_sub_memory_range(self):
64+
"""
65+
Validate we can save a Minidump for a subsection of a memory range.
66+
I.E.
67+
If our memory range is 0x2000-0x2020 and the user specifies 0x2000-0x2008
68+
we should still capture 0x2000-0x2008
69+
"""
70+
yaml = "minidump_mem64.yaml"
71+
proc = self.process_from_yaml(yaml)
72+
new_minidump_path = self.getBuildArtifact(__name__ + ".dmp")
73+
options = lldb.SBSaveCoreOptions()
74+
options.SetOutputFile(lldb.SBFileSpec(new_minidump_path))
75+
options.SetPluginName("minidump")
76+
options.SetStyle(lldb.eSaveCoreCustomOnly)
77+
78+
size = 8
79+
begin = 0x2000
80+
end = begin + size
81+
custom_range = lldb.SBMemoryRegionInfo("", begin, end, 3, True, False)
82+
options.AddMemoryRegionToSave(custom_range)
83+
84+
error = proc.SaveCore(options)
85+
self.assertTrue(error.Success(), error.GetCString())
86+
core_target = self.dbg.CreateTarget(None)
87+
core_process = core_target.LoadCore(new_minidump_path)
88+
89+
expected_address_range = AddressRange(begin, end)
90+
expected_invalid_range = AddressRange(begin, 0x2020)
91+
self.validate_regions_saved_correctly(
92+
core_process, expected_address_range, expected_invalid_range
93+
)
94+
95+
@skipIfWindows
96+
def test_saving_super_memory_range(self):
97+
"""
98+
Validate we can save a Minidump for a subsection of a memory range.
99+
I.E.
100+
If our memory range is 0x1000-0x1100 and the user specifies 0x900-x1200
101+
we should still capture 0x1000-0x1100
102+
"""
103+
yaml = "minidump_mem64.yaml"
104+
proc = self.process_from_yaml(yaml)
105+
new_minidump_path = self.getBuildArtifact(__name__ + ".dmp")
106+
options = lldb.SBSaveCoreOptions()
107+
options.SetOutputFile(lldb.SBFileSpec(new_minidump_path))
108+
options.SetPluginName("minidump")
109+
options.SetStyle(lldb.eSaveCoreCustomOnly)
110+
111+
size = 0x100
112+
begin = 0x1000
113+
end = begin + size
114+
custom_range = lldb.SBMemoryRegionInfo("", begin - 16, end + 16, 3, True, False)
115+
options.AddMemoryRegionToSave(custom_range)
116+
117+
error = proc.SaveCore(options)
118+
self.assertTrue(error.Success(), error.GetCString())
119+
core_target = self.dbg.CreateTarget(None)
120+
core_process = core_target.LoadCore(new_minidump_path)
121+
122+
expected_address_range = AddressRange(begin, end)
123+
expected_invalid_range = AddressRange(begin - 16, end + 16)
124+
self.validate_regions_saved_correctly(
125+
core_process, expected_address_range, expected_invalid_range
126+
)
127+
128+
@skipIfWindows
129+
def test_region_that_goes_out_of_bounds(self):
130+
"""
131+
Validate we can save a Minidump for a custom region
132+
that includes an end that enters an invalid (---) page.
133+
"""
134+
yaml = "minidump_mem64.yaml"
135+
proc = self.process_from_yaml(yaml)
136+
new_minidump_path = self.getBuildArtifact(__name__ + ".dmp")
137+
options = lldb.SBSaveCoreOptions()
138+
options.SetOutputFile(lldb.SBFileSpec(new_minidump_path))
139+
options.SetPluginName("minidump")
140+
options.SetStyle(lldb.eSaveCoreCustomOnly)
141+
142+
size = 0x120
143+
begin = 0x1000
144+
end = begin + size
145+
custom_range = lldb.SBMemoryRegionInfo("", begin, end, 3, True, False)
146+
options.AddMemoryRegionToSave(custom_range)
147+
148+
error = proc.SaveCore(options)
149+
self.assertTrue(error.Success(), error.GetCString())
150+
core_target = self.dbg.CreateTarget(None)
151+
core_process = core_target.LoadCore(new_minidump_path)
152+
153+
expected_address_range = AddressRange(begin, end)
154+
expected_invalid_range = AddressRange(begin - 16, end + 16)
155+
self.validate_regions_saved_correctly(
156+
core_process, expected_address_range, expected_invalid_range
157+
)
158+
159+
@skipIfWindows
160+
def test_region_that_starts_out_of_bounds(self):
161+
"""
162+
Validate we can save a Minidump for a custom region
163+
that includes a start in a (---) page but ends in a valid page.
164+
"""
165+
yaml = "minidump_mem64.yaml"
166+
proc = self.process_from_yaml(yaml)
167+
new_minidump_path = self.getBuildArtifact(__name__ + ".dmp")
168+
options = lldb.SBSaveCoreOptions()
169+
options.SetOutputFile(lldb.SBFileSpec(new_minidump_path))
170+
options.SetPluginName("minidump")
171+
options.SetStyle(lldb.eSaveCoreCustomOnly)
172+
173+
size = 0x20
174+
begin = 0x2000
175+
end = begin + size
176+
custom_range = lldb.SBMemoryRegionInfo("", begin - 16, end, 3, True, False)
177+
options.AddMemoryRegionToSave(custom_range)
178+
179+
error = proc.SaveCore(options)
180+
self.assertTrue(error.Success(), error.GetCString())
181+
core_target = self.dbg.CreateTarget(None)
182+
core_process = core_target.LoadCore(new_minidump_path)
183+
184+
expected_address_range = AddressRange(begin, end)
185+
expected_invalid_range = AddressRange(begin - 16, end)
186+
self.validate_regions_saved_correctly(
187+
core_process, expected_address_range, expected_invalid_range
188+
)
189+
190+
@skipIfWindows
191+
def test_region_spans_multiple_regions(self):
192+
"""
193+
Validate we can save a Minidump for a custom region
194+
that includes a start in a (---) page but ends in a valid page.
195+
"""
196+
yaml = "minidump_mem64.yaml"
197+
proc = self.process_from_yaml(yaml)
198+
new_minidump_path = self.getBuildArtifact(__name__ + ".dmp")
199+
options = lldb.SBSaveCoreOptions()
200+
options.SetOutputFile(lldb.SBFileSpec(new_minidump_path))
201+
options.SetPluginName("minidump")
202+
options.SetStyle(lldb.eSaveCoreCustomOnly)
203+
204+
size = 0x1000
205+
begin = 0x5000
206+
end = begin + size
207+
custom_range = lldb.SBMemoryRegionInfo("", begin, end, 3, True, False)
208+
options.AddMemoryRegionToSave(custom_range)
209+
210+
error = proc.SaveCore(options)
211+
self.assertTrue(error.Success(), error.GetCString())
212+
core_target = self.dbg.CreateTarget(None)
213+
core_process = core_target.LoadCore(new_minidump_path)
214+
215+
expected_address_range = AddressRange(begin, end)
216+
self.validate_regions_saved_correctly(core_process, expected_address_range)
217+
218+
@skipIfWindows
219+
def test_region_spans_multiple_regions_with_one_subrange(self):
220+
"""
221+
Validate we can save a Minidump for a custom region
222+
that includes a start in a (---) page but ends in a valid page.
223+
"""
224+
yaml = "minidump_mem64.yaml"
225+
proc = self.process_from_yaml(yaml)
226+
new_minidump_path = self.getBuildArtifact(__name__ + ".dmp")
227+
options = lldb.SBSaveCoreOptions()
228+
options.SetOutputFile(lldb.SBFileSpec(new_minidump_path))
229+
options.SetPluginName("minidump")
230+
options.SetStyle(lldb.eSaveCoreCustomOnly)
231+
232+
size = 0x800
233+
begin = 0x5000
234+
end = begin + size
235+
custom_range = lldb.SBMemoryRegionInfo("", begin, end, 3, True, False)
236+
options.AddMemoryRegionToSave(custom_range)
237+
238+
error = proc.SaveCore(options)
239+
self.assertTrue(error.Success(), error.GetCString())
240+
core_target = self.dbg.CreateTarget(None)
241+
core_process = core_target.LoadCore(new_minidump_path)
242+
243+
expected_address_range = AddressRange(begin, end)
244+
expected_invalid_range = AddressRange(begin, begin + 0x1000)
245+
self.validate_regions_saved_correctly(
246+
core_process, expected_address_range, expected_invalid_range
247+
)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--- !minidump
2+
Streams:
3+
- Type: SystemInfo
4+
Processor Arch: AMD64
5+
Processor Level: 6
6+
Processor Revision: 15876
7+
Number of Processors: 40
8+
Platform ID: Linux
9+
CSD Version: 'Linux 3.13.0-91-generic'
10+
CPU:
11+
Vendor ID: GenuineIntel
12+
Version Info: 0x00000000
13+
Feature Info: 0x00000000
14+
- Type: ThreadList
15+
Threads:
16+
- Thread Id: 0x2896BB
17+
Context: 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000700100000000000FFFFFFFF0000FFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B040A812FF7F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050D0A75BBA7F00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
18+
Stack:
19+
Start of Memory Range: 0x0
20+
Content: ''
21+
- Type: Memory64List
22+
Memory Ranges:
23+
- Start of Memory Range: 0x1000
24+
Data Size: 0x100
25+
Content : ''
26+
- Start of Memory Range: 0x2000
27+
Data Size: 0x20
28+
Content : ''
29+
- Start of Memory Range: 0x3000
30+
Data Size: 0x400
31+
Content : ''
32+
- Start of Memory Range: 0x5000
33+
Data Size: 0x500
34+
Content : ''
35+
- Start of Memory Range: 0x5500
36+
Data Size: 0x500
37+
Content : ''
38+
...

0 commit comments

Comments
 (0)