Skip to content

Commit 3e4af61

Browse files
authored
[LLDB][SBSaveCore] Implement a selectable threadlist for Core Options. (#100443)
In #98403 I enabled the SBSaveCoreOptions object, which allows users via the scripting API to define what they want saved into their core file. As the first option I've added a threadlist, so users can scan and identify which threads and corresponding stacks they want to save. In order to support this, I had to add a new method to `Process.h` on how we identify which threads are to be saved, and I had to change the book keeping in minidump to ensure we don't double save the stacks. Important to @jasonmolenda I also changed the MachO coredump to accept these new APIs.
1 parent 5edb493 commit 3e4af61

File tree

18 files changed

+395
-65
lines changed

18 files changed

+395
-65
lines changed

lldb/include/lldb/API/SBProcess.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,7 @@ class LLDB_API SBProcess {
586586
friend class SBBreakpointCallbackBaton;
587587
friend class SBBreakpointLocation;
588588
friend class SBCommandInterpreter;
589+
friend class SBSaveCoreOptions;
589590
friend class SBDebugger;
590591
friend class SBExecutionContext;
591592
friend class SBFunction;

lldb/include/lldb/API/SBSaveCoreOptions.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
#define LLDB_API_SBSAVECOREOPTIONS_H
1111

1212
#include "lldb/API/SBDefines.h"
13+
#include "lldb/API/SBError.h"
14+
#include "lldb/API/SBFileSpec.h"
15+
#include "lldb/API/SBProcess.h"
16+
#include "lldb/API/SBThread.h"
1317

1418
namespace lldb {
1519

@@ -53,6 +57,29 @@ class LLDB_API SBSaveCoreOptions {
5357
/// \return The output file spec.
5458
SBFileSpec GetOutputFile() const;
5559

60+
/// Set the process to save, or unset if supplied with a default constructed
61+
/// process.
62+
///
63+
/// \param process The process to save.
64+
/// \return Success if process was set, otherwise an error
65+
/// \note This will clear all process specific options if a different process
66+
/// is specified than the current set process, either explicitly from this
67+
/// api, or implicitly from any function that requires a process.
68+
SBError SetProcess(lldb::SBProcess process);
69+
70+
/// Add a thread to save in the core file.
71+
///
72+
/// \param thread The thread to save.
73+
/// \note This will set the process if it is not already set, or return
74+
/// and error if the SBThread is not from the set process.
75+
SBError AddThread(lldb::SBThread thread);
76+
77+
/// Remove a thread from the list of threads to save.
78+
///
79+
/// \param thread The thread to remove.
80+
/// \return True if the thread was removed, false if it was not in the list.
81+
bool RemoveThread(lldb::SBThread thread);
82+
5683
/// Reset all options.
5784
void Clear();
5885

lldb/include/lldb/API/SBThread.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ class LLDB_API SBThread {
233233
friend class SBBreakpoint;
234234
friend class SBBreakpointLocation;
235235
friend class SBBreakpointCallbackBaton;
236+
friend class SBSaveCoreOptions;
236237
friend class SBExecutionContext;
237238
friend class SBFrame;
238239
friend class SBProcess;
@@ -253,6 +254,8 @@ class LLDB_API SBThread {
253254
SBError ResumeNewPlan(lldb_private::ExecutionContext &exe_ctx,
254255
lldb_private::ThreadPlan *new_plan);
255256

257+
lldb::ThreadSP GetSP() const;
258+
256259
lldb::ExecutionContextRefSP m_opaque_sp;
257260

258261
lldb_private::Thread *operator->();

lldb/include/lldb/Symbol/SaveCoreOptions.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include <optional>
1717
#include <string>
18+
#include <unordered_set>
1819

1920
namespace lldb_private {
2021

@@ -32,13 +33,25 @@ class SaveCoreOptions {
3233
void SetOutputFile(lldb_private::FileSpec file);
3334
const std::optional<lldb_private::FileSpec> GetOutputFile() const;
3435

36+
Status SetProcess(lldb::ProcessSP process_sp);
37+
38+
Status AddThread(lldb::ThreadSP thread_sp);
39+
bool RemoveThread(lldb::ThreadSP thread_sp);
40+
bool ShouldThreadBeSaved(lldb::tid_t tid) const;
41+
42+
Status EnsureValidConfiguration(lldb::ProcessSP process_sp) const;
43+
3544
void Clear();
3645

3746
private:
47+
void ClearProcessSpecificData();
48+
3849
std::optional<std::string> m_plugin_name;
3950
std::optional<lldb_private::FileSpec> m_file;
4051
std::optional<lldb::SaveCoreStyle> m_style;
52+
lldb::ProcessSP m_process_sp;
53+
std::unordered_set<lldb::tid_t> m_threads_to_save;
4154
};
4255
} // namespace lldb_private
4356

44-
#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_SaveCoreOPTIONS_H
57+
#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_SAVECOREOPTIONS_H

lldb/include/lldb/Target/Process.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,9 +738,15 @@ class Process : public std::enable_shared_from_this<Process>,
738738
/// Helper function for Process::SaveCore(...) that calculates the address
739739
/// ranges that should be saved. This allows all core file plug-ins to save
740740
/// consistent memory ranges given a \a core_style.
741-
Status CalculateCoreFileSaveRanges(lldb::SaveCoreStyle core_style,
741+
Status CalculateCoreFileSaveRanges(const SaveCoreOptions &core_options,
742742
CoreFileMemoryRanges &ranges);
743743

744+
/// Helper function for Process::SaveCore(...) that calculates the thread list
745+
/// based upon options set within a given \a core_options object.
746+
/// \note If there is no thread list defined, all threads will be saved.
747+
std::vector<lldb::ThreadSP>
748+
CalculateCoreFileThreadList(const SaveCoreOptions &core_options);
749+
744750
protected:
745751
virtual JITLoaderList &GetJITLoaders();
746752

lldb/source/API/SBSaveCoreOptions.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "lldb/API/SBSaveCoreOptions.h"
10-
#include "lldb/API/SBError.h"
11-
#include "lldb/API/SBFileSpec.h"
1210
#include "lldb/Host/FileSystem.h"
1311
#include "lldb/Symbol/SaveCoreOptions.h"
1412
#include "lldb/Utility/Instrumentation.h"
@@ -75,6 +73,21 @@ lldb::SaveCoreStyle SBSaveCoreOptions::GetStyle() const {
7573
return m_opaque_up->GetStyle();
7674
}
7775

76+
SBError SBSaveCoreOptions::SetProcess(lldb::SBProcess process) {
77+
LLDB_INSTRUMENT_VA(this, process);
78+
return m_opaque_up->SetProcess(process.GetSP());
79+
}
80+
81+
SBError SBSaveCoreOptions::AddThread(lldb::SBThread thread) {
82+
LLDB_INSTRUMENT_VA(this, thread);
83+
return m_opaque_up->AddThread(thread.GetSP());
84+
}
85+
86+
bool SBSaveCoreOptions::RemoveThread(lldb::SBThread thread) {
87+
LLDB_INSTRUMENT_VA(this, thread);
88+
return m_opaque_up->RemoveThread(thread.GetSP());
89+
}
90+
7891
void SBSaveCoreOptions::Clear() {
7992
LLDB_INSTRUMENT_VA(this);
8093
m_opaque_up->Clear();

lldb/source/API/SBThread.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,6 +1331,8 @@ bool SBThread::SafeToCallFunctions() {
13311331
return true;
13321332
}
13331333

1334+
lldb::ThreadSP SBThread::GetSP() const { return m_opaque_sp->GetThreadSP(); }
1335+
13341336
lldb_private::Thread *SBThread::operator->() {
13351337
return get();
13361338
}

lldb/source/Core/PluginManager.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,10 @@ Status PluginManager::SaveCore(const lldb::ProcessSP &process_sp,
714714
return error;
715715
}
716716

717+
error = options.EnsureValidConfiguration(process_sp);
718+
if (error.Fail())
719+
return error;
720+
717721
if (!options.GetPluginName().has_value()) {
718722
// Try saving core directly from the process plugin first.
719723
llvm::Expected<bool> ret =

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

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6347,22 +6347,24 @@ struct segment_vmaddr {
63476347
// are some multiple passes over the image list while calculating
63486348
// everything.
63496349

6350-
static offset_t CreateAllImageInfosPayload(
6351-
const lldb::ProcessSP &process_sp, offset_t initial_file_offset,
6352-
StreamString &all_image_infos_payload, SaveCoreStyle core_style) {
6350+
static offset_t
6351+
CreateAllImageInfosPayload(const lldb::ProcessSP &process_sp,
6352+
offset_t initial_file_offset,
6353+
StreamString &all_image_infos_payload,
6354+
const lldb_private::SaveCoreOptions &options) {
63536355
Target &target = process_sp->GetTarget();
63546356
ModuleList modules = target.GetImages();
63556357

63566358
// stack-only corefiles have no reason to include binaries that
63576359
// are not executing; we're trying to make the smallest corefile
63586360
// we can, so leave the rest out.
6359-
if (core_style == SaveCoreStyle::eSaveCoreStackOnly)
6361+
if (options.GetStyle() == SaveCoreStyle::eSaveCoreStackOnly)
63606362
modules.Clear();
63616363

63626364
std::set<std::string> executing_uuids;
6363-
ThreadList &thread_list(process_sp->GetThreadList());
6364-
for (uint32_t i = 0; i < thread_list.GetSize(); i++) {
6365-
ThreadSP thread_sp = thread_list.GetThreadAtIndex(i);
6365+
std::vector<ThreadSP> thread_list =
6366+
process_sp->CalculateCoreFileThreadList(options);
6367+
for (const ThreadSP &thread_sp : thread_list) {
63666368
uint32_t stack_frame_count = thread_sp->GetStackFrameCount();
63676369
for (uint32_t j = 0; j < stack_frame_count; j++) {
63686370
StackFrameSP stack_frame_sp = thread_sp->GetStackFrameAtIndex(j);
@@ -6559,7 +6561,7 @@ bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp,
65596561

65606562
if (make_core) {
65616563
Process::CoreFileMemoryRanges core_ranges;
6562-
error = process_sp->CalculateCoreFileSaveRanges(core_style, core_ranges);
6564+
error = process_sp->CalculateCoreFileSaveRanges(options, core_ranges);
65636565
if (error.Success()) {
65646566
const uint32_t addr_byte_size = target_arch.GetAddressByteSize();
65656567
const ByteOrder byte_order = target_arch.GetByteOrder();
@@ -6730,8 +6732,8 @@ bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp,
67306732
std::make_shared<StructuredData::Dictionary>());
67316733
StructuredData::ArraySP threads(
67326734
std::make_shared<StructuredData::Array>());
6733-
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
6734-
ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx));
6735+
for (const ThreadSP &thread_sp :
6736+
process_sp->CalculateCoreFileThreadList(options)) {
67356737
StructuredData::DictionarySP thread(
67366738
std::make_shared<StructuredData::Dictionary>());
67376739
thread->AddIntegerItem("thread_id", thread_sp->GetID());
@@ -6754,7 +6756,7 @@ bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp,
67546756
all_image_infos_lcnote_up->payload_file_offset = file_offset;
67556757
file_offset = CreateAllImageInfosPayload(
67566758
process_sp, file_offset, all_image_infos_lcnote_up->payload,
6757-
core_style);
6759+
options);
67586760
lc_notes.push_back(std::move(all_image_infos_lcnote_up));
67596761

67606762
// Add LC_NOTE load commands

lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,9 @@ Status MinidumpFileBuilder::AddHeaderAndCalculateDirectories() {
6969
m_expected_directories += 9;
7070

7171
// Go through all of the threads and check for exceptions.
72-
lldb_private::ThreadList thread_list = m_process_sp->GetThreadList();
73-
const uint32_t num_threads = thread_list.GetSize();
74-
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
75-
ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx));
72+
std::vector<lldb::ThreadSP> threads =
73+
m_process_sp->CalculateCoreFileThreadList(m_save_core_options);
74+
for (const ThreadSP &thread_sp : threads) {
7675
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
7776
if (stop_info_sp) {
7877
const StopReason &stop_reason = stop_info_sp->GetStopReason();
@@ -588,12 +587,13 @@ Status MinidumpFileBuilder::FixThreadStacks() {
588587

589588
Status MinidumpFileBuilder::AddThreadList() {
590589
constexpr size_t minidump_thread_size = sizeof(llvm::minidump::Thread);
591-
lldb_private::ThreadList thread_list = m_process_sp->GetThreadList();
590+
std::vector<ThreadSP> thread_list =
591+
m_process_sp->CalculateCoreFileThreadList(m_save_core_options);
592592

593593
// size of the entire thread stream consists of:
594594
// number of threads and threads array
595595
size_t thread_stream_size = sizeof(llvm::support::ulittle32_t) +
596-
thread_list.GetSize() * minidump_thread_size;
596+
thread_list.size() * minidump_thread_size;
597597
// save for the ability to set up RVA
598598
size_t size_before = GetCurrentDataEndOffset();
599599
Status error;
@@ -602,17 +602,15 @@ Status MinidumpFileBuilder::AddThreadList() {
602602
return error;
603603

604604
llvm::support::ulittle32_t thread_count =
605-
static_cast<llvm::support::ulittle32_t>(thread_list.GetSize());
605+
static_cast<llvm::support::ulittle32_t>(thread_list.size());
606606
m_data.AppendData(&thread_count, sizeof(llvm::support::ulittle32_t));
607607

608608
// Take the offset after the thread count.
609609
m_thread_list_start = GetCurrentDataEndOffset();
610610
DataBufferHeap helper_data;
611611

612-
const uint32_t num_threads = thread_list.GetSize();
613612
Log *log = GetLog(LLDBLog::Object);
614-
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
615-
ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx));
613+
for (const ThreadSP &thread_sp : thread_list) {
616614
RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
617615

618616
if (!reg_ctx_sp) {
@@ -650,7 +648,7 @@ Status MinidumpFileBuilder::AddThreadList() {
650648
m_tid_to_reg_ctx[thread_sp->GetID()] = thread_context_memory_locator;
651649

652650
LLDB_LOGF(log, "AddThreadList for thread %d: thread_context %zu bytes",
653-
thread_idx, thread_context.size());
651+
thread_sp->GetIndexID(), thread_context.size());
654652
helper_data.AppendData(thread_context.data(), thread_context.size());
655653

656654
llvm::minidump::Thread t;
@@ -674,11 +672,10 @@ Status MinidumpFileBuilder::AddThreadList() {
674672
}
675673

676674
Status MinidumpFileBuilder::AddExceptions() {
677-
lldb_private::ThreadList thread_list = m_process_sp->GetThreadList();
675+
std::vector<ThreadSP> thread_list =
676+
m_process_sp->CalculateCoreFileThreadList(m_save_core_options);
678677
Status error;
679-
const uint32_t num_threads = thread_list.GetSize();
680-
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
681-
ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx));
678+
for (const ThreadSP &thread_sp : thread_list) {
682679
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
683680
bool add_exception = false;
684681
if (stop_info_sp) {
@@ -819,7 +816,7 @@ Status MinidumpFileBuilder::AddLinuxFileStreams() {
819816
return error;
820817
}
821818

822-
Status MinidumpFileBuilder::AddMemoryList(SaveCoreStyle core_style) {
819+
Status MinidumpFileBuilder::AddMemoryList() {
823820
Status error;
824821

825822
// We first save the thread stacks to ensure they fit in the first UINT32_MAX
@@ -828,18 +825,26 @@ Status MinidumpFileBuilder::AddMemoryList(SaveCoreStyle core_style) {
828825
// in accessible with a 32 bit offset.
829826
Process::CoreFileMemoryRanges ranges_32;
830827
Process::CoreFileMemoryRanges ranges_64;
831-
error = m_process_sp->CalculateCoreFileSaveRanges(
832-
SaveCoreStyle::eSaveCoreStackOnly, ranges_32);
828+
Process::CoreFileMemoryRanges all_core_memory_ranges;
829+
error = m_process_sp->CalculateCoreFileSaveRanges(m_save_core_options,
830+
all_core_memory_ranges);
833831
if (error.Fail())
834832
return error;
835833

836-
// Calculate totalsize including the current offset.
834+
// Start by saving all of the stacks and ensuring they fit under the 32b
835+
// limit.
837836
uint64_t total_size = GetCurrentDataEndOffset();
838-
total_size += ranges_32.size() * sizeof(llvm::minidump::MemoryDescriptor);
839-
std::unordered_set<addr_t> stack_start_addresses;
840-
for (const auto &core_range : ranges_32) {
841-
stack_start_addresses.insert(core_range.range.start());
842-
total_size += core_range.range.size();
837+
auto iterator = all_core_memory_ranges.begin();
838+
while (iterator != all_core_memory_ranges.end()) {
839+
if (m_saved_stack_ranges.count(iterator->range.start()) > 0) {
840+
// We don't save stacks twice.
841+
ranges_32.push_back(*iterator);
842+
total_size +=
843+
iterator->range.size() + sizeof(llvm::minidump::MemoryDescriptor);
844+
iterator = all_core_memory_ranges.erase(iterator);
845+
} else {
846+
iterator++;
847+
}
843848
}
844849

845850
if (total_size >= UINT32_MAX) {
@@ -849,31 +854,20 @@ Status MinidumpFileBuilder::AddMemoryList(SaveCoreStyle core_style) {
849854
return error;
850855
}
851856

852-
Process::CoreFileMemoryRanges all_core_memory_ranges;
853-
if (core_style != SaveCoreStyle::eSaveCoreStackOnly) {
854-
error = m_process_sp->CalculateCoreFileSaveRanges(core_style,
855-
all_core_memory_ranges);
856-
if (error.Fail())
857-
return error;
858-
}
859-
860857
// After saving the stacks, we start packing as much as we can into 32b.
861858
// We apply a generous padding here so that the Directory, MemoryList and
862859
// Memory64List sections all begin in 32b addressable space.
863860
// Then anything overflow extends into 64b addressable space.
864861
// All core memeroy ranges will either container nothing on stacks only
865862
// or all the memory ranges including stacks
866863
if (!all_core_memory_ranges.empty())
867-
total_size +=
868-
256 + (all_core_memory_ranges.size() - stack_start_addresses.size()) *
869-
sizeof(llvm::minidump::MemoryDescriptor_64);
864+
total_size += 256 + (all_core_memory_ranges.size() *
865+
sizeof(llvm::minidump::MemoryDescriptor_64));
870866

871867
for (const auto &core_range : all_core_memory_ranges) {
872868
const addr_t range_size = core_range.range.size();
873-
if (stack_start_addresses.count(core_range.range.start()) > 0)
874-
// Don't double save stacks.
875-
continue;
876-
869+
// We don't need to check for stacks here because we already removed them
870+
// from all_core_memory_ranges.
877871
if (total_size + range_size < UINT32_MAX) {
878872
ranges_32.push_back(core_range);
879873
total_size += range_size;

0 commit comments

Comments
 (0)