Skip to content

[lldb] Do not bump memory modificator ID when "internal" debugger memory is updated #129092

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
May 1, 2025
4 changes: 3 additions & 1 deletion lldb/include/lldb/Target/Memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ class AllocatedMemoryCache {

bool DeallocateMemory(lldb::addr_t ptr);

bool IsInCache(lldb::addr_t addr) const;

protected:
typedef std::shared_ptr<AllocatedBlock> AllocatedBlockSP;

Expand All @@ -133,7 +135,7 @@ class AllocatedMemoryCache {

// Classes that inherit from MemoryCache can see and modify these
Process &m_process;
std::recursive_mutex m_mutex;
mutable std::recursive_mutex m_mutex;
typedef std::multimap<uint32_t, AllocatedBlockSP> PermissionsToBlockMap;
PermissionsToBlockMap m_memory_map;

Expand Down
13 changes: 13 additions & 0 deletions lldb/include/lldb/Target/Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class ProcessProperties : public Properties {
void SetOSPluginReportsAllThreads(bool does_report);
bool GetSteppingRunsAllThreads() const;
FollowForkMode GetFollowForkMode() const;
bool TrackMemoryCacheChanges() const;

protected:
Process *m_process; // Can be nullptr for global ProcessProperties
Expand Down Expand Up @@ -312,6 +313,18 @@ class ProcessModID {
return lldb::EventSP();
}

void Dump(Stream &stream) const {
stream.Format("ProcessModID:\n"
" m_stop_id: {0}\n m_last_natural_stop_id: {1}\n"
" m_resume_id: {2}\n m_memory_id: {3}\n"
" m_last_user_expression_resume: {4}\n"
" m_running_user_expression: {5}\n"
" m_running_utility_function: {6}\n",
m_stop_id, m_last_natural_stop_id, m_resume_id, m_memory_id,
m_last_user_expression_resume, m_running_user_expression,
m_running_utility_function);
}

private:
uint32_t m_stop_id = 0;
uint32_t m_last_natural_stop_id = 0;
Expand Down
13 changes: 13 additions & 0 deletions lldb/source/Commands/CommandObjectProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1377,6 +1377,9 @@ class CommandObjectProcessStatus : public CommandObjectParsed {
case 'v':
m_verbose = true;
break;
case 'd':
m_dump = true;
break;
default:
llvm_unreachable("Unimplemented option");
}
Expand All @@ -1386,6 +1389,7 @@ class CommandObjectProcessStatus : public CommandObjectParsed {

void OptionParsingStarting(ExecutionContext *execution_context) override {
m_verbose = false;
m_dump = false;
}

llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
Expand All @@ -1394,6 +1398,7 @@ class CommandObjectProcessStatus : public CommandObjectParsed {

// Instance variables to hold the values for command options.
bool m_verbose = false;
bool m_dump = false;
};

protected:
Expand Down Expand Up @@ -1448,6 +1453,14 @@ class CommandObjectProcessStatus : public CommandObjectParsed {
crash_info_sp->GetDescription(strm);
}
}

if (m_options.m_dump) {
StateType state = process->GetState();
if (state == eStateStopped) {
ProcessModID process_mod_id = process->GetModID();
process_mod_id.Dump(result.GetOutputStream());
}
}
}

private:
Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Commands/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,8 @@ let Command = "process handle" in {
let Command = "process status" in {
def process_status_verbose : Option<"verbose", "v">, Group<1>,
Desc<"Show verbose process status including extended crash information.">;
def process_status_dump : Option<"dump-modification-id", "d">, Group<1>,
Desc<"Dump the state of the ProcessModID of the stopped process.">;
}

let Command = "process save_core" in {
Expand Down
8 changes: 8 additions & 0 deletions lldb/source/Target/Memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,3 +433,11 @@ bool AllocatedMemoryCache::DeallocateMemory(lldb::addr_t addr) {
(uint64_t)addr, success);
return success;
}

bool AllocatedMemoryCache::IsInCache(lldb::addr_t addr) const {
std::lock_guard guard(m_mutex);

return llvm::any_of(m_memory_map, [addr](const auto &block) {
return block.second->Contains(addr);
});
}
13 changes: 12 additions & 1 deletion lldb/source/Target/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,12 @@ FollowForkMode ProcessProperties::GetFollowForkMode() const {
g_process_properties[idx].default_uint_value));
}

bool ProcessProperties::TrackMemoryCacheChanges() const {
const uint32_t idx = ePropertyTrackMemoryCacheChanges;
return GetPropertyAtIndexAs<bool>(
idx, g_process_properties[idx].default_uint_value != 0);
}

ProcessSP Process::FindPlugin(lldb::TargetSP target_sp,
llvm::StringRef plugin_name,
ListenerSP listener_sp,
Expand Down Expand Up @@ -2267,6 +2273,7 @@ size_t Process::WriteMemoryPrivate(addr_t addr, const void *buf, size_t size,
return bytes_written;
}

#define USE_ALLOCATE_MEMORY_CACHE 1
size_t Process::WriteMemory(addr_t addr, const void *buf, size_t size,
Status &error) {
if (ABISP abi_sp = GetABI())
Expand All @@ -2279,7 +2286,12 @@ size_t Process::WriteMemory(addr_t addr, const void *buf, size_t size,
if (buf == nullptr || size == 0)
return 0;

#if defined(USE_ALLOCATE_MEMORY_CACHE)
if (TrackMemoryCacheChanges() || !m_allocated_memory_cache.IsInCache(addr))
m_mod_id.BumpMemoryID();
#else
m_mod_id.BumpMemoryID();
#endif

// We need to write any data that would go where any current software traps
// (enabled software breakpoints) any software traps (breakpoints) that we
Expand Down Expand Up @@ -2408,7 +2420,6 @@ Status Process::WriteObjectFile(std::vector<ObjectFile::LoadableData> entries) {
return error;
}

#define USE_ALLOCATE_MEMORY_CACHE 1
addr_t Process::AllocateMemory(size_t size, uint32_t permissions,
Status &error) {
if (GetPrivateState() != eStateStopped) {
Expand Down
3 changes: 3 additions & 0 deletions lldb/source/Target/TargetProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,9 @@ let Definition = "process" in {
DefaultEnumValue<"eFollowParent">,
EnumValues<"OptionEnumValues(g_follow_fork_mode_values)">,
Desc<"Debugger's behavior upon fork or vfork.">;
def TrackMemoryCacheChanges: Property<"track-memory-cache-changes", "Boolean">,
DefaultTrue,
Desc<"If true, memory cache modifications (which happen often during expressions evaluation) will bump process state ID (and invalidate all synthetic children). Disabling this option helps to avoid synthetic children reevaluation when pretty printers heavily use expressions. The downside of disabled setting is that convenience variables won't reevaluate synthetic children automatically.">;
}

let Definition = "platform" in {
Expand Down
1 change: 1 addition & 0 deletions lldb/test/API/commands/settings/TestSettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,7 @@ def test_all_settings_exist(self):
"target.use-hex-immediates",
"target.process.disable-memory-cache",
"target.process.extra-startup-command",
"target.process.track-memory-cache-changes",
"target.process.thread.trace-thread",
"target.process.thread.step-avoid-regexp",
],
Expand Down
55 changes: 55 additions & 0 deletions lldb/test/Shell/Expr/TestExprWithSideEffect.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Tests evaluating expressions with side effects.
// Applied side effect should be visible to the debugger.

// RUN: %build %s -o %t
// RUN: %lldb %t \
// RUN: -o "settings set target.process.track-memory-cache-changes false" \
// RUN: -o "run" \
// RUN: -o "frame variable x" \
// RUN: -o "expr x.inc()" \
// RUN: -o "frame variable x" \
// RUN: -o "continue" \
// RUN: -o "frame variable x" \
// RUN: -o "expr x.i = 10" \
// RUN: -o "frame variable x" \
// RUN: -o "continue" \
// RUN: -o "frame variable x" \
// RUN: -o "exit" | FileCheck %s -dump-input=fail

class X {
int i = 0;

public:
void inc() { ++i; }
};

int main() {
X x;
x.inc();

__builtin_debugtrap();
__builtin_debugtrap();
__builtin_debugtrap();
return 0;
}

// CHECK-LABEL: frame variable x
// CHECK: (X) x = (i = 1)

// CHECK-LABEL: expr x.inc()
// CHECK-LABEL: frame variable x
// CHECK: (X) x = (i = 2)

// CHECK-LABEL: continue
// CHECK-LABEL: frame variable x
// CHECK: (X) x = (i = 2)

// CHECK-LABEL: expr x.i = 10
// CHECK: (int) $0 = 10

// CHECK-LABEL: frame variable x
// CHECK: (X) x = (i = 10)

// CHECK-LABEL: continue
// CHECK-LABEL: frame variable x
// CHECK: (X) x = (i = 10)
52 changes: 52 additions & 0 deletions lldb/test/Shell/Expr/TestExprWithSideEffectOnConvenienceVar.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Tests evaluating expressions with side effects on convenience variable.
// Applied side effect should be visible to the debugger.

// UNSUPPORTED: system-windows

// RUN: %build %s -o %t
// RUN: %lldb %t \
// RUN: -o "settings set target.process.track-memory-cache-changes false" \
// RUN: -o "run" \
// RUN: -o "expr int \$y = 11" \
// RUN: -o "expr \$y" \
// RUN: -o "expr \$y = 100" \
// RUN: -o "expr \$y" \
// RUN: -o "continue" \
// RUN: -o "expr \$y" \
// RUN: -o "expr X \$mine = {100, 200}" \
// RUN: -o "expr \$mine.a = 300" \
// RUN: -o "expr \$mine" \
// RUN: -o "exit" | FileCheck %s -dump-input=fail

struct X {
int a;
int b;
};

int main() {
X x;

__builtin_debugtrap();
__builtin_debugtrap();
return 0;
}

// CHECK-LABEL: expr int $y = 11
// CHECK-LABEL: expr $y
// CHECK: (int) $y = 11

// CHECK-LABEL: expr $y = 100
// CHECK: (int) $0 = 100

// CHECK-LABEL: expr $y
// CHECK: (int) $y = 100

// CHECK-LABEL: continue
// CHECK-LABEL: expr $y
// CHECK: (int) $y = 100

// CHECK-LABEL: expr X $mine = {100, 200}
// CHECK-LABEL: expr $mine.a = 300
// CHECK: (int) $1 = 300
// CHECK-LABEL: expr $mine
// CHECK: (X) $mine = (a = 300, b = 200)
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Same as TestExprWithSideEffectOnConvenienceVar.cpp but without $ escaping

// REQUIRES: target-windows

// RUN: %build %s -o %t
// RUN: %lldb %t \
// RUN: -o "settings set target.process.track-memory-cache-changes false" \
// RUN: -o "run" \
// RUN: -o "expr int $y = 11" \
// RUN: -o "expr $y" \
// RUN: -o "expr $y = 100" \
// RUN: -o "expr $y" \
// RUN: -o "continue" \
// RUN: -o "expr $y" \
// RUN: -o "expr X $mine = {100, 200}" \
// RUN: -o "expr $mine.a = 300" \
// RUN: -o "expr $mine" \
// RUN: -o "exit" | FileCheck %s -dump-input=fail

struct X {
int a;
int b;
};

int main() {
X x;

__builtin_debugtrap();
__builtin_debugtrap();
return 0;
}

// CHECK-LABEL: expr int $y = 11
// CHECK-LABEL: expr $y
// CHECK: (int) $y = 11

// CHECK-LABEL: expr $y = 100
// CHECK: (int) $0 = 100

// CHECK-LABEL: expr $y
// CHECK: (int) $y = 100

// CHECK-LABEL: continue
// CHECK-LABEL: expr $y
// CHECK: (int) $y = 100

// CHECK-LABEL: expr X $mine = {100, 200}
// CHECK-LABEL: expr $mine.a = 300
// CHECK: (int) $1 = 300
// CHECK-LABEL: expr $mine
// CHECK: (X) $mine = (a = 300, b = 200)
67 changes: 67 additions & 0 deletions lldb/test/Shell/Expr/TestProcessModificationIdOnExpr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Tests that ProcessModID.m_memory_id is not bumped when evaluating expressions without side effects.

// REQUIRES: target-windows
// Due to different implementations exact numbers (m_stop_id) are different on different OSs. So we lock this test to specific platform.

// RUN: %build %s -o %t
// RUN: %lldb %t \
// RUN: -o "settings set target.process.track-memory-cache-changes false" \
// RUN: -o "run" \
// RUN: -o "process status -d" \
// RUN: -o "expr x.i != 42" \
// RUN: -o "process status -d" \
// RUN: -o "expr x.get()" \
// RUN: -o "process status -d" \
// RUN: -o "expr x.i = 10" \
// RUN: -o "process status -d" \
// RUN: -o "continue" \
// RUN: -o "process status -d" \
// RUN: -o "exit" | FileCheck %s -dump-input=fail

class X {
int i = 0;

public:
int get() { return i; }
};

int main() {
X x;
x.get();

__builtin_debugtrap();
__builtin_debugtrap();
return 0;
}

// CHECK-LABEL: process status -d
// CHECK: m_stop_id: 2
// CHECK: m_memory_id: 0

// CHECK-LABEL: expr x.i != 42
// IDs are not changed when executing simple expressions

// CHECK-LABEL: process status -d
// CHECK: m_stop_id: 2
// CHECK: m_memory_id: 0

// CHECK-LABEL: expr x.get()
// Expression causes ID to be bumped because LLDB has to execute function

// CHECK-LABEL: process status -d
// CHECK: m_stop_id: 3
// CHECK: m_memory_id: 1

// CHECK-LABEL: expr x.i = 10
// Expression causes MemoryID to be bumped because LLDB writes to non-cache memory

// CHECK-LABEL: process status -d
// CHECK: m_stop_id: 3
// CHECK: m_memory_id: 2

// CHECK-LABEL: continue
// Continue causes StopID to be bumped because process is resumed

// CHECK-LABEL: process status -d
// CHECK: m_stop_id: 4
// CHECK: m_memory_id: 2