Skip to content

Commit 9ea6dd5

Browse files
committed
Add a corefile style option to process save-core; skinny corefiles
Add a new feature to process save-core on Darwin systems -- for lldb to create a user process corefile with only the dirty (modified memory) pages included. All of the binaries that were used in the corefile are assumed to still exist on the system for the duration of the use of the corefile. A new --style option to process save-core is added, so a full corefile can be requested if portability across systems, or across time, is needed for this corefile. debugserver can now identify the dirty pages in a memory region when queried with qMemoryRegionInfo, and the size of vm pages is given in qHostInfo. Create a new "all image infos" LC_NOTE for Mach-O which allows us to describe all of the binaries that were loaded in the process -- load address, UUID, file path, segment load addresses, and optionally whether code from the binary was executing on any thread. The old "read dyld_all_image_infos and then the in-memory Mach-O load commands to get segment load addresses" no longer works when we only have dirty memory. rdar://69670807 Differential Revision: https://reviews.llvm.org/D88387
1 parent 1ae266f commit 9ea6dd5

36 files changed

+1217
-65
lines changed

lldb/bindings/interface/SBMemoryRegionInfo.i

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,42 @@ public:
4646
const char *
4747
GetName ();
4848

49+
%feature("autodoc", "
50+
GetRegionEnd(SBMemoryRegionInfo self) -> lldb::addr_t
51+
Returns whether this memory region has a list of modified (dirty)
52+
pages available or not. When calling GetNumDirtyPages(), you will
53+
have 0 returned for both \"dirty page list is not known\" and
54+
\"empty dirty page list\" (that is, no modified pages in this
55+
memory region). You must use this method to disambiguate.") HasDirtyMemoryPageList;
56+
bool
57+
HasDirtyMemoryPageList();
58+
59+
%feature("autodoc", "
60+
GetNumDirtyPages(SBMemoryRegionInfo self) -> uint32_t
61+
Return the number of dirty (modified) memory pages in this
62+
memory region, if available. You must use the
63+
SBMemoryRegionInfo::HasDirtyMemoryPageList() method to
64+
determine if a dirty memory list is available; it will depend
65+
on the target system can provide this information.") GetNumDirtyPages;
66+
uint32_t
67+
GetNumDirtyPages();
68+
69+
%feature("autodoc", "
70+
GetDirtyPageAddressAtIndex(SBMemoryRegionInfo self, uint32_t idx) -> lldb::addr_t
71+
Return the address of a modified, or dirty, page of memory.
72+
If the provided index is out of range, or this memory region
73+
does not have dirty page information, LLDB_INVALID_ADDRESS
74+
is returned.") GetDirtyPageAddressAtIndex;
75+
addr_t
76+
GetDirtyPageAddressAtIndex(uint32_t idx);
77+
78+
%feature("autodoc", "
79+
GetPageSize(SBMemoryRegionInfo self) -> int
80+
Return the size of pages in this memory region. 0 will be returned
81+
if this information was unavailable.") GetPageSize();
82+
int
83+
GetPageSize();
84+
4985
bool
5086
operator == (const lldb::SBMemoryRegionInfo &rhs) const;
5187

lldb/docs/lldb-gdb-remote.txt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -869,10 +869,14 @@ distribution_id: optional. For linux, specifies distribution id (e.g. ubuntu, fe
869869
osmajor: optional, specifies the major version number of the OS (e.g. for macOS 10.12.2, it would be 10)
870870
osminor: optional, specifies the minor version number of the OS (e.g. for macOS 10.12.2, it would be 12)
871871
ospatch: optional, specifies the patch level number of the OS (e.g. for macOS 10.12.2, it would be 2)
872+
vm-page-size: optional, specifies the target system VM page size, base 10.
873+
Needed for the "dirty-pages:" list in the qMemoryRegionInfo
874+
packet, where a list of dirty pages is sent from the remote
875+
stub. This page size tells lldb how large each dirty page is.
872876
addressing_bits: optional, specifies how many bits in addresses are
873877
significant for addressing, base 10. If bits 38..0
874878
in a 64-bit pointer are significant for addressing,
875-
then the value is 39. This is needed on e.g. Aarch64
879+
then the value is 39. This is needed on e.g. AArch64
876880
v8.3 ABIs that use pointer authentication, so lldb
877881
knows which bits to clear/set to get the actual
878882
addresses.
@@ -1174,6 +1178,18 @@ tuples to return are:
11741178
// a hex encoded string value that
11751179
// contains an error string
11761180

1181+
dirty-pages:[<hexaddr>][,<hexaddr]; // A list of memory pages within this
1182+
// region that are "dirty" -- they have been modified.
1183+
// Page addresses are in base16. The size of a page can
1184+
// be found from the qHostInfo's page-size key-value.
1185+
//
1186+
// If the stub supports identifying dirty pages within a
1187+
// memory region, this key should always be present for all
1188+
// qMemoryRegionInfo replies. This key with no pages
1189+
// listed ("dirty-pages:;") indicates no dirty pages in
1190+
// this memory region. The *absence* of this key means
1191+
// that this stub cannot determine dirty pages.
1192+
11771193
If the address requested is not in a mapped region (e.g. we've jumped through
11781194
a NULL pointer and are at 0x0) currently lldb expects to get back the size
11791195
of the unmapped region -- that is, the distance to the next valid region.

lldb/include/lldb/API/SBMemoryRegionInfo.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,40 @@ class LLDB_API SBMemoryRegionInfo {
7373
/// region. If no name can be determined the returns nullptr.
7474
const char *GetName();
7575

76+
/// Returns whether this memory region has a list of memory pages
77+
/// that have been modified -- that are dirty.
78+
///
79+
/// \return
80+
/// True if the dirty page list is available.
81+
bool HasDirtyMemoryPageList();
82+
83+
/// Returns the number of modified pages -- dirty pages -- in this
84+
/// memory region.
85+
///
86+
/// \return
87+
/// The number of dirty page entries will be returned. If
88+
/// there are no dirty pages in this memory region, 0 will
89+
/// be returned. 0 will also be returned if the dirty page
90+
/// list is not available for this memory region -- you must
91+
/// use HasDirtyMemoryPageList() to check for that.
92+
uint32_t GetNumDirtyPages();
93+
94+
/// Returns the address of a memory page that has been modified in
95+
/// this region.
96+
///
97+
/// \return
98+
/// Returns the address for his dirty page in the list.
99+
/// If this memory region does not have a dirty page list,
100+
/// LLDB_INVALID_ADDRESS is returned.
101+
addr_t GetDirtyPageAddressAtIndex(uint32_t idx);
102+
103+
/// Returns the size of a memory page in this region.
104+
///
105+
/// \return
106+
/// Returns the size of the memory pages in this region,
107+
/// or 0 if this information is unavailable.
108+
int GetPageSize();
109+
76110
bool operator==(const lldb::SBMemoryRegionInfo &rhs) const;
77111

78112
bool operator!=(const lldb::SBMemoryRegionInfo &rhs) const;

lldb/include/lldb/Core/PluginManager.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ class PluginManager {
191191
GetObjectFileCreateMemoryCallbackForPluginName(ConstString name);
192192

193193
static Status SaveCore(const lldb::ProcessSP &process_sp,
194-
const FileSpec &outfile);
194+
const FileSpec &outfile,
195+
lldb::SaveCoreStyle &core_style);
195196

196197
// ObjectContainer
197198
static bool

lldb/include/lldb/Symbol/ObjectFile.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,22 @@ class ObjectFile : public std::enable_shared_from_this<ObjectFile>,
666666
/// Creates a plugin-specific call frame info
667667
virtual std::unique_ptr<CallFrameInfo> CreateCallFrameInfo();
668668

669+
/// Load binaries listed in a corefile
670+
///
671+
/// A corefile may have metadata listing binaries that can be loaded,
672+
/// and the offsets at which they were loaded. This method will try
673+
/// to add them to the Target. If any binaries were loaded,
674+
///
675+
/// \param[in] process
676+
/// Process where to load binaries.
677+
///
678+
/// \return
679+
/// Returns true if any binaries were loaded.
680+
681+
virtual bool LoadCoreFileImages(lldb_private::Process &process) {
682+
return false;
683+
}
684+
669685
protected:
670686
// Member variables.
671687
FileSpec m_file;

lldb/include/lldb/Target/MemoryRegionInfo.h

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010
#ifndef LLDB_TARGET_MEMORYREGIONINFO_H
1111
#define LLDB_TARGET_MEMORYREGIONINFO_H
1212

13+
#include <vector>
14+
1315
#include "lldb/Utility/ConstString.h"
1416
#include "lldb/Utility/RangeMap.h"
17+
#include "llvm/ADT/Optional.h"
1518
#include "llvm/Support/FormatProviders.h"
1619

1720
namespace lldb_private {
@@ -32,10 +35,7 @@ class MemoryRegionInfo {
3235

3336
RangeType &GetRange() { return m_range; }
3437

35-
void Clear() {
36-
m_range.Clear();
37-
m_read = m_write = m_execute = m_memory_tagged = eDontKnow;
38-
}
38+
void Clear() { *this = MemoryRegionInfo(); }
3939

4040
const RangeType &GetRange() const { return m_range; }
4141

@@ -97,11 +97,33 @@ class MemoryRegionInfo {
9797
m_write == rhs.m_write && m_execute == rhs.m_execute &&
9898
m_mapped == rhs.m_mapped && m_name == rhs.m_name &&
9999
m_flash == rhs.m_flash && m_blocksize == rhs.m_blocksize &&
100-
m_memory_tagged == rhs.m_memory_tagged;
100+
m_memory_tagged == rhs.m_memory_tagged &&
101+
m_pagesize == rhs.m_pagesize;
101102
}
102103

103104
bool operator!=(const MemoryRegionInfo &rhs) const { return !(*this == rhs); }
104105

106+
/// Get the target system's VM page size in bytes.
107+
/// \return
108+
/// 0 is returned if this information is unavailable.
109+
int GetPageSize() { return m_pagesize; }
110+
111+
/// Get a vector of target VM pages that are dirty -- that have been
112+
/// modified -- within this memory region. This is an Optional return
113+
/// value; it will only be available if the remote stub was able to
114+
/// detail this.
115+
llvm::Optional<std::vector<lldb::addr_t>> &GetDirtyPageList() {
116+
return m_dirty_pages;
117+
}
118+
119+
void SetPageSize(int pagesize) { m_pagesize = pagesize; }
120+
121+
void SetDirtyPageList(std::vector<lldb::addr_t> pagelist) {
122+
if (m_dirty_pages.hasValue())
123+
m_dirty_pages.getValue().clear();
124+
m_dirty_pages = std::move(pagelist);
125+
}
126+
105127
protected:
106128
RangeType m_range;
107129
OptionalBool m_read = eDontKnow;
@@ -112,6 +134,8 @@ class MemoryRegionInfo {
112134
OptionalBool m_flash = eDontKnow;
113135
lldb::offset_t m_blocksize = 0;
114136
OptionalBool m_memory_tagged = eDontKnow;
137+
int m_pagesize = 0;
138+
llvm::Optional<std::vector<lldb::addr_t>> m_dirty_pages;
115139
};
116140

117141
inline bool operator<(const MemoryRegionInfo &lhs,

lldb/include/lldb/lldb-enumerations.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,7 @@ enum CommandArgumentType {
601601
eArgTypeCommand,
602602
eArgTypeColumnNum,
603603
eArgTypeModuleUUID,
604+
eArgTypeSaveCoreStyle,
604605
eArgTypeLastArg // Always keep this entry as the last entry in this
605606
// enumeration!!
606607
};
@@ -1111,6 +1112,14 @@ enum CommandInterpreterResult {
11111112
/// Stopped because quit was requested.
11121113
eCommandInterpreterResultQuitRequested,
11131114
};
1115+
1116+
// Style of core file to create when calling SaveCore.
1117+
enum SaveCoreStyle {
1118+
eSaveCoreUnspecified = 0,
1119+
eSaveCoreFull = 1,
1120+
eSaveCoreDirtyOnly = 2,
1121+
};
1122+
11141123
} // namespace lldb
11151124

11161125
#endif // LLDB_LLDB_ENUMERATIONS_H

lldb/include/lldb/lldb-private-interfaces.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ typedef ObjectFile *(*ObjectFileCreateMemoryInstance)(
5454
const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
5555
const lldb::ProcessSP &process_sp, lldb::addr_t offset);
5656
typedef bool (*ObjectFileSaveCore)(const lldb::ProcessSP &process_sp,
57-
const FileSpec &outfile, Status &error);
57+
const FileSpec &outfile,
58+
lldb::SaveCoreStyle &core_style,
59+
Status &error);
5860
typedef EmulateInstruction *(*EmulateInstructionCreateInstance)(
5961
const ArchSpec &arch, InstructionType inst_type);
6062
typedef OperatingSystem *(*OperatingSystemCreateInstance)(Process *process,

lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -723,7 +723,8 @@ def parse_memory_region_packet(self, context):
723723
"permissions",
724724
"flags",
725725
"name",
726-
"error"])
726+
"error",
727+
"dirty-pages"])
727728
self.assertIsNotNone(val)
728729

729730
mem_region_dict["name"] = seven.unhexlify(mem_region_dict.get("name", ""))

lldb/source/API/SBMemoryRegionInfo.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,42 @@ const char *SBMemoryRegionInfo::GetName() {
116116
return m_opaque_up->GetName().AsCString();
117117
}
118118

119+
bool SBMemoryRegionInfo::HasDirtyMemoryPageList() {
120+
LLDB_RECORD_METHOD_NO_ARGS(bool, SBMemoryRegionInfo, HasDirtyMemoryPageList);
121+
122+
return m_opaque_up->GetDirtyPageList().hasValue();
123+
}
124+
125+
uint32_t SBMemoryRegionInfo::GetNumDirtyPages() {
126+
LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBMemoryRegionInfo, GetNumDirtyPages);
127+
128+
uint32_t num_dirty_pages = 0;
129+
llvm::Optional<std::vector<addr_t>> dirty_page_list =
130+
m_opaque_up->GetDirtyPageList();
131+
if (dirty_page_list.hasValue())
132+
num_dirty_pages = dirty_page_list.getValue().size();
133+
134+
return num_dirty_pages;
135+
}
136+
137+
addr_t SBMemoryRegionInfo::GetDirtyPageAddressAtIndex(uint32_t idx) {
138+
LLDB_RECORD_METHOD(addr_t, SBMemoryRegionInfo, GetDirtyPageAddressAtIndex,
139+
(uint32_t), idx);
140+
141+
addr_t dirty_page_addr = LLDB_INVALID_ADDRESS;
142+
const llvm::Optional<std::vector<addr_t>> &dirty_page_list =
143+
m_opaque_up->GetDirtyPageList();
144+
if (dirty_page_list.hasValue() && idx < dirty_page_list.getValue().size())
145+
dirty_page_addr = dirty_page_list.getValue()[idx];
146+
147+
return dirty_page_addr;
148+
}
149+
150+
int SBMemoryRegionInfo::GetPageSize() {
151+
LLDB_RECORD_METHOD_NO_ARGS(int, SBMemoryRegionInfo, GetPageSize);
152+
return m_opaque_up->GetPageSize();
153+
}
154+
119155
bool SBMemoryRegionInfo::GetDescription(SBStream &description) {
120156
LLDB_RECORD_METHOD(bool, SBMemoryRegionInfo, GetDescription,
121157
(lldb::SBStream &), description);

lldb/source/API/SBProcess.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1227,7 +1227,8 @@ lldb::SBError SBProcess::SaveCore(const char *file_name) {
12271227
}
12281228

12291229
FileSpec core_file(file_name);
1230-
error.ref() = PluginManager::SaveCore(process_sp, core_file);
1230+
SaveCoreStyle core_style = SaveCoreStyle::eSaveCoreFull;
1231+
error.ref() = PluginManager::SaveCore(process_sp, core_file, core_style);
12311232
return LLDB_RECORD_RESULT(error);
12321233
}
12331234

lldb/source/Commands/CommandObjectMemory.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1678,6 +1678,28 @@ class CommandObjectMemoryRegion : public CommandObjectParsed {
16781678
if (memory_tagged == MemoryRegionInfo::OptionalBool::eYes)
16791679
result.AppendMessage("memory tagging: enabled");
16801680

1681+
const llvm::Optional<std::vector<addr_t>> &dirty_page_list =
1682+
range_info.GetDirtyPageList();
1683+
if (dirty_page_list.hasValue()) {
1684+
const size_t page_count = dirty_page_list.getValue().size();
1685+
result.AppendMessageWithFormat(
1686+
"Modified memory (dirty) page list provided, %zu entries.\n",
1687+
page_count);
1688+
if (page_count > 0) {
1689+
bool print_comma = false;
1690+
result.AppendMessageWithFormat("Dirty pages: ");
1691+
for (size_t i = 0; i < page_count; i++) {
1692+
if (print_comma)
1693+
result.AppendMessageWithFormat(", ");
1694+
else
1695+
print_comma = true;
1696+
result.AppendMessageWithFormat("0x%" PRIx64,
1697+
dirty_page_list.getValue()[i]);
1698+
}
1699+
result.AppendMessageWithFormat(".\n");
1700+
}
1701+
}
1702+
16811703
m_prev_end_addr = range_info.GetRange().GetRangeEnd();
16821704
result.SetStatus(eReturnStatusSuccessFinishResult);
16831705
return true;

0 commit comments

Comments
 (0)