[Lldb-commits] [lldb] [lldb] Fix data race in statusline format handling (PR #142489)
Jonas Devlieghere via lldb-commits
lldb-commits at lists.llvm.org
Tue Jun 3 13:07:20 PDT 2025
https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/142489
>From 6076f7778f3f10d7360d8f0b156992809de73094 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Tue, 3 Jun 2025 10:44:43 -0700
Subject: [PATCH 1/2] [lldb] Fix data race in statusline format handling
This fixes a data race between the main thread and the default event
handler thread. The statusline format option value was protected by a
mutex, but it was returned as a pointer, allowing one thread to access
it while another was modifying it.
Avoid the data race by returning format values by value instead of by
pointer.
---
lldb/include/lldb/Core/Debugger.h | 12 ++++----
lldb/include/lldb/Core/FormatEntity.h | 4 ++-
lldb/include/lldb/Interpreter/OptionValue.h | 12 ++++----
lldb/include/lldb/Target/Language.h | 4 +--
lldb/source/Core/Debugger.cpp | 30 ++++++++++---------
lldb/source/Core/Disassembler.cpp | 7 +++--
lldb/source/Core/FormatEntity.cpp | 4 +--
lldb/source/Core/Statusline.cpp | 8 ++---
lldb/source/Interpreter/OptionValue.cpp | 6 ++--
.../Language/CPlusPlus/CPlusPlusLanguage.cpp | 8 ++---
.../Language/CPlusPlus/CPlusPlusLanguage.h | 6 ++--
lldb/source/Target/StackFrame.cpp | 7 +++--
lldb/source/Target/Thread.cpp | 12 +++++---
lldb/source/Target/ThreadPlanTracer.cpp | 4 +--
lldb/unittests/Core/DebuggerTest.cpp | 2 +-
15 files changed, 68 insertions(+), 58 deletions(-)
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index c9e5310cded1a..d73aba1e3ce58 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -243,17 +243,17 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
bool GetAutoConfirm() const;
- const FormatEntity::Entry *GetDisassemblyFormat() const;
+ FormatEntity::Entry GetDisassemblyFormat() const;
- const FormatEntity::Entry *GetFrameFormat() const;
+ FormatEntity::Entry GetFrameFormat() const;
- const FormatEntity::Entry *GetFrameFormatUnique() const;
+ FormatEntity::Entry GetFrameFormatUnique() const;
uint64_t GetStopDisassemblyMaxSize() const;
- const FormatEntity::Entry *GetThreadFormat() const;
+ FormatEntity::Entry GetThreadFormat() const;
- const FormatEntity::Entry *GetThreadStopFormat() const;
+ FormatEntity::Entry GetThreadStopFormat() const;
lldb::ScriptLanguage GetScriptLanguage() const;
@@ -297,7 +297,7 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
bool GetShowStatusline() const;
- const FormatEntity::Entry *GetStatuslineFormat() const;
+ FormatEntity::Entry GetStatuslineFormat() const;
bool SetStatuslineFormat(const FormatEntity::Entry &format);
llvm::StringRef GetSeparator() const;
diff --git a/lldb/include/lldb/Core/FormatEntity.h b/lldb/include/lldb/Core/FormatEntity.h
index 1aed3c6ff9e9d..18257161eec7b 100644
--- a/lldb/include/lldb/Core/FormatEntity.h
+++ b/lldb/include/lldb/Core/FormatEntity.h
@@ -205,6 +205,8 @@ struct Entry {
return true;
}
+ operator bool() const { return type != Type::Invalid; }
+
std::vector<Entry> &GetChildren();
std::string string;
@@ -217,7 +219,7 @@ struct Entry {
size_t level = 0;
/// @}
- Type type;
+ Type type = Type::Invalid;
lldb::Format fmt = lldb::eFormatDefault;
lldb::addr_t number = 0;
bool deref = false;
diff --git a/lldb/include/lldb/Interpreter/OptionValue.h b/lldb/include/lldb/Interpreter/OptionValue.h
index e3c139155b0ef..f293a3a33bfa0 100644
--- a/lldb/include/lldb/Interpreter/OptionValue.h
+++ b/lldb/include/lldb/Interpreter/OptionValue.h
@@ -284,6 +284,8 @@ class OptionValue {
return GetStringValue();
if constexpr (std::is_same_v<T, ArchSpec>)
return GetArchSpecValue();
+ if constexpr (std::is_same_v<T, FormatEntity::Entry>)
+ return GetFormatEntityValue();
if constexpr (std::is_enum_v<T>)
if (std::optional<int64_t> value = GetEnumerationValue())
return static_cast<T>(*value);
@@ -295,11 +297,9 @@ class OptionValue {
typename std::remove_pointer<T>::type>::type,
std::enable_if_t<std::is_pointer_v<T>, bool> = true>
T GetValueAs() const {
- if constexpr (std::is_same_v<U, FormatEntity::Entry>)
- return GetFormatEntity();
- if constexpr (std::is_same_v<U, RegularExpression>)
- return GetRegexValue();
- return {};
+ static_assert(std::is_same_v<U, RegularExpression>,
+ "only for RegularExpression");
+ return GetRegexValue();
}
bool SetValueAs(bool v) { return SetBooleanValue(v); }
@@ -382,7 +382,7 @@ class OptionValue {
std::optional<UUID> GetUUIDValue() const;
bool SetUUIDValue(const UUID &uuid);
- const FormatEntity::Entry *GetFormatEntity() const;
+ FormatEntity::Entry GetFormatEntityValue() const;
bool SetFormatEntityValue(const FormatEntity::Entry &entry);
const RegularExpression *GetRegexValue() const;
diff --git a/lldb/include/lldb/Target/Language.h b/lldb/include/lldb/Target/Language.h
index d62871bd7ed70..3d0aa326d5a6d 100644
--- a/lldb/include/lldb/Target/Language.h
+++ b/lldb/include/lldb/Target/Language.h
@@ -495,9 +495,7 @@ class Language : public PluginInterface {
/// Python uses \b except. Defaults to \b catch.
virtual llvm::StringRef GetCatchKeyword() const { return "catch"; }
- virtual const FormatEntity::Entry *GetFunctionNameFormat() const {
- return nullptr;
- }
+ virtual FormatEntity::Entry GetFunctionNameFormat() const { return {}; }
protected:
// Classes that inherit from Language can see and modify these
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 519a2528ca7e0..de60d25b93112 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -294,19 +294,19 @@ bool Debugger::GetAutoConfirm() const {
idx, g_debugger_properties[idx].default_uint_value != 0);
}
-const FormatEntity::Entry *Debugger::GetDisassemblyFormat() const {
+FormatEntity::Entry Debugger::GetDisassemblyFormat() const {
constexpr uint32_t idx = ePropertyDisassemblyFormat;
- return GetPropertyAtIndexAs<const FormatEntity::Entry *>(idx);
+ return GetPropertyAtIndexAs<FormatEntity::Entry>(idx, {});
}
-const FormatEntity::Entry *Debugger::GetFrameFormat() const {
+FormatEntity::Entry Debugger::GetFrameFormat() const {
constexpr uint32_t idx = ePropertyFrameFormat;
- return GetPropertyAtIndexAs<const FormatEntity::Entry *>(idx);
+ return GetPropertyAtIndexAs<FormatEntity::Entry>(idx, {});
}
-const FormatEntity::Entry *Debugger::GetFrameFormatUnique() const {
+FormatEntity::Entry Debugger::GetFrameFormatUnique() const {
constexpr uint32_t idx = ePropertyFrameFormatUnique;
- return GetPropertyAtIndexAs<const FormatEntity::Entry *>(idx);
+ return GetPropertyAtIndexAs<FormatEntity::Entry>(idx, {});
}
uint64_t Debugger::GetStopDisassemblyMaxSize() const {
@@ -350,14 +350,14 @@ void Debugger::SetPrompt(llvm::StringRef p) {
GetCommandInterpreter().UpdatePrompt(new_prompt);
}
-const FormatEntity::Entry *Debugger::GetThreadFormat() const {
+FormatEntity::Entry Debugger::GetThreadFormat() const {
constexpr uint32_t idx = ePropertyThreadFormat;
- return GetPropertyAtIndexAs<const FormatEntity::Entry *>(idx);
+ return GetPropertyAtIndexAs<FormatEntity::Entry>(idx, {});
}
-const FormatEntity::Entry *Debugger::GetThreadStopFormat() const {
+FormatEntity::Entry Debugger::GetThreadStopFormat() const {
constexpr uint32_t idx = ePropertyThreadStopFormat;
- return GetPropertyAtIndexAs<const FormatEntity::Entry *>(idx);
+ return GetPropertyAtIndexAs<FormatEntity::Entry>(idx, {});
}
lldb::ScriptLanguage Debugger::GetScriptLanguage() const {
@@ -492,9 +492,9 @@ bool Debugger::GetShowStatusline() const {
idx, g_debugger_properties[idx].default_uint_value != 0);
}
-const FormatEntity::Entry *Debugger::GetStatuslineFormat() const {
+FormatEntity::Entry Debugger::GetStatuslineFormat() const {
constexpr uint32_t idx = ePropertyStatuslineFormat;
- return GetPropertyAtIndexAs<const FormatEntity::Entry *>(idx);
+ return GetPropertyAtIndexAs<FormatEntity::Entry>(idx, {});
}
bool Debugger::SetStatuslineFormat(const FormatEntity::Entry &format) {
@@ -1536,8 +1536,10 @@ bool Debugger::FormatDisassemblerAddress(const FormatEntity::Entry *format,
FormatEntity::Entry format_entry;
if (format == nullptr) {
- if (exe_ctx != nullptr && exe_ctx->HasTargetScope())
- format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat();
+ if (exe_ctx != nullptr && exe_ctx->HasTargetScope()) {
+ format_entry = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat();
+ format = &format_entry;
+ }
if (format == nullptr) {
FormatEntity::Parse("${addr}: ", format_entry);
format = &format_entry;
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index dce3d59457c0e..6ecbd1f696f44 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -310,8 +310,8 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
const FormatEntity::Entry *disassembly_format = nullptr;
FormatEntity::Entry format;
if (exe_ctx.HasTargetScope()) {
- disassembly_format =
- exe_ctx.GetTargetRef().GetDebugger().GetDisassemblyFormat();
+ format = exe_ctx.GetTargetRef().GetDebugger().GetDisassemblyFormat();
+ disassembly_format = &format;
} else {
FormatEntity::Parse("${addr}: ", format);
disassembly_format = &format;
@@ -1037,8 +1037,9 @@ void InstructionList::Dump(Stream *s, bool show_address, bool show_bytes,
const FormatEntity::Entry *disassembly_format = nullptr;
FormatEntity::Entry format;
if (exe_ctx && exe_ctx->HasTargetScope()) {
- disassembly_format =
+ format =
exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat();
+ disassembly_format = &format;
} else {
FormatEntity::Parse("${addr}: ", format);
disassembly_format = &format;
diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp
index 4dcfa43a7bb04..3c591ba15a075 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -1279,13 +1279,13 @@ static bool FormatFunctionNameForLanguage(Stream &s,
if (!language_plugin)
return false;
- const auto *format = language_plugin->GetFunctionNameFormat();
+ FormatEntity::Entry format = language_plugin->GetFunctionNameFormat();
if (!format)
return false;
StreamString name_stream;
const bool success =
- FormatEntity::Format(*format, name_stream, sc, exe_ctx, /*addr=*/nullptr,
+ FormatEntity::Format(format, name_stream, sc, exe_ctx, /*addr=*/nullptr,
/*valobj=*/nullptr, /*function_changed=*/false,
/*initial_function=*/false);
if (success)
diff --git a/lldb/source/Core/Statusline.cpp b/lldb/source/Core/Statusline.cpp
index 8b3c8d1ccfa80..52f6134123b76 100644
--- a/lldb/source/Core/Statusline.cpp
+++ b/lldb/source/Core/Statusline.cpp
@@ -144,9 +144,9 @@ void Statusline::Redraw(bool update) {
}
StreamString stream;
- if (auto *format = m_debugger.GetStatuslineFormat())
- FormatEntity::Format(*format, stream, &symbol_ctx, &exe_ctx, nullptr,
- nullptr, false, false);
+ FormatEntity::Entry format = m_debugger.GetStatuslineFormat();
+ FormatEntity::Format(format, stream, &symbol_ctx, &exe_ctx, nullptr, nullptr,
+ false, false);
- Draw(std::string(stream.GetString()));
+ Draw(stream.GetString().str());
}
diff --git a/lldb/source/Interpreter/OptionValue.cpp b/lldb/source/Interpreter/OptionValue.cpp
index 28bc57a07ac71..aa118107f27c5 100644
--- a/lldb/source/Interpreter/OptionValue.cpp
+++ b/lldb/source/Interpreter/OptionValue.cpp
@@ -380,11 +380,11 @@ bool OptionValue::SetLanguageValue(lldb::LanguageType new_language) {
return false;
}
-const FormatEntity::Entry *OptionValue::GetFormatEntity() const {
+FormatEntity::Entry OptionValue::GetFormatEntityValue() const {
std::lock_guard<std::mutex> lock(m_mutex);
if (const OptionValueFormatEntity *option_value = GetAsFormatEntity())
- return &option_value->GetCurrentValue();
- return nullptr;
+ return option_value->GetCurrentValue();
+ return {};
}
const RegularExpression *OptionValue::GetRegexValue() const {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 7485577ae67e0..0f18abb47591d 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -2066,9 +2066,9 @@ class PluginProperties : public Properties {
m_collection_sp->Initialize(g_language_cplusplus_properties);
}
- const FormatEntity::Entry *GetFunctionNameFormat() const {
- return GetPropertyAtIndexAs<const FormatEntity::Entry *>(
- ePropertyFunctionNameFormat);
+ FormatEntity::Entry GetFunctionNameFormat() const {
+ return GetPropertyAtIndexAs<FormatEntity::Entry>(
+ ePropertyFunctionNameFormat, {});
}
};
} // namespace
@@ -2078,7 +2078,7 @@ static PluginProperties &GetGlobalPluginProperties() {
return g_settings;
}
-const FormatEntity::Entry *CPlusPlusLanguage::GetFunctionNameFormat() const {
+FormatEntity::Entry CPlusPlusLanguage::GetFunctionNameFormat() const {
return GetGlobalPluginProperties().GetFunctionNameFormat();
}
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
index 575f76c3101ed..22acdf3e8efe9 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
@@ -93,8 +93,8 @@ class CPlusPlusLanguage : public Language {
static llvm::StringRef GetPluginNameStatic() { return "cplusplus"; }
bool SymbolNameFitsToLanguage(Mangled mangled) const override;
-
- bool DemangledNameContainsPath(llvm::StringRef path,
+
+ bool DemangledNameContainsPath(llvm::StringRef path,
ConstString demangled) const override;
ConstString
@@ -136,7 +136,7 @@ class CPlusPlusLanguage : public Language {
llvm::StringRef GetInstanceVariableName() override { return "this"; }
- const FormatEntity::Entry *GetFunctionNameFormat() const override;
+ FormatEntity::Entry GetFunctionNameFormat() const override;
// PluginInterface protocol
llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index ab5cd0b27c789..52a54020bc468 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -1937,12 +1937,15 @@ void StackFrame::DumpUsingSettingsFormat(Stream *strm, bool show_unique,
ExecutionContext exe_ctx(shared_from_this());
const FormatEntity::Entry *frame_format = nullptr;
+ FormatEntity::Entry format_entry;
Target *target = exe_ctx.GetTargetPtr();
if (target) {
if (show_unique) {
- frame_format = target->GetDebugger().GetFrameFormatUnique();
+ format_entry = target->GetDebugger().GetFrameFormatUnique();
+ frame_format = &format_entry;
} else {
- frame_format = target->GetDebugger().GetFrameFormat();
+ format_entry = target->GetDebugger().GetFrameFormat();
+ frame_format = &format_entry;
}
}
if (!DumpUsingFormat(*strm, frame_format, frame_marker)) {
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index b0e0f1e67c060..c68894808eacc 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -1668,10 +1668,14 @@ void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
ExecutionContext exe_ctx(shared_from_this());
const FormatEntity::Entry *thread_format;
- if (stop_format)
- thread_format = exe_ctx.GetTargetRef().GetDebugger().GetThreadStopFormat();
- else
- thread_format = exe_ctx.GetTargetRef().GetDebugger().GetThreadFormat();
+ FormatEntity::Entry format_entry;
+ if (stop_format) {
+ format_entry = exe_ctx.GetTargetRef().GetDebugger().GetThreadStopFormat();
+ thread_format = &format_entry;
+ } else {
+ format_entry = exe_ctx.GetTargetRef().GetDebugger().GetThreadFormat();
+ thread_format = &format_entry;
+ }
assert(thread_format);
diff --git a/lldb/source/Target/ThreadPlanTracer.cpp b/lldb/source/Target/ThreadPlanTracer.cpp
index ab63cc7f6c223..c5a9c5b806c30 100644
--- a/lldb/source/Target/ThreadPlanTracer.cpp
+++ b/lldb/source/Target/ThreadPlanTracer.cpp
@@ -174,11 +174,11 @@ void ThreadPlanAssemblyTracer::Log() {
const bool show_control_flow_kind = true;
Instruction *instruction =
instruction_list.GetInstructionAtIndex(0).get();
- const FormatEntity::Entry *disassemble_format =
+ FormatEntity::Entry disassemble_format =
m_process.GetTarget().GetDebugger().GetDisassemblyFormat();
instruction->Dump(stream_sp.get(), max_opcode_byte_size, show_address,
show_bytes, show_control_flow_kind, nullptr, nullptr,
- nullptr, disassemble_format, 0);
+ nullptr, &disassemble_format, 0);
}
}
}
diff --git a/lldb/unittests/Core/DebuggerTest.cpp b/lldb/unittests/Core/DebuggerTest.cpp
index df7d999788553..4dccd912c63ae 100644
--- a/lldb/unittests/Core/DebuggerTest.cpp
+++ b/lldb/unittests/Core/DebuggerTest.cpp
@@ -46,7 +46,7 @@ TEST_F(DebuggerTest, TestSettings) {
FormatEntity::Entry format("foo");
EXPECT_TRUE(debugger_sp->SetStatuslineFormat(format));
- EXPECT_EQ(debugger_sp->GetStatuslineFormat()->string, "foo");
+ EXPECT_EQ(debugger_sp->GetStatuslineFormat().string, "foo");
Debugger::Destroy(debugger_sp);
}
>From 92662d805e3b1ca6923042a74cd29f88afc3ecfe Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Tue, 3 Jun 2025 13:07:05 -0700
Subject: [PATCH 2/2] Formatting
---
lldb/source/Core/Debugger.cpp | 3 ++-
lldb/source/Core/Disassembler.cpp | 3 +--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index de60d25b93112..81037d3def811 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -1537,7 +1537,8 @@ bool Debugger::FormatDisassemblerAddress(const FormatEntity::Entry *format,
if (format == nullptr) {
if (exe_ctx != nullptr && exe_ctx->HasTargetScope()) {
- format_entry = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat();
+ format_entry =
+ exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat();
format = &format_entry;
}
if (format == nullptr) {
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 6ecbd1f696f44..833e327579a29 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -1037,8 +1037,7 @@ void InstructionList::Dump(Stream *s, bool show_address, bool show_bytes,
const FormatEntity::Entry *disassembly_format = nullptr;
FormatEntity::Entry format;
if (exe_ctx && exe_ctx->HasTargetScope()) {
- format =
- exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat();
+ format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat();
disassembly_format = &format;
} else {
FormatEntity::Parse("${addr}: ", format);
More information about the lldb-commits
mailing list