Skip to content

Commit 8c31efe

Browse files
committed
Add the ability to process save-core stack-memory-only corefiles
Add a field to the qMemoryRegionInfo packet where the remote stub can describe the type of memory -- heap, stack. Keep track of memory regions that are stack memory in lldb. Add a new "--style stack" to process save-core to request that only stack memory be included in the corefile. Differential Revision: https://reviews.llvm.org/D107625
1 parent 7f3c3d7 commit 8c31efe

File tree

15 files changed

+212
-26
lines changed

15 files changed

+212
-26
lines changed

lldb/docs/lldb-gdb-remote.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,6 +1206,9 @@ tuples to return are:
12061206
// is "mt" for AArch64 memory tagging. lldb will
12071207
// ignore any other flags in this field.
12081208

1209+
type:[<type>][,<type>]; // memory types that apply to this region, e.g.
1210+
// "stack" for stack memory.
1211+
12091212
error:<ascii-byte-error-string>; // where <ascii-byte-error-string> is
12101213
// a hex encoded string value that
12111214
// contains an error string

lldb/include/lldb/Target/MemoryRegionInfo.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ class MemoryRegionInfo {
2828
MemoryRegionInfo(RangeType range, OptionalBool read, OptionalBool write,
2929
OptionalBool execute, OptionalBool mapped, ConstString name,
3030
OptionalBool flash, lldb::offset_t blocksize,
31-
OptionalBool memory_tagged)
31+
OptionalBool memory_tagged, OptionalBool stack_memory)
3232
: m_range(range), m_read(read), m_write(write), m_execute(execute),
3333
m_mapped(mapped), m_name(name), m_flash(flash), m_blocksize(blocksize),
34-
m_memory_tagged(memory_tagged) {}
34+
m_memory_tagged(memory_tagged), m_is_stack_memory(stack_memory) {}
3535

3636
RangeType &GetRange() { return m_range; }
3737

@@ -98,7 +98,8 @@ class MemoryRegionInfo {
9898
m_mapped == rhs.m_mapped && m_name == rhs.m_name &&
9999
m_flash == rhs.m_flash && m_blocksize == rhs.m_blocksize &&
100100
m_memory_tagged == rhs.m_memory_tagged &&
101-
m_pagesize == rhs.m_pagesize;
101+
m_pagesize == rhs.m_pagesize &&
102+
m_is_stack_memory == rhs.m_is_stack_memory;
102103
}
103104

104105
bool operator!=(const MemoryRegionInfo &rhs) const { return !(*this == rhs); }
@@ -116,6 +117,10 @@ class MemoryRegionInfo {
116117
return m_dirty_pages;
117118
}
118119

120+
OptionalBool IsStackMemory() const { return m_is_stack_memory; }
121+
122+
void SetIsStackMemory(OptionalBool val) { m_is_stack_memory = val; }
123+
119124
void SetPageSize(int pagesize) { m_pagesize = pagesize; }
120125

121126
void SetDirtyPageList(std::vector<lldb::addr_t> pagelist) {
@@ -134,6 +139,7 @@ class MemoryRegionInfo {
134139
OptionalBool m_flash = eDontKnow;
135140
lldb::offset_t m_blocksize = 0;
136141
OptionalBool m_memory_tagged = eDontKnow;
142+
OptionalBool m_is_stack_memory = eDontKnow;
137143
int m_pagesize = 0;
138144
llvm::Optional<std::vector<lldb::addr_t>> m_dirty_pages;
139145
};

lldb/include/lldb/lldb-enumerations.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,7 @@ enum SaveCoreStyle {
11371137
eSaveCoreUnspecified = 0,
11381138
eSaveCoreFull = 1,
11391139
eSaveCoreDirtyOnly = 2,
1140+
eSaveCoreStackOnly = 3,
11401141
};
11411142

11421143
} // namespace lldb

lldb/source/Commands/CommandObjectProcess.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,7 +1166,9 @@ class CommandObjectProcessKill : public CommandObjectParsed {
11661166
static constexpr OptionEnumValueElement g_corefile_save_style[] = {
11671167
{eSaveCoreFull, "full", "Create a core file with all memory saved"},
11681168
{eSaveCoreDirtyOnly, "modified-memory",
1169-
"Create a corefile with only modified memory saved"}};
1169+
"Create a corefile with only modified memory saved"},
1170+
{eSaveCoreStackOnly, "stack",
1171+
"Create a corefile with only stack memory saved"}};
11701172

11711173
static constexpr OptionEnumValues SaveCoreStyles() {
11721174
return OptionEnumValues(g_corefile_save_style);
@@ -1237,11 +1239,12 @@ class CommandObjectProcessSaveCore : public CommandObjectParsed {
12371239
Status error =
12381240
PluginManager::SaveCore(process_sp, output_file, corefile_style);
12391241
if (error.Success()) {
1240-
if (corefile_style == SaveCoreStyle::eSaveCoreDirtyOnly) {
1242+
if (corefile_style == SaveCoreStyle::eSaveCoreDirtyOnly ||
1243+
corefile_style == SaveCoreStyle::eSaveCoreStackOnly) {
12411244
result.AppendMessageWithFormat(
1242-
"\nModified-memory only corefile "
1243-
"created. This corefile may not show \n"
1244-
"library/framework/app binaries "
1245+
"\nModified-memory or stack-memory only corefile "
1246+
"created. This corefile may \n"
1247+
"not show library/framework/app binaries "
12451248
"on a different system, or when \n"
12461249
"those binaries have "
12471250
"been updated/modified. Copies are not included\n"

lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6342,13 +6342,17 @@ struct segment_vmaddr {
63426342
// are some multiple passes over the image list while calculating
63436343
// everything.
63446344

6345-
static offset_t
6346-
CreateAllImageInfosPayload(const lldb::ProcessSP &process_sp,
6347-
offset_t initial_file_offset,
6348-
StreamString &all_image_infos_payload) {
6345+
static offset_t CreateAllImageInfosPayload(
6346+
const lldb::ProcessSP &process_sp, offset_t initial_file_offset,
6347+
StreamString &all_image_infos_payload, SaveCoreStyle core_style) {
63496348
Target &target = process_sp->GetTarget();
6350-
const ModuleList &modules = target.GetImages();
6351-
size_t modules_count = modules.GetSize();
6349+
ModuleList modules = target.GetImages();
6350+
6351+
// stack-only corefiles have no reason to include binaries that
6352+
// are not executing; we're trying to make the smallest corefile
6353+
// we can, so leave the rest out.
6354+
if (core_style == SaveCoreStyle::eSaveCoreStackOnly)
6355+
modules.Clear();
63526356

63536357
std::set<std::string> executing_uuids;
63546358
ThreadList &thread_list(process_sp->GetThreadList());
@@ -6363,10 +6367,12 @@ CreateAllImageInfosPayload(const lldb::ProcessSP &process_sp,
63636367
UUID uuid = module_sp->GetUUID();
63646368
if (uuid.IsValid()) {
63656369
executing_uuids.insert(uuid.GetAsString());
6370+
modules.AppendIfNeeded(module_sp);
63666371
}
63676372
}
63686373
}
63696374
}
6375+
size_t modules_count = modules.GetSize();
63706376

63716377
struct all_image_infos_header infos;
63726378
infos.version = 1;
@@ -6508,12 +6514,9 @@ bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp,
65086514
if (!process_sp)
65096515
return false;
65106516

6511-
// For Mach-O, we can only create full corefiles or dirty-page-only
6512-
// corefiles. The default is dirty-page-only.
6513-
if (core_style != SaveCoreStyle::eSaveCoreFull) {
6517+
// Default on macOS is to create a dirty-memory-only corefile.
6518+
if (core_style == SaveCoreStyle::eSaveCoreUnspecified) {
65146519
core_style = SaveCoreStyle::eSaveCoreDirtyOnly;
6515-
} else {
6516-
core_style = SaveCoreStyle::eSaveCoreFull;
65176520
}
65186521

65196522
Target &target = process_sp->GetTarget();
@@ -6568,13 +6571,23 @@ bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp,
65686571
if (size == 0)
65696572
break;
65706573

6571-
if (prot != 0) {
6574+
bool include_this_region = true;
6575+
bool dirty_pages_only = false;
6576+
if (core_style == SaveCoreStyle::eSaveCoreStackOnly) {
6577+
dirty_pages_only = true;
6578+
if (range_info.IsStackMemory() != MemoryRegionInfo::eYes) {
6579+
include_this_region = false;
6580+
}
6581+
}
6582+
if (core_style == SaveCoreStyle::eSaveCoreDirtyOnly) {
6583+
dirty_pages_only = true;
6584+
}
6585+
6586+
if (prot != 0 && include_this_region) {
65726587
addr_t pagesize = range_info.GetPageSize();
65736588
const llvm::Optional<std::vector<addr_t>> &dirty_page_list =
65746589
range_info.GetDirtyPageList();
6575-
if (core_style == SaveCoreStyle::eSaveCoreDirtyOnly &&
6576-
dirty_page_list.hasValue()) {
6577-
core_style = SaveCoreStyle::eSaveCoreDirtyOnly;
6590+
if (dirty_pages_only && dirty_page_list.hasValue()) {
65786591
for (addr_t dirtypage : dirty_page_list.getValue()) {
65796592
page_object obj;
65806593
obj.addr = dirtypage;
@@ -6617,6 +6630,12 @@ bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp,
66176630
last_obj = obj;
66186631
}
66196632

6633+
// If we only ended up with one contiguous memory segment
6634+
if (combined_page_objects.size() == 0 &&
6635+
last_obj.addr != LLDB_INVALID_ADDRESS) {
6636+
combined_page_objects.push_back(last_obj);
6637+
}
6638+
66206639
for (page_object obj : combined_page_objects) {
66216640
uint32_t cmd_type = LC_SEGMENT_64;
66226641
uint32_t segment_size = sizeof(llvm::MachO::segment_command_64);
@@ -6767,7 +6786,8 @@ bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp,
67676786
all_image_infos_lcnote_up->name = "all image infos";
67686787
all_image_infos_lcnote_up->payload_file_offset = file_offset;
67696788
file_offset = CreateAllImageInfosPayload(
6770-
process_sp, file_offset, all_image_infos_lcnote_up->payload);
6789+
process_sp, file_offset, all_image_infos_lcnote_up->payload,
6790+
core_style);
67716791
lc_notes.push_back(std::move(all_image_infos_lcnote_up));
67726792

67736793
// Add LC_NOTE load commands

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,6 +1572,19 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo(
15721572
}
15731573
}
15741574
}
1575+
} else if (name.equals("type")) {
1576+
std::string comma_sep_str = value.str();
1577+
size_t comma_pos;
1578+
while ((comma_pos = comma_sep_str.find(',')) != std::string::npos) {
1579+
comma_sep_str[comma_pos] = '\0';
1580+
if (comma_sep_str == "stack") {
1581+
region_info.SetIsStackMemory(MemoryRegionInfo::eYes);
1582+
}
1583+
}
1584+
// handle final (or only) type of "stack"
1585+
if (comma_sep_str == "stack") {
1586+
region_info.SetIsStackMemory(MemoryRegionInfo::eYes);
1587+
}
15751588
} else if (name.equals("error")) {
15761589
StringExtractorGDBRemote error_extractor(value);
15771590
std::string error_string;

lldb/test/API/macosx/skinny-corefile/TestSkinnyCorefile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from lldbsuite.test import lldbutil
1313

1414

15-
class TestFirmwareCorefiles(TestBase):
15+
class TestSkinnyCorefile(TestBase):
1616

1717
mydir = TestBase.compute_mydir(__file__)
1818

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES = main.c
2+
3+
include Makefile.rules
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"""Test that lldb can create a stack-only corefile, and load the main binary."""
2+
3+
import os
4+
import re
5+
import subprocess
6+
7+
import lldb
8+
from lldbsuite.test.decorators import *
9+
from lldbsuite.test.lldbtest import *
10+
from lldbsuite.test import lldbutil
11+
12+
class TestStackCorefile(TestBase):
13+
14+
mydir = TestBase.compute_mydir(__file__)
15+
16+
@no_debug_info_test
17+
@skipUnlessDarwin
18+
def test(self):
19+
20+
corefile = self.getBuildArtifact("process.core")
21+
self.build()
22+
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
23+
self, "// break here", lldb.SBFileSpec("main.c"))
24+
25+
frame = thread.GetFrameAtIndex(0)
26+
stack_int = frame.GetValueForVariablePath("stack_int")
27+
heap_int = frame.GetValueForVariablePath("*heap_int")
28+
stack_str = frame.GetValueForVariablePath("stack_str")
29+
heap_str = frame.GetValueForVariablePath("heap_str")
30+
self.assertEqual(stack_int.GetValueAsUnsigned(), 5)
31+
self.assertEqual(heap_int.GetValueAsUnsigned(), 10)
32+
self.assertEqual(stack_str.summary, '"stack string"')
33+
self.assertEqual(heap_str.summary, '"heap string"')
34+
35+
self.runCmd("process save-core -s stack " + corefile)
36+
process.Kill()
37+
self.dbg.DeleteTarget(target)
38+
39+
# Now load the corefile
40+
target = self.dbg.CreateTarget('')
41+
process = target.LoadCore(corefile)
42+
thread = process.GetSelectedThread()
43+
self.assertTrue(process.IsValid())
44+
self.assertTrue(process.GetSelectedThread().IsValid())
45+
if self.TraceOn():
46+
self.runCmd("image list")
47+
self.runCmd("bt")
48+
self.runCmd("fr v")
49+
num_modules = target.GetNumModules()
50+
# We should only have the a.out binary and possibly
51+
# the libdyld.dylib. Extra libraries loaded means
52+
# extra LC_NOTE and unnecessarily large corefile.
53+
self.assertTrue(num_modules == 1 or num_modules == 2)
54+
55+
# The heap variables should be unavailable now.
56+
frame = thread.GetFrameAtIndex(0)
57+
stack_int = frame.GetValueForVariablePath("stack_int")
58+
heap_int = frame.GetValueForVariablePath("*heap_int")
59+
stack_str = frame.GetValueForVariablePath("stack_str")
60+
heap_str = frame.GetValueForVariablePath("heap_str")
61+
62+
## The heap SBValues both come back as IsValid()==true,
63+
## which I'm not so sure is a great/correct thing --
64+
## it hides the memory read error that actually happened,
65+
## and we don't have a real value.
66+
self.assertEqual(stack_int.GetValueAsUnsigned(), 5)
67+
self.assertEqual(heap_int.GetValueAsUnsigned(), 0)
68+
self.assertEqual(stack_str.summary, '"stack string"')
69+
self.assertEqual(heap_str.summary, '""')
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
int main() {
5+
int stack_int = 5;
6+
int *heap_int = (int*) malloc(sizeof (int));
7+
*heap_int = 10;
8+
9+
char stack_str[80];
10+
strcpy (stack_str, "stack string");
11+
char *heap_str = (char*) malloc(80);
12+
strcpy (heap_str, "heap string");
13+
14+
return stack_int; // break here;
15+
}

lldb/tools/debugserver/source/DNBDefs.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <csignal>
1717
#include <cstdint>
1818
#include <cstdio>
19+
#include <string>
1920
#include <sys/syslimits.h>
2021
#include <unistd.h>
2122
#include <vector>
@@ -318,11 +319,13 @@ struct DNBExecutableImageInfo {
318319

319320
struct DNBRegionInfo {
320321
public:
321-
DNBRegionInfo() : addr(0), size(0), permissions(0), dirty_pages() {}
322+
DNBRegionInfo()
323+
: addr(0), size(0), permissions(0), dirty_pages(), vm_types() {}
322324
nub_addr_t addr;
323325
nub_addr_t size;
324326
uint32_t permissions;
325327
std::vector<nub_addr_t> dirty_pages;
328+
std::vector<std::string> vm_types;
326329
};
327330

328331
enum DNBProfileDataScanType {

lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ nub_bool_t MachVMMemory::GetMemoryRegionInfo(task_t task, nub_addr_t address,
125125
region_info->permissions = vmRegion.GetDNBPermissions();
126126
region_info->dirty_pages =
127127
get_dirty_pages(task, vmRegion.StartAddress(), vmRegion.GetByteSize());
128+
region_info->vm_types = vmRegion.GetMemoryTypes();
128129
} else {
129130
region_info->addr = address;
130131
region_info->size = 0;

lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,43 @@ uint32_t MachVMRegion::GetDNBPermissions() const {
182182
dnb_permissions |= eMemoryPermissionsExecutable;
183183
return dnb_permissions;
184184
}
185+
186+
std::vector<std::string> MachVMRegion::GetMemoryTypes() const {
187+
std::vector<std::string> types;
188+
if (m_data.user_tag == VM_MEMORY_STACK) {
189+
if (m_data.protection == VM_PROT_NONE) {
190+
types.push_back("stack-guard");
191+
} else {
192+
types.push_back("stack");
193+
}
194+
}
195+
if (m_data.user_tag == VM_MEMORY_MALLOC) {
196+
if (m_data.protection == VM_PROT_NONE)
197+
types.push_back("malloc-guard");
198+
else if (m_data.share_mode == SM_EMPTY)
199+
types.push_back("malloc-reserved");
200+
else
201+
types.push_back("malloc-metadata");
202+
}
203+
if (m_data.user_tag == VM_MEMORY_MALLOC_NANO ||
204+
m_data.user_tag == VM_MEMORY_MALLOC_TINY ||
205+
m_data.user_tag == VM_MEMORY_MALLOC_SMALL ||
206+
m_data.user_tag == VM_MEMORY_MALLOC_LARGE ||
207+
m_data.user_tag == VM_MEMORY_MALLOC_LARGE_REUSED ||
208+
m_data.user_tag == VM_MEMORY_MALLOC_LARGE_REUSABLE ||
209+
m_data.user_tag == VM_MEMORY_MALLOC_HUGE ||
210+
m_data.user_tag == VM_MEMORY_REALLOC ||
211+
m_data.user_tag == VM_MEMORY_SBRK) {
212+
types.push_back("heap");
213+
if (m_data.user_tag == VM_MEMORY_MALLOC_TINY) {
214+
types.push_back("malloc-tiny");
215+
}
216+
if (m_data.user_tag == VM_MEMORY_MALLOC_LARGE) {
217+
types.push_back("malloc-large");
218+
}
219+
if (m_data.user_tag == VM_MEMORY_MALLOC_SMALL) {
220+
types.push_back("malloc-small");
221+
}
222+
}
223+
return types;
224+
}

lldb/tools/debugserver/source/MacOSX/MachVMRegion.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class MachVMRegion {
4040
vm_prot_t prot);
4141
bool RestoreProtections();
4242
bool GetRegionForAddress(nub_addr_t addr);
43+
std::vector<std::string> GetMemoryTypes() const;
4344

4445
uint32_t GetDNBPermissions() const;
4546

0 commit comments

Comments
 (0)