Felipe de Azevedo Piovezan 03bb10bea6
[lldb] Clear Frames when changing disable-language-runtime-unwindplans (#151208)
This patch uses the "setting changed" callback to clear previously
cached stack frames when
`target.process.disable-language-runtime-unwindplans` is changed. This
is necessary so that changing the setting followed by a `backtrace`
command produces an accurate backtrace.

With this, a user can create a custom command like below in order to
quickly inspect a backtrace created without language plugins.

```
debugger.HandleCommand("settings set target.process.disable-language-runtime-unwindplans true")
debugger.HandleCommand("bt " + command)
debugger.HandleCommand("settings set target.process.disable-language-runtime-unwindplans false")
```

In the future, we may consider implementing this as an option to
`backtrace`. Currently, this process setting is the only way of
affecting the unwinder, and changing the process setting as part of the
backtrace implementation doesn't feel right.

There are no upstream users of this flag, so we cannot write a test for
it here.
2025-07-31 18:00:55 -07:00

6816 lines
244 KiB
C++

//===-- Process.cpp -------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include <atomic>
#include <memory>
#include <mutex>
#include <optional>
#include "llvm/ADT/ScopeExit.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/Threading.h"
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Breakpoint/StoppointCallbackContext.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Progress.h"
#include "lldb/Core/Telemetry.h"
#include "lldb/Expression/DiagnosticManager.h"
#include "lldb/Expression/DynamicCheckerFunctions.h"
#include "lldb/Expression/UserExpression.h"
#include "lldb/Expression/UtilityFunction.h"
#include "lldb/Host/ConnectionFileDescriptor.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Host/OptionParser.h"
#include "lldb/Host/Pipe.h"
#include "lldb/Host/Terminal.h"
#include "lldb/Host/ThreadLauncher.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/OptionArgParser.h"
#include "lldb/Interpreter/OptionValueProperties.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Target/ABI.h"
#include "lldb/Target/AssertFrameRecognizer.h"
#include "lldb/Target/DynamicLoader.h"
#include "lldb/Target/InstrumentationRuntime.h"
#include "lldb/Target/JITLoader.h"
#include "lldb/Target/JITLoaderList.h"
#include "lldb/Target/Language.h"
#include "lldb/Target/LanguageRuntime.h"
#include "lldb/Target/MemoryHistory.h"
#include "lldb/Target/MemoryRegionInfo.h"
#include "lldb/Target/OperatingSystem.h"
#include "lldb/Target/Platform.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StopInfo.h"
#include "lldb/Target/StructuredDataPlugin.h"
#include "lldb/Target/SystemRuntime.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/TargetList.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/ThreadPlan.h"
#include "lldb/Target/ThreadPlanBase.h"
#include "lldb/Target/ThreadPlanCallFunction.h"
#include "lldb/Target/ThreadPlanStack.h"
#include "lldb/Target/UnixSignals.h"
#include "lldb/Target/VerboseTrapFrameRecognizer.h"
#include "lldb/Utility/AddressableBits.h"
#include "lldb/Utility/Event.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/NameMatches.h"
#include "lldb/Utility/ProcessInfo.h"
#include "lldb/Utility/SelectHelper.h"
#include "lldb/Utility/State.h"
#include "lldb/Utility/Timer.h"
using namespace lldb;
using namespace lldb_private;
using namespace std::chrono;
class ProcessOptionValueProperties
: public Cloneable<ProcessOptionValueProperties, OptionValueProperties> {
public:
ProcessOptionValueProperties(llvm::StringRef name) : Cloneable(name) {}
const Property *
GetPropertyAtIndex(size_t idx,
const ExecutionContext *exe_ctx) const override {
// When getting the value for a key from the process options, we will
// always try and grab the setting from the current process if there is
// one. Else we just use the one from this instance.
if (exe_ctx) {
Process *process = exe_ctx->GetProcessPtr();
if (process) {
ProcessOptionValueProperties *instance_properties =
static_cast<ProcessOptionValueProperties *>(
process->GetValueProperties().get());
if (this != instance_properties)
return instance_properties->ProtectedGetPropertyAtIndex(idx);
}
}
return ProtectedGetPropertyAtIndex(idx);
}
};
static constexpr OptionEnumValueElement g_follow_fork_mode_values[] = {
{
eFollowParent,
"parent",
"Continue tracing the parent process and detach the child.",
},
{
eFollowChild,
"child",
"Trace the child process and detach the parent.",
},
};
#define LLDB_PROPERTIES_process
#include "TargetProperties.inc"
enum {
#define LLDB_PROPERTIES_process
#include "TargetPropertiesEnum.inc"
ePropertyExperimental,
};
#define LLDB_PROPERTIES_process_experimental
#include "TargetProperties.inc"
enum {
#define LLDB_PROPERTIES_process_experimental
#include "TargetPropertiesEnum.inc"
};
class ProcessExperimentalOptionValueProperties
: public Cloneable<ProcessExperimentalOptionValueProperties,
OptionValueProperties> {
public:
ProcessExperimentalOptionValueProperties()
: Cloneable(Properties::GetExperimentalSettingsName()) {}
};
ProcessExperimentalProperties::ProcessExperimentalProperties()
: Properties(OptionValuePropertiesSP(
new ProcessExperimentalOptionValueProperties())) {
m_collection_sp->Initialize(g_process_experimental_properties);
}
ProcessProperties::ProcessProperties(lldb_private::Process *process)
: Properties(),
m_process(process) // Can be nullptr for global ProcessProperties
{
if (process == nullptr) {
// Global process properties, set them up one time
m_collection_sp = std::make_shared<ProcessOptionValueProperties>("process");
m_collection_sp->Initialize(g_process_properties);
m_collection_sp->AppendProperty(
"thread", "Settings specific to threads.", true,
Thread::GetGlobalProperties().GetValueProperties());
} else {
m_collection_sp =
OptionValueProperties::CreateLocalCopy(Process::GetGlobalProperties());
m_collection_sp->SetValueChangedCallback(
ePropertyPythonOSPluginPath,
[this] { m_process->LoadOperatingSystemPlugin(true); });
m_collection_sp->SetValueChangedCallback(
ePropertyDisableLangRuntimeUnwindPlans,
[this] { DisableLanguageRuntimeUnwindPlansCallback(); });
}
m_experimental_properties_up =
std::make_unique<ProcessExperimentalProperties>();
m_collection_sp->AppendProperty(
Properties::GetExperimentalSettingsName(),
"Experimental settings - setting these won't produce "
"errors if the setting is not present.",
true, m_experimental_properties_up->GetValueProperties());
}
ProcessProperties::~ProcessProperties() = default;
bool ProcessProperties::GetDisableMemoryCache() const {
const uint32_t idx = ePropertyDisableMemCache;
return GetPropertyAtIndexAs<bool>(
idx, g_process_properties[idx].default_uint_value != 0);
}
uint64_t ProcessProperties::GetMemoryCacheLineSize() const {
const uint32_t idx = ePropertyMemCacheLineSize;
return GetPropertyAtIndexAs<uint64_t>(
idx, g_process_properties[idx].default_uint_value);
}
Args ProcessProperties::GetExtraStartupCommands() const {
Args args;
const uint32_t idx = ePropertyExtraStartCommand;
m_collection_sp->GetPropertyAtIndexAsArgs(idx, args);
return args;
}
void ProcessProperties::SetExtraStartupCommands(const Args &args) {
const uint32_t idx = ePropertyExtraStartCommand;
m_collection_sp->SetPropertyAtIndexFromArgs(idx, args);
}
FileSpec ProcessProperties::GetPythonOSPluginPath() const {
const uint32_t idx = ePropertyPythonOSPluginPath;
return GetPropertyAtIndexAs<FileSpec>(idx, {});
}
uint32_t ProcessProperties::GetVirtualAddressableBits() const {
const uint32_t idx = ePropertyVirtualAddressableBits;
return GetPropertyAtIndexAs<uint64_t>(
idx, g_process_properties[idx].default_uint_value);
}
void ProcessProperties::SetVirtualAddressableBits(uint32_t bits) {
const uint32_t idx = ePropertyVirtualAddressableBits;
SetPropertyAtIndex(idx, static_cast<uint64_t>(bits));
}
uint32_t ProcessProperties::GetHighmemVirtualAddressableBits() const {
const uint32_t idx = ePropertyHighmemVirtualAddressableBits;
return GetPropertyAtIndexAs<uint64_t>(
idx, g_process_properties[idx].default_uint_value);
}
void ProcessProperties::SetHighmemVirtualAddressableBits(uint32_t bits) {
const uint32_t idx = ePropertyHighmemVirtualAddressableBits;
SetPropertyAtIndex(idx, static_cast<uint64_t>(bits));
}
void ProcessProperties::SetPythonOSPluginPath(const FileSpec &file) {
const uint32_t idx = ePropertyPythonOSPluginPath;
SetPropertyAtIndex(idx, file);
}
bool ProcessProperties::GetIgnoreBreakpointsInExpressions() const {
const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions;
return GetPropertyAtIndexAs<bool>(
idx, g_process_properties[idx].default_uint_value != 0);
}
void ProcessProperties::SetIgnoreBreakpointsInExpressions(bool ignore) {
const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions;
SetPropertyAtIndex(idx, ignore);
}
bool ProcessProperties::GetUnwindOnErrorInExpressions() const {
const uint32_t idx = ePropertyUnwindOnErrorInExpressions;
return GetPropertyAtIndexAs<bool>(
idx, g_process_properties[idx].default_uint_value != 0);
}
void ProcessProperties::SetUnwindOnErrorInExpressions(bool ignore) {
const uint32_t idx = ePropertyUnwindOnErrorInExpressions;
SetPropertyAtIndex(idx, ignore);
}
bool ProcessProperties::GetStopOnSharedLibraryEvents() const {
const uint32_t idx = ePropertyStopOnSharedLibraryEvents;
return GetPropertyAtIndexAs<bool>(
idx, g_process_properties[idx].default_uint_value != 0);
}
void ProcessProperties::SetStopOnSharedLibraryEvents(bool stop) {
const uint32_t idx = ePropertyStopOnSharedLibraryEvents;
SetPropertyAtIndex(idx, stop);
}
bool ProcessProperties::GetDisableLangRuntimeUnwindPlans() const {
const uint32_t idx = ePropertyDisableLangRuntimeUnwindPlans;
return GetPropertyAtIndexAs<bool>(
idx, g_process_properties[idx].default_uint_value != 0);
}
void ProcessProperties::SetDisableLangRuntimeUnwindPlans(bool disable) {
const uint32_t idx = ePropertyDisableLangRuntimeUnwindPlans;
SetPropertyAtIndex(idx, disable);
m_process->Flush();
}
void ProcessProperties::DisableLanguageRuntimeUnwindPlansCallback() {
if (!m_process)
return;
for (auto thread_sp : m_process->Threads()) {
thread_sp->ClearStackFrames();
thread_sp->DiscardThreadPlans(/*force*/ true);
}
}
bool ProcessProperties::GetDetachKeepsStopped() const {
const uint32_t idx = ePropertyDetachKeepsStopped;
return GetPropertyAtIndexAs<bool>(
idx, g_process_properties[idx].default_uint_value != 0);
}
void ProcessProperties::SetDetachKeepsStopped(bool stop) {
const uint32_t idx = ePropertyDetachKeepsStopped;
SetPropertyAtIndex(idx, stop);
}
bool ProcessProperties::GetWarningsOptimization() const {
const uint32_t idx = ePropertyWarningOptimization;
return GetPropertyAtIndexAs<bool>(
idx, g_process_properties[idx].default_uint_value != 0);
}
bool ProcessProperties::GetWarningsUnsupportedLanguage() const {
const uint32_t idx = ePropertyWarningUnsupportedLanguage;
return GetPropertyAtIndexAs<bool>(
idx, g_process_properties[idx].default_uint_value != 0);
}
bool ProcessProperties::GetStopOnExec() const {
const uint32_t idx = ePropertyStopOnExec;
return GetPropertyAtIndexAs<bool>(
idx, g_process_properties[idx].default_uint_value != 0);
}
std::chrono::seconds ProcessProperties::GetUtilityExpressionTimeout() const {
const uint32_t idx = ePropertyUtilityExpressionTimeout;
uint64_t value = GetPropertyAtIndexAs<uint64_t>(
idx, g_process_properties[idx].default_uint_value);
return std::chrono::seconds(value);
}
std::chrono::seconds ProcessProperties::GetInterruptTimeout() const {
const uint32_t idx = ePropertyInterruptTimeout;
uint64_t value = GetPropertyAtIndexAs<uint64_t>(
idx, g_process_properties[idx].default_uint_value);
return std::chrono::seconds(value);
}
bool ProcessProperties::GetSteppingRunsAllThreads() const {
const uint32_t idx = ePropertySteppingRunsAllThreads;
return GetPropertyAtIndexAs<bool>(
idx, g_process_properties[idx].default_uint_value != 0);
}
bool ProcessProperties::GetOSPluginReportsAllThreads() const {
const bool fail_value = true;
const Property *exp_property =
m_collection_sp->GetPropertyAtIndex(ePropertyExperimental);
OptionValueProperties *exp_values =
exp_property->GetValue()->GetAsProperties();
if (!exp_values)
return fail_value;
return exp_values
->GetPropertyAtIndexAs<bool>(ePropertyOSPluginReportsAllThreads)
.value_or(fail_value);
}
void ProcessProperties::SetOSPluginReportsAllThreads(bool does_report) {
const Property *exp_property =
m_collection_sp->GetPropertyAtIndex(ePropertyExperimental);
OptionValueProperties *exp_values =
exp_property->GetValue()->GetAsProperties();
if (exp_values)
exp_values->SetPropertyAtIndex(ePropertyOSPluginReportsAllThreads,
does_report);
}
FollowForkMode ProcessProperties::GetFollowForkMode() const {
const uint32_t idx = ePropertyFollowForkMode;
return GetPropertyAtIndexAs<FollowForkMode>(
idx, static_cast<FollowForkMode>(
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,
const FileSpec *crash_file_path,
bool can_connect) {
static uint32_t g_process_unique_id = 0;
ProcessSP process_sp;
ProcessCreateInstance create_callback = nullptr;
if (!plugin_name.empty()) {
create_callback =
PluginManager::GetProcessCreateCallbackForPluginName(plugin_name);
if (create_callback) {
process_sp = create_callback(target_sp, listener_sp, crash_file_path,
can_connect);
if (process_sp) {
if (process_sp->CanDebug(target_sp, true)) {
process_sp->m_process_unique_id = ++g_process_unique_id;
} else
process_sp.reset();
}
}
} else {
for (uint32_t idx = 0;
(create_callback =
PluginManager::GetProcessCreateCallbackAtIndex(idx)) != nullptr;
++idx) {
process_sp = create_callback(target_sp, listener_sp, crash_file_path,
can_connect);
if (process_sp) {
if (process_sp->CanDebug(target_sp, false)) {
process_sp->m_process_unique_id = ++g_process_unique_id;
break;
} else
process_sp.reset();
}
}
}
return process_sp;
}
llvm::StringRef Process::GetStaticBroadcasterClass() {
static constexpr llvm::StringLiteral class_name("lldb.process");
return class_name;
}
Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp)
: Process(target_sp, listener_sp, UnixSignals::CreateForHost()) {
// This constructor just delegates to the full Process constructor,
// defaulting to using the Host's UnixSignals.
}
Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp,
const UnixSignalsSP &unix_signals_sp)
: ProcessProperties(this),
Broadcaster((target_sp->GetDebugger().GetBroadcasterManager()),
Process::GetStaticBroadcasterClass().str()),
m_target_wp(target_sp), m_public_state(eStateUnloaded),
m_private_state(eStateUnloaded),
m_private_state_broadcaster(nullptr,
"lldb.process.internal_state_broadcaster"),
m_private_state_control_broadcaster(
nullptr, "lldb.process.internal_state_control_broadcaster"),
m_private_state_listener_sp(
Listener::MakeListener("lldb.process.internal_state_listener")),
m_mod_id(), m_process_unique_id(0), m_thread_index_id(0),
m_thread_id_to_index_id_map(), m_exit_status(-1),
m_thread_list_real(*this), m_thread_list(*this), m_thread_plans(*this),
m_extended_thread_list(*this),
m_base_direction(RunDirection::eRunForward), m_extended_thread_stop_id(0),
m_queue_list(this), m_queue_list_stop_id(0),
m_unix_signals_sp(unix_signals_sp), m_abi_sp(), m_process_input_reader(),
m_stdio_communication("process.stdio"), m_stdio_communication_mutex(),
m_stdin_forward(false), m_stdout_data(), m_stderr_data(),
m_profile_data_comm_mutex(), m_profile_data(), m_iohandler_sync(0),
m_memory_cache(*this), m_allocated_memory_cache(*this),
m_should_detach(false), m_next_event_action_up(), m_public_run_lock(),
m_private_run_lock(), m_currently_handling_do_on_removals(false),
m_resume_requested(false), m_interrupt_tid(LLDB_INVALID_THREAD_ID),
m_finalizing(false), m_destructing(false),
m_clear_thread_plans_on_stop(false), m_force_next_event_delivery(false),
m_last_broadcast_state(eStateInvalid), m_destroy_in_process(false),
m_can_interpret_function_calls(false), m_run_thread_plan_lock(),
m_can_jit(eCanJITDontKnow),
m_crash_info_dict_sp(new StructuredData::Dictionary()) {
CheckInWithManager();
Log *log = GetLog(LLDBLog::Object);
LLDB_LOGF(log, "%p Process::Process()", static_cast<void *>(this));
if (!m_unix_signals_sp)
m_unix_signals_sp = std::make_shared<UnixSignals>();
SetEventName(eBroadcastBitStateChanged, "state-changed");
SetEventName(eBroadcastBitInterrupt, "interrupt");
SetEventName(eBroadcastBitSTDOUT, "stdout-available");
SetEventName(eBroadcastBitSTDERR, "stderr-available");
SetEventName(eBroadcastBitProfileData, "profile-data-available");
SetEventName(eBroadcastBitStructuredData, "structured-data-available");
m_private_state_control_broadcaster.SetEventName(
eBroadcastInternalStateControlStop, "control-stop");
m_private_state_control_broadcaster.SetEventName(
eBroadcastInternalStateControlPause, "control-pause");
m_private_state_control_broadcaster.SetEventName(
eBroadcastInternalStateControlResume, "control-resume");
// The listener passed into process creation is the primary listener:
// It always listens for all the event bits for Process:
SetPrimaryListener(listener_sp);
m_private_state_listener_sp->StartListeningForEvents(
&m_private_state_broadcaster,
eBroadcastBitStateChanged | eBroadcastBitInterrupt);
m_private_state_listener_sp->StartListeningForEvents(
&m_private_state_control_broadcaster,
eBroadcastInternalStateControlStop | eBroadcastInternalStateControlPause |
eBroadcastInternalStateControlResume);
// We need something valid here, even if just the default UnixSignalsSP.
assert(m_unix_signals_sp && "null m_unix_signals_sp after initialization");
// Allow the platform to override the default cache line size
OptionValueSP value_sp =
m_collection_sp->GetPropertyAtIndex(ePropertyMemCacheLineSize)
->GetValue();
uint64_t platform_cache_line_size =
target_sp->GetPlatform()->GetDefaultMemoryCacheLineSize();
if (!value_sp->OptionWasSet() && platform_cache_line_size != 0)
value_sp->SetValueAs(platform_cache_line_size);
// FIXME: Frame recognizer registration should not be done in Target.
// We should have a plugin do the registration instead, for example, a
// common C LanguageRuntime plugin.
RegisterAssertFrameRecognizer(this);
RegisterVerboseTrapFrameRecognizer(*this);
}
Process::~Process() {
Log *log = GetLog(LLDBLog::Object);
LLDB_LOGF(log, "%p Process::~Process()", static_cast<void *>(this));
StopPrivateStateThread();
// ThreadList::Clear() will try to acquire this process's mutex, so
// explicitly clear the thread list here to ensure that the mutex is not
// destroyed before the thread list.
m_thread_list.Clear();
}
ProcessProperties &Process::GetGlobalProperties() {
// NOTE: intentional leak so we don't crash if global destructor chain gets
// called as other threads still use the result of this function
static ProcessProperties *g_settings_ptr =
new ProcessProperties(nullptr);
return *g_settings_ptr;
}
void Process::Finalize(bool destructing) {
if (m_finalizing.exchange(true))
return;
if (destructing)
m_destructing.exchange(true);
// Destroy the process. This will call the virtual function DoDestroy under
// the hood, giving our derived class a chance to do the ncessary tear down.
DestroyImpl(false);
// Clear our broadcaster before we proceed with destroying
Broadcaster::Clear();
// Do any cleanup needed prior to being destructed... Subclasses that
// override this method should call this superclass method as well.
// We need to destroy the loader before the derived Process class gets
// destroyed since it is very likely that undoing the loader will require
// access to the real process.
m_dynamic_checkers_up.reset();
m_abi_sp.reset();
m_os_up.reset();
m_system_runtime_up.reset();
m_dyld_up.reset();
m_jit_loaders_up.reset();
m_thread_plans.Clear();
m_thread_list_real.Destroy();
m_thread_list.Destroy();
m_extended_thread_list.Destroy();
m_queue_list.Clear();
m_queue_list_stop_id = 0;
m_watchpoint_resource_list.Clear();
std::vector<Notifications> empty_notifications;
m_notifications.swap(empty_notifications);
m_image_tokens.clear();
m_memory_cache.Clear();
m_allocated_memory_cache.Clear(/*deallocate_memory=*/true);
{
std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex);
m_language_runtimes.clear();
}
m_instrumentation_runtimes.clear();
m_next_event_action_up.reset();
// Clear the last natural stop ID since it has a strong reference to this
// process
m_mod_id.SetStopEventForLastNaturalStopID(EventSP());
// We have to be very careful here as the m_private_state_listener might
// contain events that have ProcessSP values in them which can keep this
// process around forever. These events need to be cleared out.
m_private_state_listener_sp->Clear();
m_public_run_lock.SetStopped();
m_private_run_lock.SetStopped();
m_structured_data_plugin_map.clear();
}
void Process::RegisterNotificationCallbacks(const Notifications &callbacks) {
m_notifications.push_back(callbacks);
if (callbacks.initialize != nullptr)
callbacks.initialize(callbacks.baton, this);
}
bool Process::UnregisterNotificationCallbacks(const Notifications &callbacks) {
std::vector<Notifications>::iterator pos, end = m_notifications.end();
for (pos = m_notifications.begin(); pos != end; ++pos) {
if (pos->baton == callbacks.baton &&
pos->initialize == callbacks.initialize &&
pos->process_state_changed == callbacks.process_state_changed) {
m_notifications.erase(pos);
return true;
}
}
return false;
}
void Process::SynchronouslyNotifyStateChanged(StateType state) {
std::vector<Notifications>::iterator notification_pos,
notification_end = m_notifications.end();
for (notification_pos = m_notifications.begin();
notification_pos != notification_end; ++notification_pos) {
if (notification_pos->process_state_changed)
notification_pos->process_state_changed(notification_pos->baton, this,
state);
}
}
// FIXME: We need to do some work on events before the general Listener sees
// them.
// For instance if we are continuing from a breakpoint, we need to ensure that
// we do the little "insert real insn, step & stop" trick. But we can't do
// that when the event is delivered by the broadcaster - since that is done on
// the thread that is waiting for new events, so if we needed more than one
// event for our handling, we would stall. So instead we do it when we fetch
// the event off of the queue.
//
StateType Process::GetNextEvent(EventSP &event_sp) {
StateType state = eStateInvalid;
if (GetPrimaryListener()->GetEventForBroadcaster(this, event_sp,
std::chrono::seconds(0)) &&
event_sp)
state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
return state;
}
void Process::SyncIOHandler(uint32_t iohandler_id,
const Timeout<std::micro> &timeout) {
// don't sync (potentially context switch) in case where there is no process
// IO
if (!ProcessIOHandlerExists())
return;
auto Result = m_iohandler_sync.WaitForValueNotEqualTo(iohandler_id, timeout);
Log *log = GetLog(LLDBLog::Process);
if (Result) {
LLDB_LOG(
log,
"waited from m_iohandler_sync to change from {0}. New value is {1}.",
iohandler_id, *Result);
} else {
LLDB_LOG(log, "timed out waiting for m_iohandler_sync to change from {0}.",
iohandler_id);
}
}
StateType Process::WaitForProcessToStop(
const Timeout<std::micro> &timeout, EventSP *event_sp_ptr, bool wait_always,
ListenerSP hijack_listener_sp, Stream *stream, bool use_run_lock,
SelectMostRelevant select_most_relevant) {
// We can't just wait for a "stopped" event, because the stopped event may
// have restarted the target. We have to actually check each event, and in
// the case of a stopped event check the restarted flag on the event.
if (event_sp_ptr)
event_sp_ptr->reset();
StateType state = GetState();
// If we are exited or detached, we won't ever get back to any other valid
// state...
if (state == eStateDetached || state == eStateExited)
return state;
Log *log = GetLog(LLDBLog::Process);
LLDB_LOG(log, "timeout = {0}", timeout);
if (!wait_always && StateIsStoppedState(state, true) &&
StateIsStoppedState(GetPrivateState(), true)) {
LLDB_LOGF(log,
"Process::%s returning without waiting for events; process "
"private and public states are already 'stopped'.",
__FUNCTION__);
// We need to toggle the run lock as this won't get done in
// SetPublicState() if the process is hijacked.
if (hijack_listener_sp && use_run_lock)
m_public_run_lock.SetStopped();
return state;
}
while (state != eStateInvalid) {
EventSP event_sp;
state = GetStateChangedEvents(event_sp, timeout, hijack_listener_sp);
if (event_sp_ptr && event_sp)
*event_sp_ptr = event_sp;
bool pop_process_io_handler = (hijack_listener_sp.get() != nullptr);
Process::HandleProcessStateChangedEvent(
event_sp, stream, select_most_relevant, pop_process_io_handler);
switch (state) {
case eStateCrashed:
case eStateDetached:
case eStateExited:
case eStateUnloaded:
// We need to toggle the run lock as this won't get done in
// SetPublicState() if the process is hijacked.
if (hijack_listener_sp && use_run_lock)
m_public_run_lock.SetStopped();
return state;
case eStateStopped:
if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get()))
continue;
else {
// We need to toggle the run lock as this won't get done in
// SetPublicState() if the process is hijacked.
if (hijack_listener_sp && use_run_lock)
m_public_run_lock.SetStopped();
return state;
}
default:
continue;
}
}
return state;
}
bool Process::HandleProcessStateChangedEvent(
const EventSP &event_sp, Stream *stream,
SelectMostRelevant select_most_relevant,
bool &pop_process_io_handler) {
const bool handle_pop = pop_process_io_handler;
pop_process_io_handler = false;
ProcessSP process_sp =
Process::ProcessEventData::GetProcessFromEvent(event_sp.get());
if (!process_sp)
return false;
StateType event_state =
Process::ProcessEventData::GetStateFromEvent(event_sp.get());
if (event_state == eStateInvalid)
return false;
switch (event_state) {
case eStateInvalid:
case eStateUnloaded:
case eStateAttaching:
case eStateLaunching:
case eStateStepping:
case eStateDetached:
if (stream)
stream->Printf("Process %" PRIu64 " %s\n", process_sp->GetID(),
StateAsCString(event_state));
if (event_state == eStateDetached)
pop_process_io_handler = true;
break;
case eStateConnected:
case eStateRunning:
// Don't be chatty when we run...
break;
case eStateExited:
if (stream)
process_sp->GetStatus(*stream);
pop_process_io_handler = true;
break;
case eStateStopped:
case eStateCrashed:
case eStateSuspended:
// Make sure the program hasn't been auto-restarted:
if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) {
if (stream) {
size_t num_reasons =
Process::ProcessEventData::GetNumRestartedReasons(event_sp.get());
if (num_reasons > 0) {
// FIXME: Do we want to report this, or would that just be annoyingly
// chatty?
if (num_reasons == 1) {
const char *reason =
Process::ProcessEventData::GetRestartedReasonAtIndex(
event_sp.get(), 0);
stream->Printf("Process %" PRIu64 " stopped and restarted: %s\n",
process_sp->GetID(),
reason ? reason : "<UNKNOWN REASON>");
} else {
stream->Printf("Process %" PRIu64
" stopped and restarted, reasons:\n",
process_sp->GetID());
for (size_t i = 0; i < num_reasons; i++) {
const char *reason =
Process::ProcessEventData::GetRestartedReasonAtIndex(
event_sp.get(), i);
stream->Printf("\t%s\n", reason ? reason : "<UNKNOWN REASON>");
}
}
}
}
} else {
StopInfoSP curr_thread_stop_info_sp;
// Lock the thread list so it doesn't change on us, this is the scope for
// the locker:
{
ThreadList &thread_list = process_sp->GetThreadList();
std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
ThreadSP curr_thread(thread_list.GetSelectedThread());
if (curr_thread && curr_thread->IsValid())
curr_thread_stop_info_sp = curr_thread->GetStopInfo();
bool prefer_curr_thread = curr_thread_stop_info_sp &&
curr_thread_stop_info_sp->ShouldSelect();
if (!prefer_curr_thread) {
// Prefer a thread that has just completed its plan over another
// thread as current thread.
ThreadSP plan_thread;
ThreadSP other_thread;
for (ThreadSP thread : thread_list.Threads()) {
StopInfoSP stop_info = thread->GetStopInfo();
if (!stop_info || !stop_info->ShouldSelect())
continue;
StopReason thread_stop_reason = stop_info->GetStopReason();
if (thread_stop_reason == eStopReasonPlanComplete) {
if (!plan_thread)
plan_thread = thread;
} else if (!other_thread) {
other_thread = thread;
}
}
if (plan_thread)
thread_list.SetSelectedThreadByID(plan_thread->GetID());
else if (other_thread)
thread_list.SetSelectedThreadByID(other_thread->GetID());
else {
ThreadSP thread;
if (curr_thread && curr_thread->IsValid())
thread = curr_thread;
else
thread = thread_list.GetThreadAtIndex(0);
if (thread)
thread_list.SetSelectedThreadByID(thread->GetID());
}
}
}
// Drop the ThreadList mutex by here, since GetThreadStatus below might
// have to run code, e.g. for Data formatters, and if we hold the
// ThreadList mutex, then the process is going to have a hard time
// restarting the process.
if (stream) {
Debugger &debugger = process_sp->GetTarget().GetDebugger();
if (debugger.GetTargetList().GetSelectedTarget().get() ==
&process_sp->GetTarget()) {
ThreadSP thread_sp = process_sp->GetThreadList().GetSelectedThread();
if (!thread_sp || !thread_sp->IsValid())
return false;
const bool only_threads_with_stop_reason = true;
const uint32_t start_frame =
thread_sp->GetSelectedFrameIndex(select_most_relevant);
const uint32_t num_frames = 1;
const uint32_t num_frames_with_source = 1;
const bool stop_format = true;
process_sp->GetStatus(*stream);
process_sp->GetThreadStatus(*stream, only_threads_with_stop_reason,
start_frame, num_frames,
num_frames_with_source,
stop_format);
if (curr_thread_stop_info_sp) {
lldb::addr_t crashing_address;
ValueObjectSP valobj_sp = StopInfo::GetCrashingDereference(
curr_thread_stop_info_sp, &crashing_address);
if (valobj_sp) {
const ValueObject::GetExpressionPathFormat format =
ValueObject::GetExpressionPathFormat::
eGetExpressionPathFormatHonorPointers;
stream->PutCString("Likely cause: ");
valobj_sp->GetExpressionPath(*stream, format);
stream->Printf(" accessed 0x%" PRIx64 "\n", crashing_address);
}
}
} else {
uint32_t target_idx = debugger.GetTargetList().GetIndexOfTarget(
process_sp->GetTarget().shared_from_this());
if (target_idx != UINT32_MAX)
stream->Printf("Target %d: (", target_idx);
else
stream->Printf("Target <unknown index>: (");
process_sp->GetTarget().Dump(stream, eDescriptionLevelBrief);
stream->Printf(") stopped.\n");
}
}
// Pop the process IO handler
pop_process_io_handler = true;
}
break;
}
if (handle_pop && pop_process_io_handler)
process_sp->PopProcessIOHandler();
return true;
}
bool Process::HijackProcessEvents(ListenerSP listener_sp) {
if (listener_sp) {
return HijackBroadcaster(listener_sp, eBroadcastBitStateChanged |
eBroadcastBitInterrupt);
} else
return false;
}
void Process::RestoreProcessEvents() { RestoreBroadcaster(); }
StateType Process::GetStateChangedEvents(EventSP &event_sp,
const Timeout<std::micro> &timeout,
ListenerSP hijack_listener_sp) {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOG(log, "timeout = {0}, event_sp)...", timeout);
ListenerSP listener_sp = hijack_listener_sp;
if (!listener_sp)
listener_sp = GetPrimaryListener();
StateType state = eStateInvalid;
if (listener_sp->GetEventForBroadcasterWithType(
this, eBroadcastBitStateChanged | eBroadcastBitInterrupt, event_sp,
timeout)) {
if (event_sp && event_sp->GetType() == eBroadcastBitStateChanged)
state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
else
LLDB_LOG(log, "got no event or was interrupted.");
}
LLDB_LOG(log, "timeout = {0}, event_sp) => {1}", timeout, state);
return state;
}
Event *Process::PeekAtStateChangedEvents() {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOGF(log, "Process::%s...", __FUNCTION__);
Event *event_ptr;
event_ptr = GetPrimaryListener()->PeekAtNextEventForBroadcasterWithType(
this, eBroadcastBitStateChanged);
if (log) {
if (event_ptr) {
LLDB_LOGF(log, "Process::%s (event_ptr) => %s", __FUNCTION__,
StateAsCString(ProcessEventData::GetStateFromEvent(event_ptr)));
} else {
LLDB_LOGF(log, "Process::%s no events found", __FUNCTION__);
}
}
return event_ptr;
}
StateType
Process::GetStateChangedEventsPrivate(EventSP &event_sp,
const Timeout<std::micro> &timeout) {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOG(log, "timeout = {0}, event_sp)...", timeout);
StateType state = eStateInvalid;
if (m_private_state_listener_sp->GetEventForBroadcasterWithType(
&m_private_state_broadcaster,
eBroadcastBitStateChanged | eBroadcastBitInterrupt, event_sp,
timeout))
if (event_sp && event_sp->GetType() == eBroadcastBitStateChanged)
state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
LLDB_LOG(log, "timeout = {0}, event_sp) => {1}", timeout,
state == eStateInvalid ? "TIMEOUT" : StateAsCString(state));
return state;
}
bool Process::GetEventsPrivate(EventSP &event_sp,
const Timeout<std::micro> &timeout,
bool control_only) {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOG(log, "timeout = {0}, event_sp)...", timeout);
if (control_only)
return m_private_state_listener_sp->GetEventForBroadcaster(
&m_private_state_control_broadcaster, event_sp, timeout);
else
return m_private_state_listener_sp->GetEvent(event_sp, timeout);
}
bool Process::IsRunning() const {
return StateIsRunningState(m_public_state.GetValue());
}
int Process::GetExitStatus() {
std::lock_guard<std::mutex> guard(m_exit_status_mutex);
if (m_public_state.GetValue() == eStateExited)
return m_exit_status;
return -1;
}
const char *Process::GetExitDescription() {
std::lock_guard<std::mutex> guard(m_exit_status_mutex);
if (m_public_state.GetValue() == eStateExited && !m_exit_string.empty())
return m_exit_string.c_str();
return nullptr;
}
bool Process::SetExitStatus(int status, llvm::StringRef exit_string) {
// Use a mutex to protect setting the exit status.
std::lock_guard<std::mutex> guard(m_exit_status_mutex);
Log *log(GetLog(LLDBLog::State | LLDBLog::Process));
LLDB_LOG(log, "(plugin = {0} status = {1} ({1:x8}), description=\"{2}\")",
GetPluginName(), status, exit_string);
// We were already in the exited state
if (m_private_state.GetValue() == eStateExited) {
LLDB_LOG(
log,
"(plugin = {0}) ignoring exit status because state was already set "
"to eStateExited",
GetPluginName());
return false;
}
telemetry::ScopedDispatcher<telemetry::ProcessExitInfo> helper;
UUID module_uuid;
// Need this check because the pointer may not be valid at this point.
if (TargetSP target_sp = m_target_wp.lock()) {
helper.SetDebugger(&target_sp->GetDebugger());
if (ModuleSP mod = target_sp->GetExecutableModule())
module_uuid = mod->GetUUID();
}
helper.DispatchNow([&](telemetry::ProcessExitInfo *info) {
info->module_uuid = module_uuid;
info->pid = m_pid;
info->is_start_entry = true;
info->exit_desc = {status, exit_string.str()};
});
helper.DispatchOnExit(
[module_uuid, pid = m_pid](telemetry::ProcessExitInfo *info) {
info->module_uuid = module_uuid;
info->pid = pid;
});
m_exit_status = status;
if (!exit_string.empty())
m_exit_string = exit_string.str();
else
m_exit_string.clear();
// Clear the last natural stop ID since it has a strong reference to this
// process
m_mod_id.SetStopEventForLastNaturalStopID(EventSP());
SetPrivateState(eStateExited);
// Allow subclasses to do some cleanup
DidExit();
return true;
}
bool Process::IsAlive() {
switch (m_private_state.GetValue()) {
case eStateConnected:
case eStateAttaching:
case eStateLaunching:
case eStateStopped:
case eStateRunning:
case eStateStepping:
case eStateCrashed:
case eStateSuspended:
return true;
default:
return false;
}
}
// This static callback can be used to watch for local child processes on the
// current host. The child process exits, the process will be found in the
// global target list (we want to be completely sure that the
// lldb_private::Process doesn't go away before we can deliver the signal.
bool Process::SetProcessExitStatus(
lldb::pid_t pid, bool exited,
int signo, // Zero for no signal
int exit_status // Exit value of process if signal is zero
) {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOGF(log,
"Process::SetProcessExitStatus (pid=%" PRIu64
", exited=%i, signal=%i, exit_status=%i)\n",
pid, exited, signo, exit_status);
if (exited) {
TargetSP target_sp(Debugger::FindTargetWithProcessID(pid));
if (target_sp) {
ProcessSP process_sp(target_sp->GetProcessSP());
if (process_sp) {
llvm::StringRef signal_str =
process_sp->GetUnixSignals()->GetSignalAsStringRef(signo);
process_sp->SetExitStatus(exit_status, signal_str);
}
}
return true;
}
return false;
}
bool Process::UpdateThreadList(ThreadList &old_thread_list,
ThreadList &new_thread_list) {
m_thread_plans.ClearThreadCache();
return DoUpdateThreadList(old_thread_list, new_thread_list);
}
void Process::UpdateThreadListIfNeeded() {
const uint32_t stop_id = GetStopID();
if (m_thread_list.GetSize(false) == 0 ||
stop_id != m_thread_list.GetStopID()) {
bool clear_unused_threads = true;
const StateType state = GetPrivateState();
if (StateIsStoppedState(state, true)) {
std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
m_thread_list.SetStopID(stop_id);
// m_thread_list does have its own mutex, but we need to hold onto the
// mutex between the call to UpdateThreadList(...) and the
// os->UpdateThreadList(...) so it doesn't change on us
ThreadList &old_thread_list = m_thread_list;
ThreadList real_thread_list(*this);
ThreadList new_thread_list(*this);
// Always update the thread list with the protocol specific thread list,
// but only update if "true" is returned
if (UpdateThreadList(m_thread_list_real, real_thread_list)) {
// Don't call into the OperatingSystem to update the thread list if we
// are shutting down, since that may call back into the SBAPI's,
// requiring the API lock which is already held by whoever is shutting
// us down, causing a deadlock.
OperatingSystem *os = GetOperatingSystem();
if (os && !m_destroy_in_process) {
// Clear any old backing threads where memory threads might have been
// backed by actual threads from the lldb_private::Process subclass
size_t num_old_threads = old_thread_list.GetSize(false);
for (size_t i = 0; i < num_old_threads; ++i)
old_thread_list.GetThreadAtIndex(i, false)->ClearBackingThread();
// See if the OS plugin reports all threads. If it does, then
// it is safe to clear unseen thread's plans here. Otherwise we
// should preserve them in case they show up again:
clear_unused_threads = os->DoesPluginReportAllThreads();
// Turn off dynamic types to ensure we don't run any expressions.
// Objective-C can run an expression to determine if a SBValue is a
// dynamic type or not and we need to avoid this. OperatingSystem
// plug-ins can't run expressions that require running code...
Target &target = GetTarget();
const lldb::DynamicValueType saved_prefer_dynamic =
target.GetPreferDynamicValue();
if (saved_prefer_dynamic != lldb::eNoDynamicValues)
target.SetPreferDynamicValue(lldb::eNoDynamicValues);
// Now let the OperatingSystem plug-in update the thread list
os->UpdateThreadList(
old_thread_list, // Old list full of threads created by OS plug-in
real_thread_list, // The actual thread list full of threads
// created by each lldb_private::Process
// subclass
new_thread_list); // The new thread list that we will show to the
// user that gets filled in
if (saved_prefer_dynamic != lldb::eNoDynamicValues)
target.SetPreferDynamicValue(saved_prefer_dynamic);
} else {
// No OS plug-in, the new thread list is the same as the real thread
// list.
new_thread_list = real_thread_list;
}
m_thread_list_real.Update(real_thread_list);
m_thread_list.Update(new_thread_list);
m_thread_list.SetStopID(stop_id);
if (GetLastNaturalStopID() != m_extended_thread_stop_id) {
// Clear any extended threads that we may have accumulated previously
m_extended_thread_list.Clear();
m_extended_thread_stop_id = GetLastNaturalStopID();
m_queue_list.Clear();
m_queue_list_stop_id = GetLastNaturalStopID();
}
}
// Now update the plan stack map.
// If we do have an OS plugin, any absent real threads in the
// m_thread_list have already been removed from the ThreadPlanStackMap.
// So any remaining threads are OS Plugin threads, and those we want to
// preserve in case they show up again.
m_thread_plans.Update(m_thread_list, clear_unused_threads);
}
}
}
ThreadPlanStack *Process::FindThreadPlans(lldb::tid_t tid) {
return m_thread_plans.Find(tid);
}
bool Process::PruneThreadPlansForTID(lldb::tid_t tid) {
return m_thread_plans.PrunePlansForTID(tid);
}
void Process::PruneThreadPlans() {
m_thread_plans.Update(GetThreadList(), true, false);
}
bool Process::DumpThreadPlansForTID(Stream &strm, lldb::tid_t tid,
lldb::DescriptionLevel desc_level,
bool internal, bool condense_trivial,
bool skip_unreported_plans) {
return m_thread_plans.DumpPlansForTID(
strm, tid, desc_level, internal, condense_trivial, skip_unreported_plans);
}
void Process::DumpThreadPlans(Stream &strm, lldb::DescriptionLevel desc_level,
bool internal, bool condense_trivial,
bool skip_unreported_plans) {
m_thread_plans.DumpPlans(strm, desc_level, internal, condense_trivial,
skip_unreported_plans);
}
void Process::UpdateQueueListIfNeeded() {
if (m_system_runtime_up) {
if (m_queue_list.GetSize() == 0 ||
m_queue_list_stop_id != GetLastNaturalStopID()) {
const StateType state = GetPrivateState();
if (StateIsStoppedState(state, true)) {
m_system_runtime_up->PopulateQueueList(m_queue_list);
m_queue_list_stop_id = GetLastNaturalStopID();
}
}
}
}
ThreadSP Process::CreateOSPluginThread(lldb::tid_t tid, lldb::addr_t context) {
OperatingSystem *os = GetOperatingSystem();
if (os)
return os->CreateThread(tid, context);
return ThreadSP();
}
uint32_t Process::GetNextThreadIndexID(uint64_t thread_id) {
return AssignIndexIDToThread(thread_id);
}
bool Process::HasAssignedIndexIDToThread(uint64_t thread_id) {
return (m_thread_id_to_index_id_map.find(thread_id) !=
m_thread_id_to_index_id_map.end());
}
uint32_t Process::AssignIndexIDToThread(uint64_t thread_id) {
auto [iterator, inserted] =
m_thread_id_to_index_id_map.try_emplace(thread_id, m_thread_index_id + 1);
if (inserted)
++m_thread_index_id;
return iterator->second;
}
StateType Process::GetState() {
if (CurrentThreadPosesAsPrivateStateThread())
return m_private_state.GetValue();
else
return m_public_state.GetValue();
}
void Process::SetPublicState(StateType new_state, bool restarted) {
const bool new_state_is_stopped = StateIsStoppedState(new_state, false);
if (new_state_is_stopped) {
// This will only set the time if the public stop time has no value, so
// it is ok to call this multiple times. With a public stop we can't look
// at the stop ID because many private stops might have happened, so we
// can't check for a stop ID of zero. This allows the "statistics" command
// to dump the time it takes to reach somewhere in your code, like a
// breakpoint you set.
GetTarget().GetStatistics().SetFirstPublicStopTime();
}
Log *log(GetLog(LLDBLog::State | LLDBLog::Process));
LLDB_LOGF(log, "(plugin = %s, state = %s, restarted = %i)",
GetPluginName().data(), StateAsCString(new_state), restarted);
const StateType old_state = m_public_state.GetValue();
m_public_state.SetValue(new_state);
// On the transition from Run to Stopped, we unlock the writer end of the run
// lock. The lock gets locked in Resume, which is the public API to tell the
// program to run.
if (!StateChangedIsExternallyHijacked()) {
if (new_state == eStateDetached) {
LLDB_LOGF(log,
"(plugin = %s, state = %s) -- unlocking run lock for detach",
GetPluginName().data(), StateAsCString(new_state));
m_public_run_lock.SetStopped();
} else {
const bool old_state_is_stopped = StateIsStoppedState(old_state, false);
if ((old_state_is_stopped != new_state_is_stopped)) {
if (new_state_is_stopped && !restarted) {
LLDB_LOGF(log, "(plugin = %s, state = %s) -- unlocking run lock",
GetPluginName().data(), StateAsCString(new_state));
m_public_run_lock.SetStopped();
}
}
}
}
}
Status Process::Resume() {
Log *log(GetLog(LLDBLog::State | LLDBLog::Process));
LLDB_LOGF(log, "(plugin = %s) -- locking run lock", GetPluginName().data());
if (!m_public_run_lock.SetRunning()) {
LLDB_LOGF(log, "(plugin = %s) -- SetRunning failed, not resuming.",
GetPluginName().data());
return Status::FromErrorString(
"resume request failed - process already running");
}
Status error = PrivateResume();
if (!error.Success()) {
// Undo running state change
m_public_run_lock.SetStopped();
}
return error;
}
Status Process::ResumeSynchronous(Stream *stream) {
Log *log(GetLog(LLDBLog::State | LLDBLog::Process));
LLDB_LOGF(log, "Process::ResumeSynchronous -- locking run lock");
if (!m_public_run_lock.SetRunning()) {
LLDB_LOGF(log, "Process::Resume: -- SetRunning failed, not resuming.");
return Status::FromErrorString(
"resume request failed: process already running");
}
ListenerSP listener_sp(
Listener::MakeListener(ResumeSynchronousHijackListenerName.data()));
HijackProcessEvents(listener_sp);
Status error = PrivateResume();
if (error.Success()) {
StateType state =
WaitForProcessToStop(std::nullopt, nullptr, true, listener_sp, stream,
true /* use_run_lock */, SelectMostRelevantFrame);
const bool must_be_alive =
false; // eStateExited is ok, so this must be false
if (!StateIsStoppedState(state, must_be_alive))
error = Status::FromErrorStringWithFormat(
"process not in stopped state after synchronous resume: %s",
StateAsCString(state));
} else {
// Undo running state change
m_public_run_lock.SetStopped();
}
// Undo the hijacking of process events...
RestoreProcessEvents();
return error;
}
bool Process::StateChangedIsExternallyHijacked() {
if (IsHijackedForEvent(eBroadcastBitStateChanged)) {
llvm::StringRef hijacking_name = GetHijackingListenerName();
if (!hijacking_name.starts_with("lldb.internal"))
return true;
}
return false;
}
bool Process::StateChangedIsHijackedForSynchronousResume() {
if (IsHijackedForEvent(eBroadcastBitStateChanged)) {
llvm::StringRef hijacking_name = GetHijackingListenerName();
if (hijacking_name == ResumeSynchronousHijackListenerName)
return true;
}
return false;
}
StateType Process::GetPrivateState() { return m_private_state.GetValue(); }
void Process::SetPrivateState(StateType new_state) {
// Use m_destructing not m_finalizing here. If we are finalizing a process
// that we haven't started tearing down, we'd like to be able to nicely
// detach if asked, but that requires the event system be live. That will
// not be true for an in-the-middle-of-being-destructed Process, since the
// event system relies on Process::shared_from_this, which may have already
// been destroyed.
if (m_destructing)
return;
Log *log(GetLog(LLDBLog::State | LLDBLog::Process | LLDBLog::Unwind));
bool state_changed = false;
LLDB_LOGF(log, "(plugin = %s, state = %s)", GetPluginName().data(),
StateAsCString(new_state));
std::lock_guard<std::recursive_mutex> thread_guard(m_thread_list.GetMutex());
std::lock_guard<std::recursive_mutex> guard(m_private_state.GetMutex());
const StateType old_state = m_private_state.GetValueNoLock();
state_changed = old_state != new_state;
const bool old_state_is_stopped = StateIsStoppedState(old_state, false);
const bool new_state_is_stopped = StateIsStoppedState(new_state, false);
if (old_state_is_stopped != new_state_is_stopped) {
if (new_state_is_stopped)
m_private_run_lock.SetStopped();
else
m_private_run_lock.SetRunning();
}
if (state_changed) {
m_private_state.SetValueNoLock(new_state);
EventSP event_sp(
new Event(eBroadcastBitStateChanged,
new ProcessEventData(shared_from_this(), new_state)));
if (StateIsStoppedState(new_state, false)) {
// Note, this currently assumes that all threads in the list stop when
// the process stops. In the future we will want to support a debugging
// model where some threads continue to run while others are stopped.
// When that happens we will either need a way for the thread list to
// identify which threads are stopping or create a special thread list
// containing only threads which actually stopped.
//
// The process plugin is responsible for managing the actual behavior of
// the threads and should have stopped any threads that are going to stop
// before we get here.
m_thread_list.DidStop();
if (m_mod_id.BumpStopID() == 0)
GetTarget().GetStatistics().SetFirstPrivateStopTime();
if (!m_mod_id.IsLastResumeForUserExpression())
m_mod_id.SetStopEventForLastNaturalStopID(event_sp);
m_memory_cache.Clear();
LLDB_LOGF(log, "(plugin = %s, state = %s, stop_id = %u",
GetPluginName().data(), StateAsCString(new_state),
m_mod_id.GetStopID());
}
m_private_state_broadcaster.BroadcastEvent(event_sp);
} else {
LLDB_LOGF(log, "(plugin = %s, state = %s) state didn't change. Ignoring...",
GetPluginName().data(), StateAsCString(new_state));
}
}
void Process::SetRunningUserExpression(bool on) {
m_mod_id.SetRunningUserExpression(on);
}
void Process::SetRunningUtilityFunction(bool on) {
m_mod_id.SetRunningUtilityFunction(on);
}
addr_t Process::GetImageInfoAddress() { return LLDB_INVALID_ADDRESS; }
const lldb::ABISP &Process::GetABI() {
if (!m_abi_sp)
m_abi_sp = ABI::FindPlugin(shared_from_this(), GetTarget().GetArchitecture());
return m_abi_sp;
}
std::vector<LanguageRuntime *> Process::GetLanguageRuntimes() {
std::vector<LanguageRuntime *> language_runtimes;
if (m_finalizing)
return language_runtimes;
std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex);
// Before we pass off a copy of the language runtimes, we must make sure that
// our collection is properly populated. It's possible that some of the
// language runtimes were not loaded yet, either because nobody requested it
// yet or the proper condition for loading wasn't yet met (e.g. libc++.so
// hadn't been loaded).
for (const lldb::LanguageType lang_type : Language::GetSupportedLanguages()) {
if (LanguageRuntime *runtime = GetLanguageRuntime(lang_type))
language_runtimes.emplace_back(runtime);
}
return language_runtimes;
}
LanguageRuntime *Process::GetLanguageRuntime(lldb::LanguageType language) {
if (m_finalizing)
return nullptr;
LanguageRuntime *runtime = nullptr;
std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex);
LanguageRuntimeCollection::iterator pos;
pos = m_language_runtimes.find(language);
if (pos == m_language_runtimes.end() || !pos->second) {
lldb::LanguageRuntimeSP runtime_sp(
LanguageRuntime::FindPlugin(this, language));
m_language_runtimes[language] = runtime_sp;
runtime = runtime_sp.get();
} else
runtime = pos->second.get();
if (runtime)
// It's possible that a language runtime can support multiple LanguageTypes,
// for example, CPPLanguageRuntime will support eLanguageTypeC_plus_plus,
// eLanguageTypeC_plus_plus_03, etc. Because of this, we should get the
// primary language type and make sure that our runtime supports it.
assert(runtime->GetLanguageType() == Language::GetPrimaryLanguage(language));
return runtime;
}
bool Process::IsPossibleDynamicValue(ValueObject &in_value) {
if (m_finalizing)
return false;
if (in_value.IsDynamic())
return false;
LanguageType known_type = in_value.GetObjectRuntimeLanguage();
if (known_type != eLanguageTypeUnknown && known_type != eLanguageTypeC) {
LanguageRuntime *runtime = GetLanguageRuntime(known_type);
return runtime ? runtime->CouldHaveDynamicValue(in_value) : false;
}
for (LanguageRuntime *runtime : GetLanguageRuntimes()) {
if (runtime->CouldHaveDynamicValue(in_value))
return true;
}
return false;
}
void Process::SetDynamicCheckers(DynamicCheckerFunctions *dynamic_checkers) {
m_dynamic_checkers_up.reset(dynamic_checkers);
}
StopPointSiteList<BreakpointSite> &Process::GetBreakpointSiteList() {
return m_breakpoint_site_list;
}
const StopPointSiteList<BreakpointSite> &
Process::GetBreakpointSiteList() const {
return m_breakpoint_site_list;
}
void Process::DisableAllBreakpointSites() {
m_breakpoint_site_list.ForEach([this](BreakpointSite *bp_site) -> void {
// bp_site->SetEnabled(true);
DisableBreakpointSite(bp_site);
});
}
Status Process::ClearBreakpointSiteByID(lldb::user_id_t break_id) {
Status error(DisableBreakpointSiteByID(break_id));
if (error.Success())
m_breakpoint_site_list.Remove(break_id);
return error;
}
Status Process::DisableBreakpointSiteByID(lldb::user_id_t break_id) {
Status error;
BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID(break_id);
if (bp_site_sp) {
if (bp_site_sp->IsEnabled())
error = DisableBreakpointSite(bp_site_sp.get());
} else {
error = Status::FromErrorStringWithFormat(
"invalid breakpoint site ID: %" PRIu64, break_id);
}
return error;
}
Status Process::EnableBreakpointSiteByID(lldb::user_id_t break_id) {
Status error;
BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID(break_id);
if (bp_site_sp) {
if (!bp_site_sp->IsEnabled())
error = EnableBreakpointSite(bp_site_sp.get());
} else {
error = Status::FromErrorStringWithFormat(
"invalid breakpoint site ID: %" PRIu64, break_id);
}
return error;
}
lldb::break_id_t
Process::CreateBreakpointSite(const BreakpointLocationSP &constituent,
bool use_hardware) {
addr_t load_addr = LLDB_INVALID_ADDRESS;
bool show_error = true;
switch (GetState()) {
case eStateInvalid:
case eStateUnloaded:
case eStateConnected:
case eStateAttaching:
case eStateLaunching:
case eStateDetached:
case eStateExited:
show_error = false;
break;
case eStateStopped:
case eStateRunning:
case eStateStepping:
case eStateCrashed:
case eStateSuspended:
show_error = IsAlive();
break;
}
// Reset the IsIndirect flag here, in case the location changes from pointing
// to a indirect symbol to a regular symbol.
constituent->SetIsIndirect(false);
if (constituent->ShouldResolveIndirectFunctions()) {
Symbol *symbol = constituent->GetAddress().CalculateSymbolContextSymbol();
if (symbol && symbol->IsIndirect()) {
Status error;
Address symbol_address = symbol->GetAddress();
load_addr = ResolveIndirectFunction(&symbol_address, error);
if (!error.Success() && show_error) {
GetTarget().GetDebugger().GetAsyncErrorStream()->Printf(
"warning: failed to resolve indirect function at 0x%" PRIx64
" for breakpoint %i.%i: %s\n",
symbol->GetLoadAddress(&GetTarget()),
constituent->GetBreakpoint().GetID(), constituent->GetID(),
error.AsCString() ? error.AsCString() : "unknown error");
return LLDB_INVALID_BREAK_ID;
}
Address resolved_address(load_addr);
load_addr = resolved_address.GetOpcodeLoadAddress(&GetTarget());
constituent->SetIsIndirect(true);
} else
load_addr = constituent->GetAddress().GetOpcodeLoadAddress(&GetTarget());
} else
load_addr = constituent->GetAddress().GetOpcodeLoadAddress(&GetTarget());
if (load_addr != LLDB_INVALID_ADDRESS) {
BreakpointSiteSP bp_site_sp;
// Look up this breakpoint site. If it exists, then add this new
// constituent, otherwise create a new breakpoint site and add it.
bp_site_sp = m_breakpoint_site_list.FindByAddress(load_addr);
if (bp_site_sp) {
bp_site_sp->AddConstituent(constituent);
constituent->SetBreakpointSite(bp_site_sp);
return bp_site_sp->GetID();
} else {
bp_site_sp.reset(
new BreakpointSite(constituent, load_addr, use_hardware));
if (bp_site_sp) {
Status error = EnableBreakpointSite(bp_site_sp.get());
if (error.Success()) {
constituent->SetBreakpointSite(bp_site_sp);
return m_breakpoint_site_list.Add(bp_site_sp);
} else {
if (show_error || use_hardware) {
// Report error for setting breakpoint...
GetTarget().GetDebugger().GetAsyncErrorStream()->Printf(
"warning: failed to set breakpoint site at 0x%" PRIx64
" for breakpoint %i.%i: %s\n",
load_addr, constituent->GetBreakpoint().GetID(),
constituent->GetID(),
error.AsCString() ? error.AsCString() : "unknown error");
}
}
}
}
}
// We failed to enable the breakpoint
return LLDB_INVALID_BREAK_ID;
}
void Process::RemoveConstituentFromBreakpointSite(
lldb::user_id_t constituent_id, lldb::user_id_t constituent_loc_id,
BreakpointSiteSP &bp_site_sp) {
uint32_t num_constituents =
bp_site_sp->RemoveConstituent(constituent_id, constituent_loc_id);
if (num_constituents == 0) {
// Don't try to disable the site if we don't have a live process anymore.
if (IsAlive())
DisableBreakpointSite(bp_site_sp.get());
m_breakpoint_site_list.RemoveByAddress(bp_site_sp->GetLoadAddress());
}
}
size_t Process::RemoveBreakpointOpcodesFromBuffer(addr_t bp_addr, size_t size,
uint8_t *buf) const {
size_t bytes_removed = 0;
StopPointSiteList<BreakpointSite> bp_sites_in_range;
if (m_breakpoint_site_list.FindInRange(bp_addr, bp_addr + size,
bp_sites_in_range)) {
bp_sites_in_range.ForEach([bp_addr, size,
buf](BreakpointSite *bp_site) -> void {
if (bp_site->GetType() == BreakpointSite::eSoftware) {
addr_t intersect_addr;
size_t intersect_size;
size_t opcode_offset;
if (bp_site->IntersectsRange(bp_addr, size, &intersect_addr,
&intersect_size, &opcode_offset)) {
assert(bp_addr <= intersect_addr && intersect_addr < bp_addr + size);
assert(bp_addr < intersect_addr + intersect_size &&
intersect_addr + intersect_size <= bp_addr + size);
assert(opcode_offset + intersect_size <= bp_site->GetByteSize());
size_t buf_offset = intersect_addr - bp_addr;
::memcpy(buf + buf_offset,
bp_site->GetSavedOpcodeBytes() + opcode_offset,
intersect_size);
}
}
});
}
return bytes_removed;
}
size_t Process::GetSoftwareBreakpointTrapOpcode(BreakpointSite *bp_site) {
PlatformSP platform_sp(GetTarget().GetPlatform());
if (platform_sp)
return platform_sp->GetSoftwareBreakpointTrapOpcode(GetTarget(), bp_site);
return 0;
}
Status Process::EnableSoftwareBreakpoint(BreakpointSite *bp_site) {
Status error;
assert(bp_site != nullptr);
Log *log = GetLog(LLDBLog::Breakpoints);
const addr_t bp_addr = bp_site->GetLoadAddress();
LLDB_LOGF(
log, "Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64,
bp_site->GetID(), (uint64_t)bp_addr);
if (bp_site->IsEnabled()) {
LLDB_LOGF(
log,
"Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64
" -- already enabled",
bp_site->GetID(), (uint64_t)bp_addr);
return error;
}
if (bp_addr == LLDB_INVALID_ADDRESS) {
error = Status::FromErrorString(
"BreakpointSite contains an invalid load address.");
return error;
}
// Ask the lldb::Process subclass to fill in the correct software breakpoint
// trap for the breakpoint site
const size_t bp_opcode_size = GetSoftwareBreakpointTrapOpcode(bp_site);
if (bp_opcode_size == 0) {
error = Status::FromErrorStringWithFormat(
"Process::GetSoftwareBreakpointTrapOpcode() "
"returned zero, unable to get breakpoint "
"trap for address 0x%" PRIx64,
bp_addr);
} else {
const uint8_t *const bp_opcode_bytes = bp_site->GetTrapOpcodeBytes();
if (bp_opcode_bytes == nullptr) {
error = Status::FromErrorString(
"BreakpointSite doesn't contain a valid breakpoint trap opcode.");
return error;
}
// Save the original opcode by reading it
if (DoReadMemory(bp_addr, bp_site->GetSavedOpcodeBytes(), bp_opcode_size,
error) == bp_opcode_size) {
// Write a software breakpoint in place of the original opcode
if (DoWriteMemory(bp_addr, bp_opcode_bytes, bp_opcode_size, error) ==
bp_opcode_size) {
uint8_t verify_bp_opcode_bytes[64];
if (DoReadMemory(bp_addr, verify_bp_opcode_bytes, bp_opcode_size,
error) == bp_opcode_size) {
if (::memcmp(bp_opcode_bytes, verify_bp_opcode_bytes,
bp_opcode_size) == 0) {
bp_site->SetEnabled(true);
bp_site->SetType(BreakpointSite::eSoftware);
LLDB_LOGF(log,
"Process::EnableSoftwareBreakpoint (site_id = %d) "
"addr = 0x%" PRIx64 " -- SUCCESS",
bp_site->GetID(), (uint64_t)bp_addr);
} else
error = Status::FromErrorString(
"failed to verify the breakpoint trap in memory.");
} else
error = Status::FromErrorString(
"Unable to read memory to verify breakpoint trap.");
} else
error = Status::FromErrorString(
"Unable to write breakpoint trap to memory.");
} else
error = Status::FromErrorString(
"Unable to read memory at breakpoint address.");
}
if (log && error.Fail())
LLDB_LOGF(
log,
"Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64
" -- FAILED: %s",
bp_site->GetID(), (uint64_t)bp_addr, error.AsCString());
return error;
}
Status Process::DisableSoftwareBreakpoint(BreakpointSite *bp_site) {
Status error;
assert(bp_site != nullptr);
Log *log = GetLog(LLDBLog::Breakpoints);
addr_t bp_addr = bp_site->GetLoadAddress();
lldb::user_id_t breakID = bp_site->GetID();
LLDB_LOGF(log,
"Process::DisableSoftwareBreakpoint (breakID = %" PRIu64
") addr = 0x%" PRIx64,
breakID, (uint64_t)bp_addr);
if (bp_site->IsHardware()) {
error =
Status::FromErrorString("Breakpoint site is a hardware breakpoint.");
} else if (bp_site->IsEnabled()) {
const size_t break_op_size = bp_site->GetByteSize();
const uint8_t *const break_op = bp_site->GetTrapOpcodeBytes();
if (break_op_size > 0) {
// Clear a software breakpoint instruction
uint8_t curr_break_op[8];
assert(break_op_size <= sizeof(curr_break_op));
bool break_op_found = false;
// Read the breakpoint opcode
if (DoReadMemory(bp_addr, curr_break_op, break_op_size, error) ==
break_op_size) {
bool verify = false;
// Make sure the breakpoint opcode exists at this address
if (::memcmp(curr_break_op, break_op, break_op_size) == 0) {
break_op_found = true;
// We found a valid breakpoint opcode at this address, now restore
// the saved opcode.
if (DoWriteMemory(bp_addr, bp_site->GetSavedOpcodeBytes(),
break_op_size, error) == break_op_size) {
verify = true;
} else
error = Status::FromErrorString(
"Memory write failed when restoring original opcode.");
} else {
error = Status::FromErrorString(
"Original breakpoint trap is no longer in memory.");
// Set verify to true and so we can check if the original opcode has
// already been restored
verify = true;
}
if (verify) {
uint8_t verify_opcode[8];
assert(break_op_size < sizeof(verify_opcode));
// Verify that our original opcode made it back to the inferior
if (DoReadMemory(bp_addr, verify_opcode, break_op_size, error) ==
break_op_size) {
// compare the memory we just read with the original opcode
if (::memcmp(bp_site->GetSavedOpcodeBytes(), verify_opcode,
break_op_size) == 0) {
// SUCCESS
bp_site->SetEnabled(false);
LLDB_LOGF(log,
"Process::DisableSoftwareBreakpoint (site_id = %d) "
"addr = 0x%" PRIx64 " -- SUCCESS",
bp_site->GetID(), (uint64_t)bp_addr);
return error;
} else {
if (break_op_found)
error = Status::FromErrorString(
"Failed to restore original opcode.");
}
} else
error =
Status::FromErrorString("Failed to read memory to verify that "
"breakpoint trap was restored.");
}
} else
error = Status::FromErrorString(
"Unable to read memory that should contain the breakpoint trap.");
}
} else {
LLDB_LOGF(
log,
"Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64
" -- already disabled",
bp_site->GetID(), (uint64_t)bp_addr);
return error;
}
LLDB_LOGF(
log,
"Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64
" -- FAILED: %s",
bp_site->GetID(), (uint64_t)bp_addr, error.AsCString());
return error;
}
// Uncomment to verify memory caching works after making changes to caching
// code
//#define VERIFY_MEMORY_READS
size_t Process::ReadMemory(addr_t addr, void *buf, size_t size, Status &error) {
if (ABISP abi_sp = GetABI())
addr = abi_sp->FixAnyAddress(addr);
error.Clear();
if (!GetDisableMemoryCache()) {
#if defined(VERIFY_MEMORY_READS)
// Memory caching is enabled, with debug verification
if (buf && size) {
// Uncomment the line below to make sure memory caching is working.
// I ran this through the test suite and got no assertions, so I am
// pretty confident this is working well. If any changes are made to
// memory caching, uncomment the line below and test your changes!
// Verify all memory reads by using the cache first, then redundantly
// reading the same memory from the inferior and comparing to make sure
// everything is exactly the same.
std::string verify_buf(size, '\0');
assert(verify_buf.size() == size);
const size_t cache_bytes_read =
m_memory_cache.Read(this, addr, buf, size, error);
Status verify_error;
const size_t verify_bytes_read =
ReadMemoryFromInferior(addr, const_cast<char *>(verify_buf.data()),
verify_buf.size(), verify_error);
assert(cache_bytes_read == verify_bytes_read);
assert(memcmp(buf, verify_buf.data(), verify_buf.size()) == 0);
assert(verify_error.Success() == error.Success());
return cache_bytes_read;
}
return 0;
#else // !defined(VERIFY_MEMORY_READS)
// Memory caching is enabled, without debug verification
return m_memory_cache.Read(addr, buf, size, error);
#endif // defined (VERIFY_MEMORY_READS)
} else {
// Memory caching is disabled
return ReadMemoryFromInferior(addr, buf, size, error);
}
}
void Process::DoFindInMemory(lldb::addr_t start_addr, lldb::addr_t end_addr,
const uint8_t *buf, size_t size,
AddressRanges &matches, size_t alignment,
size_t max_matches) {
// Inputs are already validated in FindInMemory() functions.
assert(buf != nullptr);
assert(size > 0);
assert(alignment > 0);
assert(max_matches > 0);
assert(start_addr != LLDB_INVALID_ADDRESS);
assert(end_addr != LLDB_INVALID_ADDRESS);
assert(start_addr < end_addr);
lldb::addr_t start = llvm::alignTo(start_addr, alignment);
while (matches.size() < max_matches && (start + size) < end_addr) {
const lldb::addr_t found_addr = FindInMemory(start, end_addr, buf, size);
if (found_addr == LLDB_INVALID_ADDRESS)
break;
if (found_addr % alignment) {
// We need to check the alignment because the FindInMemory uses a special
// algorithm to efficiently search mememory but doesn't support alignment.
start = llvm::alignTo(start + 1, alignment);
continue;
}
matches.emplace_back(found_addr, size);
start = found_addr + alignment;
}
}
AddressRanges Process::FindRangesInMemory(const uint8_t *buf, uint64_t size,
const AddressRanges &ranges,
size_t alignment, size_t max_matches,
Status &error) {
AddressRanges matches;
if (buf == nullptr) {
error = Status::FromErrorString("buffer is null");
return matches;
}
if (size == 0) {
error = Status::FromErrorString("buffer size is zero");
return matches;
}
if (ranges.empty()) {
error = Status::FromErrorString("empty ranges");
return matches;
}
if (alignment == 0) {
error = Status::FromErrorString("alignment must be greater than zero");
return matches;
}
if (max_matches == 0) {
error = Status::FromErrorString("max_matches must be greater than zero");
return matches;
}
int resolved_ranges = 0;
Target &target = GetTarget();
for (size_t i = 0; i < ranges.size(); ++i) {
if (matches.size() >= max_matches)
break;
const AddressRange &range = ranges[i];
if (range.IsValid() == false)
continue;
const lldb::addr_t start_addr =
range.GetBaseAddress().GetLoadAddress(&target);
if (start_addr == LLDB_INVALID_ADDRESS)
continue;
++resolved_ranges;
const lldb::addr_t end_addr = start_addr + range.GetByteSize();
DoFindInMemory(start_addr, end_addr, buf, size, matches, alignment,
max_matches);
}
if (resolved_ranges > 0)
error.Clear();
else
error = Status::FromErrorString("unable to resolve any ranges");
return matches;
}
lldb::addr_t Process::FindInMemory(const uint8_t *buf, uint64_t size,
const AddressRange &range, size_t alignment,
Status &error) {
if (buf == nullptr) {
error = Status::FromErrorString("buffer is null");
return LLDB_INVALID_ADDRESS;
}
if (size == 0) {
error = Status::FromErrorString("buffer size is zero");
return LLDB_INVALID_ADDRESS;
}
if (!range.IsValid()) {
error = Status::FromErrorString("range is invalid");
return LLDB_INVALID_ADDRESS;
}
if (alignment == 0) {
error = Status::FromErrorString("alignment must be greater than zero");
return LLDB_INVALID_ADDRESS;
}
Target &target = GetTarget();
const lldb::addr_t start_addr =
range.GetBaseAddress().GetLoadAddress(&target);
if (start_addr == LLDB_INVALID_ADDRESS) {
error = Status::FromErrorString("range load address is invalid");
return LLDB_INVALID_ADDRESS;
}
const lldb::addr_t end_addr = start_addr + range.GetByteSize();
AddressRanges matches;
DoFindInMemory(start_addr, end_addr, buf, size, matches, alignment, 1);
if (matches.empty())
return LLDB_INVALID_ADDRESS;
error.Clear();
return matches[0].GetBaseAddress().GetLoadAddress(&target);
}
size_t Process::ReadCStringFromMemory(addr_t addr, std::string &out_str,
Status &error) {
char buf[256];
out_str.clear();
addr_t curr_addr = addr;
while (true) {
size_t length = ReadCStringFromMemory(curr_addr, buf, sizeof(buf), error);
if (length == 0)
break;
out_str.append(buf, length);
// If we got "length - 1" bytes, we didn't get the whole C string, we need
// to read some more characters
if (length == sizeof(buf) - 1)
curr_addr += length;
else
break;
}
return out_str.size();
}
// Deprecated in favor of ReadStringFromMemory which has wchar support and
// correct code to find null terminators.
size_t Process::ReadCStringFromMemory(addr_t addr, char *dst,
size_t dst_max_len,
Status &result_error) {
size_t total_cstr_len = 0;
if (dst && dst_max_len) {
result_error.Clear();
// NULL out everything just to be safe
memset(dst, 0, dst_max_len);
addr_t curr_addr = addr;
const size_t cache_line_size = m_memory_cache.GetMemoryCacheLineSize();
size_t bytes_left = dst_max_len - 1;
char *curr_dst = dst;
while (bytes_left > 0) {
addr_t cache_line_bytes_left =
cache_line_size - (curr_addr % cache_line_size);
addr_t bytes_to_read =
std::min<addr_t>(bytes_left, cache_line_bytes_left);
Status error;
size_t bytes_read = ReadMemory(curr_addr, curr_dst, bytes_to_read, error);
if (bytes_read == 0) {
result_error = std::move(error);
dst[total_cstr_len] = '\0';
break;
}
const size_t len = strlen(curr_dst);
total_cstr_len += len;
if (len < bytes_to_read)
break;
curr_dst += bytes_read;
curr_addr += bytes_read;
bytes_left -= bytes_read;
}
} else {
if (dst == nullptr)
result_error = Status::FromErrorString("invalid arguments");
else
result_error.Clear();
}
return total_cstr_len;
}
size_t Process::ReadMemoryFromInferior(addr_t addr, void *buf, size_t size,
Status &error) {
LLDB_SCOPED_TIMER();
if (ABISP abi_sp = GetABI())
addr = abi_sp->FixAnyAddress(addr);
if (buf == nullptr || size == 0)
return 0;
size_t bytes_read = 0;
uint8_t *bytes = (uint8_t *)buf;
while (bytes_read < size) {
const size_t curr_size = size - bytes_read;
const size_t curr_bytes_read =
DoReadMemory(addr + bytes_read, bytes + bytes_read, curr_size, error);
bytes_read += curr_bytes_read;
if (curr_bytes_read == curr_size || curr_bytes_read == 0)
break;
}
// Replace any software breakpoint opcodes that fall into this range back
// into "buf" before we return
if (bytes_read > 0)
RemoveBreakpointOpcodesFromBuffer(addr, bytes_read, (uint8_t *)buf);
return bytes_read;
}
lldb::offset_t Process::ReadMemoryInChunks(lldb::addr_t vm_addr, void *buf,
lldb::addr_t chunk_size,
lldb::offset_t size,
ReadMemoryChunkCallback callback) {
// Safety check to prevent an infinite loop.
if (chunk_size == 0)
return 0;
// Buffer for when a NULL buf is provided, initialized
// to 0 bytes, we set it to chunk_size and then replace buf
// with the new buffer.
DataBufferHeap data_buffer;
if (!buf) {
data_buffer.SetByteSize(chunk_size);
buf = data_buffer.GetBytes();
}
uint64_t bytes_remaining = size;
uint64_t bytes_read = 0;
Status error;
while (bytes_remaining > 0) {
// Get the next read chunk size as the minimum of the remaining bytes and
// the write chunk max size.
const lldb::addr_t bytes_to_read = std::min(bytes_remaining, chunk_size);
const lldb::addr_t current_addr = vm_addr + bytes_read;
const lldb::addr_t bytes_read_for_chunk =
ReadMemoryFromInferior(current_addr, buf, bytes_to_read, error);
bytes_read += bytes_read_for_chunk;
// If the bytes read in this chunk would cause us to overflow, something
// went wrong and we should fail fast.
if (bytes_read_for_chunk > bytes_remaining)
return 0;
else
bytes_remaining -= bytes_read_for_chunk;
if (callback(error, current_addr, buf, bytes_read_for_chunk) ==
IterationAction::Stop)
break;
}
return bytes_read;
}
uint64_t Process::ReadUnsignedIntegerFromMemory(lldb::addr_t vm_addr,
size_t integer_byte_size,
uint64_t fail_value,
Status &error) {
Scalar scalar;
if (ReadScalarIntegerFromMemory(vm_addr, integer_byte_size, false, scalar,
error))
return scalar.ULongLong(fail_value);
return fail_value;
}
int64_t Process::ReadSignedIntegerFromMemory(lldb::addr_t vm_addr,
size_t integer_byte_size,
int64_t fail_value,
Status &error) {
Scalar scalar;
if (ReadScalarIntegerFromMemory(vm_addr, integer_byte_size, true, scalar,
error))
return scalar.SLongLong(fail_value);
return fail_value;
}
addr_t Process::ReadPointerFromMemory(lldb::addr_t vm_addr, Status &error) {
Scalar scalar;
if (ReadScalarIntegerFromMemory(vm_addr, GetAddressByteSize(), false, scalar,
error))
return scalar.ULongLong(LLDB_INVALID_ADDRESS);
return LLDB_INVALID_ADDRESS;
}
bool Process::WritePointerToMemory(lldb::addr_t vm_addr, lldb::addr_t ptr_value,
Status &error) {
Scalar scalar;
const uint32_t addr_byte_size = GetAddressByteSize();
if (addr_byte_size <= 4)
scalar = (uint32_t)ptr_value;
else
scalar = ptr_value;
return WriteScalarToMemory(vm_addr, scalar, addr_byte_size, error) ==
addr_byte_size;
}
size_t Process::WriteMemoryPrivate(addr_t addr, const void *buf, size_t size,
Status &error) {
size_t bytes_written = 0;
const uint8_t *bytes = (const uint8_t *)buf;
while (bytes_written < size) {
const size_t curr_size = size - bytes_written;
const size_t curr_bytes_written = DoWriteMemory(
addr + bytes_written, bytes + bytes_written, curr_size, error);
bytes_written += curr_bytes_written;
if (curr_bytes_written == curr_size || curr_bytes_written == 0)
break;
}
return bytes_written;
}
size_t Process::WriteMemory(addr_t addr, const void *buf, size_t size,
Status &error) {
if (ABISP abi_sp = GetABI())
addr = abi_sp->FixAnyAddress(addr);
m_memory_cache.Flush(addr, size);
if (buf == nullptr || size == 0)
return 0;
if (TrackMemoryCacheChanges() || !m_allocated_memory_cache.IsInCache(addr))
m_mod_id.BumpMemoryID();
// We need to write any data that would go where any current software traps
// (enabled software breakpoints) any software traps (breakpoints) that we
// may have placed in our tasks memory.
StopPointSiteList<BreakpointSite> bp_sites_in_range;
if (!m_breakpoint_site_list.FindInRange(addr, addr + size, bp_sites_in_range))
return WriteMemoryPrivate(addr, buf, size, error);
// No breakpoint sites overlap
if (bp_sites_in_range.IsEmpty())
return WriteMemoryPrivate(addr, buf, size, error);
const uint8_t *ubuf = (const uint8_t *)buf;
uint64_t bytes_written = 0;
bp_sites_in_range.ForEach([this, addr, size, &bytes_written, &ubuf,
&error](BreakpointSite *bp) -> void {
if (error.Fail())
return;
if (bp->GetType() != BreakpointSite::eSoftware)
return;
addr_t intersect_addr;
size_t intersect_size;
size_t opcode_offset;
const bool intersects = bp->IntersectsRange(
addr, size, &intersect_addr, &intersect_size, &opcode_offset);
UNUSED_IF_ASSERT_DISABLED(intersects);
assert(intersects);
assert(addr <= intersect_addr && intersect_addr < addr + size);
assert(addr < intersect_addr + intersect_size &&
intersect_addr + intersect_size <= addr + size);
assert(opcode_offset + intersect_size <= bp->GetByteSize());
// Check for bytes before this breakpoint
const addr_t curr_addr = addr + bytes_written;
if (intersect_addr > curr_addr) {
// There are some bytes before this breakpoint that we need to just
// write to memory
size_t curr_size = intersect_addr - curr_addr;
size_t curr_bytes_written =
WriteMemoryPrivate(curr_addr, ubuf + bytes_written, curr_size, error);
bytes_written += curr_bytes_written;
if (curr_bytes_written != curr_size) {
// We weren't able to write all of the requested bytes, we are
// done looping and will return the number of bytes that we have
// written so far.
if (error.Success())
error = Status::FromErrorString("could not write all bytes");
}
}
// Now write any bytes that would cover up any software breakpoints
// directly into the breakpoint opcode buffer
::memcpy(bp->GetSavedOpcodeBytes() + opcode_offset, ubuf + bytes_written,
intersect_size);
bytes_written += intersect_size;
});
// Write any remaining bytes after the last breakpoint if we have any left
if (bytes_written < size)
bytes_written +=
WriteMemoryPrivate(addr + bytes_written, ubuf + bytes_written,
size - bytes_written, error);
return bytes_written;
}
size_t Process::WriteScalarToMemory(addr_t addr, const Scalar &scalar,
size_t byte_size, Status &error) {
if (byte_size == UINT32_MAX)
byte_size = scalar.GetByteSize();
if (byte_size > 0) {
uint8_t buf[32];
const size_t mem_size =
scalar.GetAsMemoryData(buf, byte_size, GetByteOrder(), error);
if (mem_size > 0)
return WriteMemory(addr, buf, mem_size, error);
else
error = Status::FromErrorString("failed to get scalar as memory data");
} else {
error = Status::FromErrorString("invalid scalar value");
}
return 0;
}
size_t Process::ReadScalarIntegerFromMemory(addr_t addr, uint32_t byte_size,
bool is_signed, Scalar &scalar,
Status &error) {
uint64_t uval = 0;
if (byte_size == 0) {
error = Status::FromErrorString("byte size is zero");
} else if (byte_size & (byte_size - 1)) {
error = Status::FromErrorStringWithFormat(
"byte size %u is not a power of 2", byte_size);
} else if (byte_size <= sizeof(uval)) {
const size_t bytes_read = ReadMemory(addr, &uval, byte_size, error);
if (bytes_read == byte_size) {
DataExtractor data(&uval, sizeof(uval), GetByteOrder(),
GetAddressByteSize());
lldb::offset_t offset = 0;
if (byte_size <= 4)
scalar = data.GetMaxU32(&offset, byte_size);
else
scalar = data.GetMaxU64(&offset, byte_size);
if (is_signed)
scalar.SignExtend(byte_size * 8);
return bytes_read;
}
} else {
error = Status::FromErrorStringWithFormat(
"byte size of %u is too large for integer scalar type", byte_size);
}
return 0;
}
Status Process::WriteObjectFile(std::vector<ObjectFile::LoadableData> entries) {
Status error;
for (const auto &Entry : entries) {
WriteMemory(Entry.Dest, Entry.Contents.data(), Entry.Contents.size(),
error);
if (!error.Success())
break;
}
return error;
}
addr_t Process::AllocateMemory(size_t size, uint32_t permissions,
Status &error) {
if (GetPrivateState() != eStateStopped) {
error = Status::FromErrorString(
"cannot allocate memory while process is running");
return LLDB_INVALID_ADDRESS;
}
return m_allocated_memory_cache.AllocateMemory(size, permissions, error);
}
addr_t Process::CallocateMemory(size_t size, uint32_t permissions,
Status &error) {
addr_t return_addr = AllocateMemory(size, permissions, error);
if (error.Success()) {
std::string buffer(size, 0);
WriteMemory(return_addr, buffer.c_str(), size, error);
}
return return_addr;
}
bool Process::CanJIT() {
if (m_can_jit == eCanJITDontKnow) {
Log *log = GetLog(LLDBLog::Process);
Status err;
uint64_t allocated_memory = AllocateMemory(
8, ePermissionsReadable | ePermissionsWritable | ePermissionsExecutable,
err);
if (err.Success()) {
m_can_jit = eCanJITYes;
LLDB_LOGF(log,
"Process::%s pid %" PRIu64
" allocation test passed, CanJIT () is true",
__FUNCTION__, GetID());
} else {
m_can_jit = eCanJITNo;
LLDB_LOGF(log,
"Process::%s pid %" PRIu64
" allocation test failed, CanJIT () is false: %s",
__FUNCTION__, GetID(), err.AsCString());
}
DeallocateMemory(allocated_memory);
}
return m_can_jit == eCanJITYes;
}
void Process::SetCanJIT(bool can_jit) {
m_can_jit = (can_jit ? eCanJITYes : eCanJITNo);
}
void Process::SetCanRunCode(bool can_run_code) {
SetCanJIT(can_run_code);
m_can_interpret_function_calls = can_run_code;
}
Status Process::DeallocateMemory(addr_t ptr) {
Status error;
if (!m_allocated_memory_cache.DeallocateMemory(ptr)) {
error = Status::FromErrorStringWithFormat(
"deallocation of memory at 0x%" PRIx64 " failed.", (uint64_t)ptr);
}
return error;
}
bool Process::GetWatchpointReportedAfter() {
if (std::optional<bool> subclass_override = DoGetWatchpointReportedAfter())
return *subclass_override;
bool reported_after = true;
const ArchSpec &arch = GetTarget().GetArchitecture();
if (!arch.IsValid())
return reported_after;
llvm::Triple triple = arch.GetTriple();
if (triple.isMIPS() || triple.isPPC64() || triple.isRISCV() ||
triple.isAArch64() || triple.isArmMClass() || triple.isARM() ||
triple.isLoongArch())
reported_after = false;
return reported_after;
}
ModuleSP Process::ReadModuleFromMemory(const FileSpec &file_spec,
lldb::addr_t header_addr,
size_t size_to_read) {
Log *log = GetLog(LLDBLog::Host);
if (log) {
LLDB_LOGF(log,
"Process::ReadModuleFromMemory reading %s binary from memory",
file_spec.GetPath().c_str());
}
ModuleSP module_sp(new Module(file_spec, ArchSpec()));
if (module_sp) {
Status error;
std::unique_ptr<Progress> progress_up;
// Reading an ObjectFile from a local corefile is very fast,
// only print a progress update if we're reading from a
// live session which might go over gdb remote serial protocol.
if (IsLiveDebugSession())
progress_up = std::make_unique<Progress>(
"Reading binary from memory", file_spec.GetFilename().GetString());
ObjectFile *objfile = module_sp->GetMemoryObjectFile(
shared_from_this(), header_addr, error, size_to_read);
if (objfile)
return module_sp;
}
return ModuleSP();
}
bool Process::GetLoadAddressPermissions(lldb::addr_t load_addr,
uint32_t &permissions) {
MemoryRegionInfo range_info;
permissions = 0;
Status error(GetMemoryRegionInfo(load_addr, range_info));
if (!error.Success())
return false;
if (range_info.GetReadable() == MemoryRegionInfo::eDontKnow ||
range_info.GetWritable() == MemoryRegionInfo::eDontKnow ||
range_info.GetExecutable() == MemoryRegionInfo::eDontKnow) {
return false;
}
permissions = range_info.GetLLDBPermissions();
return true;
}
Status Process::EnableWatchpoint(WatchpointSP wp_sp, bool notify) {
Status error;
error = Status::FromErrorString("watchpoints are not supported");
return error;
}
Status Process::DisableWatchpoint(WatchpointSP wp_sp, bool notify) {
Status error;
error = Status::FromErrorString("watchpoints are not supported");
return error;
}
StateType
Process::WaitForProcessStopPrivate(EventSP &event_sp,
const Timeout<std::micro> &timeout) {
StateType state;
while (true) {
event_sp.reset();
state = GetStateChangedEventsPrivate(event_sp, timeout);
if (StateIsStoppedState(state, false))
break;
// If state is invalid, then we timed out
if (state == eStateInvalid)
break;
if (event_sp)
HandlePrivateEvent(event_sp);
}
return state;
}
void Process::LoadOperatingSystemPlugin(bool flush) {
std::lock_guard<std::recursive_mutex> guard(m_thread_mutex);
if (flush)
m_thread_list.Clear();
m_os_up.reset(OperatingSystem::FindPlugin(this, nullptr));
if (flush)
Flush();
}
Status Process::Launch(ProcessLaunchInfo &launch_info) {
StateType state_after_launch = eStateInvalid;
EventSP first_stop_event_sp;
Status status =
LaunchPrivate(launch_info, state_after_launch, first_stop_event_sp);
if (status.Fail())
return status;
if (state_after_launch != eStateStopped &&
state_after_launch != eStateCrashed)
return Status();
// Note, the stop event was consumed above, but not handled. This
// was done to give DidLaunch a chance to run. The target is either
// stopped or crashed. Directly set the state. This is done to
// prevent a stop message with a bunch of spurious output on thread
// status, as well as not pop a ProcessIOHandler.
SetPublicState(state_after_launch, false);
if (PrivateStateThreadIsValid())
ResumePrivateStateThread();
else
StartPrivateStateThread();
// Target was stopped at entry as was intended. Need to notify the
// listeners about it.
if (launch_info.GetFlags().Test(eLaunchFlagStopAtEntry))
HandlePrivateEvent(first_stop_event_sp);
return Status();
}
Status Process::LaunchPrivate(ProcessLaunchInfo &launch_info, StateType &state,
EventSP &event_sp) {
Status error;
m_abi_sp.reset();
m_dyld_up.reset();
m_jit_loaders_up.reset();
m_system_runtime_up.reset();
m_os_up.reset();
GetTarget().ClearAllLoadedSections();
{
std::lock_guard<std::mutex> guard(m_process_input_reader_mutex);
m_process_input_reader.reset();
}
Module *exe_module = GetTarget().GetExecutableModulePointer();
// The "remote executable path" is hooked up to the local Executable
// module. But we should be able to debug a remote process even if the
// executable module only exists on the remote. However, there needs to
// be a way to express this path, without actually having a module.
// The way to do that is to set the ExecutableFile in the LaunchInfo.
// Figure that out here:
FileSpec exe_spec_to_use;
if (!exe_module) {
if (!launch_info.GetExecutableFile() && !launch_info.IsScriptedProcess()) {
error = Status::FromErrorString("executable module does not exist");
return error;
}
exe_spec_to_use = launch_info.GetExecutableFile();
} else
exe_spec_to_use = exe_module->GetFileSpec();
if (exe_module && FileSystem::Instance().Exists(exe_module->GetFileSpec())) {
// Install anything that might need to be installed prior to launching.
// For host systems, this will do nothing, but if we are connected to a
// remote platform it will install any needed binaries
error = GetTarget().Install(&launch_info);
if (error.Fail())
return error;
}
// Listen and queue events that are broadcasted during the process launch.
ListenerSP listener_sp(Listener::MakeListener("LaunchEventHijack"));
HijackProcessEvents(listener_sp);
auto on_exit = llvm::make_scope_exit([this]() { RestoreProcessEvents(); });
if (PrivateStateThreadIsValid())
PausePrivateStateThread();
error = WillLaunch(exe_module);
if (error.Fail()) {
std::string local_exec_file_path = exe_spec_to_use.GetPath();
return Status::FromErrorStringWithFormat("file doesn't exist: '%s'",
local_exec_file_path.c_str());
}
const bool restarted = false;
SetPublicState(eStateLaunching, restarted);
m_should_detach = false;
m_public_run_lock.SetRunning();
error = DoLaunch(exe_module, launch_info);
if (error.Fail()) {
if (GetID() != LLDB_INVALID_PROCESS_ID) {
SetID(LLDB_INVALID_PROCESS_ID);
const char *error_string = error.AsCString();
if (error_string == nullptr)
error_string = "launch failed";
SetExitStatus(-1, error_string);
}
return error;
}
// Now wait for the process to launch and return control to us, and then
// call DidLaunch:
state = WaitForProcessStopPrivate(event_sp, seconds(10));
if (state == eStateInvalid || !event_sp) {
// We were able to launch the process, but we failed to catch the
// initial stop.
error = Status::FromErrorString("failed to catch stop after launch");
SetExitStatus(0, error.AsCString());
Destroy(false);
return error;
}
if (state == eStateExited) {
// We exited while trying to launch somehow. Don't call DidLaunch
// as that's not likely to work, and return an invalid pid.
HandlePrivateEvent(event_sp);
return Status();
}
if (state == eStateStopped || state == eStateCrashed) {
DidLaunch();
// Now that we know the process type, update its signal responses from the
// ones stored in the Target:
if (m_unix_signals_sp)
GetTarget().UpdateSignalsFromDummy(
m_unix_signals_sp, GetTarget().GetDebugger().GetAsyncErrorStream());
DynamicLoader *dyld = GetDynamicLoader();
if (dyld)
dyld->DidLaunch();
GetJITLoaders().DidLaunch();
SystemRuntime *system_runtime = GetSystemRuntime();
if (system_runtime)
system_runtime->DidLaunch();
if (!m_os_up)
LoadOperatingSystemPlugin(false);
// We successfully launched the process and stopped, now it the
// right time to set up signal filters before resuming.
UpdateAutomaticSignalFiltering();
return Status();
}
return Status::FromErrorStringWithFormat(
"Unexpected process state after the launch: %s, expected %s, "
"%s, %s or %s",
StateAsCString(state), StateAsCString(eStateInvalid),
StateAsCString(eStateExited), StateAsCString(eStateStopped),
StateAsCString(eStateCrashed));
}
Status Process::LoadCore() {
GetTarget().ClearAllLoadedSections();
Status error = DoLoadCore();
if (error.Success()) {
ListenerSP listener_sp(
Listener::MakeListener("lldb.process.load_core_listener"));
HijackProcessEvents(listener_sp);
if (PrivateStateThreadIsValid())
ResumePrivateStateThread();
else
StartPrivateStateThread();
DynamicLoader *dyld = GetDynamicLoader();
if (dyld)
dyld->DidAttach();
GetJITLoaders().DidAttach();
SystemRuntime *system_runtime = GetSystemRuntime();
if (system_runtime)
system_runtime->DidAttach();
if (!m_os_up)
LoadOperatingSystemPlugin(false);
// We successfully loaded a core file, now pretend we stopped so we can
// show all of the threads in the core file and explore the crashed state.
SetPrivateState(eStateStopped);
// Wait for a stopped event since we just posted one above...
lldb::EventSP event_sp;
StateType state =
WaitForProcessToStop(std::nullopt, &event_sp, true, listener_sp,
nullptr, true, SelectMostRelevantFrame);
if (!StateIsStoppedState(state, false)) {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOGF(log, "Process::Halt() failed to stop, state is: %s",
StateAsCString(state));
error = Status::FromErrorString(
"Did not get stopped event after loading the core file.");
}
RestoreProcessEvents();
// Since we hijacked the event stream, we will have we won't have run the
// stop hooks. Make sure we do that here:
GetTarget().RunStopHooks(/* at_initial_stop= */ true);
}
return error;
}
DynamicLoader *Process::GetDynamicLoader() {
if (!m_dyld_up)
m_dyld_up.reset(DynamicLoader::FindPlugin(this, ""));
return m_dyld_up.get();
}
void Process::SetDynamicLoader(DynamicLoaderUP dyld_up) {
m_dyld_up = std::move(dyld_up);
}
DataExtractor Process::GetAuxvData() { return DataExtractor(); }
llvm::Expected<bool> Process::SaveCore(llvm::StringRef outfile) {
return false;
}
JITLoaderList &Process::GetJITLoaders() {
if (!m_jit_loaders_up) {
m_jit_loaders_up = std::make_unique<JITLoaderList>();
JITLoader::LoadPlugins(this, *m_jit_loaders_up);
}
return *m_jit_loaders_up;
}
SystemRuntime *Process::GetSystemRuntime() {
if (!m_system_runtime_up)
m_system_runtime_up.reset(SystemRuntime::FindPlugin(this));
return m_system_runtime_up.get();
}
Process::AttachCompletionHandler::AttachCompletionHandler(Process *process,
uint32_t exec_count)
: NextEventAction(process), m_exec_count(exec_count) {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOGF(
log,
"Process::AttachCompletionHandler::%s process=%p, exec_count=%" PRIu32,
__FUNCTION__, static_cast<void *>(process), exec_count);
}
Process::NextEventAction::EventActionResult
Process::AttachCompletionHandler::PerformAction(lldb::EventSP &event_sp) {
Log *log = GetLog(LLDBLog::Process);
StateType state = ProcessEventData::GetStateFromEvent(event_sp.get());
LLDB_LOGF(log,
"Process::AttachCompletionHandler::%s called with state %s (%d)",
__FUNCTION__, StateAsCString(state), static_cast<int>(state));
switch (state) {
case eStateAttaching:
return eEventActionSuccess;
case eStateRunning:
case eStateConnected:
return eEventActionRetry;
case eStateStopped:
case eStateCrashed:
// During attach, prior to sending the eStateStopped event,
// lldb_private::Process subclasses must set the new process ID.
assert(m_process->GetID() != LLDB_INVALID_PROCESS_ID);
// We don't want these events to be reported, so go set the
// ShouldReportStop here:
m_process->GetThreadList().SetShouldReportStop(eVoteNo);
if (m_exec_count > 0) {
--m_exec_count;
LLDB_LOGF(log,
"Process::AttachCompletionHandler::%s state %s: reduced "
"remaining exec count to %" PRIu32 ", requesting resume",
__FUNCTION__, StateAsCString(state), m_exec_count);
RequestResume();
return eEventActionRetry;
} else {
LLDB_LOGF(log,
"Process::AttachCompletionHandler::%s state %s: no more "
"execs expected to start, continuing with attach",
__FUNCTION__, StateAsCString(state));
m_process->CompleteAttach();
return eEventActionSuccess;
}
break;
default:
case eStateExited:
case eStateInvalid:
break;
}
m_exit_string.assign("No valid Process");
return eEventActionExit;
}
Process::NextEventAction::EventActionResult
Process::AttachCompletionHandler::HandleBeingInterrupted() {
return eEventActionSuccess;
}
const char *Process::AttachCompletionHandler::GetExitString() {
return m_exit_string.c_str();
}
ListenerSP ProcessAttachInfo::GetListenerForProcess(Debugger &debugger) {
if (m_listener_sp)
return m_listener_sp;
else
return debugger.GetListener();
}
Status Process::WillLaunch(Module *module) {
return DoWillLaunch(module);
}
Status Process::WillAttachToProcessWithID(lldb::pid_t pid) {
return DoWillAttachToProcessWithID(pid);
}
Status Process::WillAttachToProcessWithName(const char *process_name,
bool wait_for_launch) {
return DoWillAttachToProcessWithName(process_name, wait_for_launch);
}
Status Process::Attach(ProcessAttachInfo &attach_info) {
m_abi_sp.reset();
{
std::lock_guard<std::mutex> guard(m_process_input_reader_mutex);
m_process_input_reader.reset();
}
m_dyld_up.reset();
m_jit_loaders_up.reset();
m_system_runtime_up.reset();
m_os_up.reset();
GetTarget().ClearAllLoadedSections();
lldb::pid_t attach_pid = attach_info.GetProcessID();
Status error;
if (attach_pid == LLDB_INVALID_PROCESS_ID) {
char process_name[PATH_MAX];
if (attach_info.GetExecutableFile().GetPath(process_name,
sizeof(process_name))) {
const bool wait_for_launch = attach_info.GetWaitForLaunch();
if (wait_for_launch) {
error = WillAttachToProcessWithName(process_name, wait_for_launch);
if (error.Success()) {
m_public_run_lock.SetRunning();
m_should_detach = true;
const bool restarted = false;
SetPublicState(eStateAttaching, restarted);
// Now attach using these arguments.
error = DoAttachToProcessWithName(process_name, attach_info);
if (error.Fail()) {
if (GetID() != LLDB_INVALID_PROCESS_ID) {
SetID(LLDB_INVALID_PROCESS_ID);
if (error.AsCString() == nullptr)
error = Status::FromErrorString("attach failed");
SetExitStatus(-1, error.AsCString());
}
} else {
SetNextEventAction(new Process::AttachCompletionHandler(
this, attach_info.GetResumeCount()));
StartPrivateStateThread();
}
return error;
}
} else {
ProcessInstanceInfoList process_infos;
PlatformSP platform_sp(GetTarget().GetPlatform());
if (platform_sp) {
ProcessInstanceInfoMatch match_info;
match_info.GetProcessInfo() = attach_info;
match_info.SetNameMatchType(NameMatch::Equals);
platform_sp->FindProcesses(match_info, process_infos);
const uint32_t num_matches = process_infos.size();
if (num_matches == 1) {
attach_pid = process_infos[0].GetProcessID();
// Fall through and attach using the above process ID
} else {
match_info.GetProcessInfo().GetExecutableFile().GetPath(
process_name, sizeof(process_name));
if (num_matches > 1) {
StreamString s;
ProcessInstanceInfo::DumpTableHeader(s, true, false);
for (size_t i = 0; i < num_matches; i++) {
process_infos[i].DumpAsTableRow(
s, platform_sp->GetUserIDResolver(), true, false);
}
error = Status::FromErrorStringWithFormat(
"more than one process named %s:\n%s", process_name,
s.GetData());
} else
error = Status::FromErrorStringWithFormat(
"could not find a process named %s", process_name);
}
} else {
error = Status::FromErrorString(
"invalid platform, can't find processes by name");
return error;
}
}
} else {
error = Status::FromErrorString("invalid process name");
}
}
if (attach_pid != LLDB_INVALID_PROCESS_ID) {
error = WillAttachToProcessWithID(attach_pid);
if (error.Success()) {
m_public_run_lock.SetRunning();
// Now attach using these arguments.
m_should_detach = true;
const bool restarted = false;
SetPublicState(eStateAttaching, restarted);
error = DoAttachToProcessWithID(attach_pid, attach_info);
if (error.Success()) {
SetNextEventAction(new Process::AttachCompletionHandler(
this, attach_info.GetResumeCount()));
StartPrivateStateThread();
} else {
if (GetID() != LLDB_INVALID_PROCESS_ID)
SetID(LLDB_INVALID_PROCESS_ID);
const char *error_string = error.AsCString();
if (error_string == nullptr)
error_string = "attach failed";
SetExitStatus(-1, error_string);
}
}
}
return error;
}
void Process::CompleteAttach() {
Log *log(GetLog(LLDBLog::Process | LLDBLog::Target));
LLDB_LOGF(log, "Process::%s()", __FUNCTION__);
// Let the process subclass figure out at much as it can about the process
// before we go looking for a dynamic loader plug-in.
ArchSpec process_arch;
DidAttach(process_arch);
if (process_arch.IsValid()) {
LLDB_LOG(log,
"Process::{0} replacing process architecture with DidAttach() "
"architecture: \"{1}\"",
__FUNCTION__, process_arch.GetTriple().getTriple());
GetTarget().SetArchitecture(process_arch);
}
// We just attached. If we have a platform, ask it for the process
// architecture, and if it isn't the same as the one we've already set,
// switch architectures.
PlatformSP platform_sp(GetTarget().GetPlatform());
assert(platform_sp);
ArchSpec process_host_arch = GetSystemArchitecture();
if (platform_sp) {
const ArchSpec &target_arch = GetTarget().GetArchitecture();
if (target_arch.IsValid() && !platform_sp->IsCompatibleArchitecture(
target_arch, process_host_arch,
ArchSpec::CompatibleMatch, nullptr)) {
ArchSpec platform_arch;
platform_sp = GetTarget().GetDebugger().GetPlatformList().GetOrCreate(
target_arch, process_host_arch, &platform_arch);
if (platform_sp) {
GetTarget().SetPlatform(platform_sp);
GetTarget().SetArchitecture(platform_arch);
LLDB_LOG(log,
"switching platform to {0} and architecture to {1} based on "
"info from attach",
platform_sp->GetName(), platform_arch.GetTriple().getTriple());
}
} else if (!process_arch.IsValid()) {
ProcessInstanceInfo process_info;
GetProcessInfo(process_info);
const ArchSpec &process_arch = process_info.GetArchitecture();
const ArchSpec &target_arch = GetTarget().GetArchitecture();
if (process_arch.IsValid() &&
target_arch.IsCompatibleMatch(process_arch) &&
!target_arch.IsExactMatch(process_arch)) {
GetTarget().SetArchitecture(process_arch);
LLDB_LOGF(log,
"Process::%s switching architecture to %s based on info "
"the platform retrieved for pid %" PRIu64,
__FUNCTION__, process_arch.GetTriple().getTriple().c_str(),
GetID());
}
}
}
// Now that we know the process type, update its signal responses from the
// ones stored in the Target:
if (m_unix_signals_sp)
GetTarget().UpdateSignalsFromDummy(
m_unix_signals_sp, GetTarget().GetDebugger().GetAsyncErrorStream());
// We have completed the attach, now it is time to find the dynamic loader
// plug-in
DynamicLoader *dyld = GetDynamicLoader();
if (dyld) {
dyld->DidAttach();
if (log) {
ModuleSP exe_module_sp = GetTarget().GetExecutableModule();
LLDB_LOG(log,
"after DynamicLoader::DidAttach(), target "
"executable is {0} (using {1} plugin)",
exe_module_sp ? exe_module_sp->GetFileSpec() : FileSpec(),
dyld->GetPluginName());
}
}
GetJITLoaders().DidAttach();
SystemRuntime *system_runtime = GetSystemRuntime();
if (system_runtime) {
system_runtime->DidAttach();
if (log) {
ModuleSP exe_module_sp = GetTarget().GetExecutableModule();
LLDB_LOG(log,
"after SystemRuntime::DidAttach(), target "
"executable is {0} (using {1} plugin)",
exe_module_sp ? exe_module_sp->GetFileSpec() : FileSpec(),
system_runtime->GetPluginName());
}
}
// If we don't have an operating system plugin loaded yet, see if
// LoadOperatingSystemPlugin can find one (and stuff it in m_os_up).
if (!m_os_up)
LoadOperatingSystemPlugin(false);
if (m_os_up) {
// Somebody might have gotten threads before we loaded the OS Plugin above,
// so we need to force the update now or the newly loaded plugin won't get
// a chance to process the threads.
m_thread_list.Clear();
UpdateThreadListIfNeeded();
}
// Figure out which one is the executable, and set that in our target:
ModuleSP new_executable_module_sp;
for (ModuleSP module_sp : GetTarget().GetImages().Modules()) {
if (module_sp && module_sp->IsExecutable()) {
if (GetTarget().GetExecutableModulePointer() != module_sp.get())
new_executable_module_sp = module_sp;
break;
}
}
if (new_executable_module_sp) {
GetTarget().SetExecutableModule(new_executable_module_sp,
eLoadDependentsNo);
if (log) {
ModuleSP exe_module_sp = GetTarget().GetExecutableModule();
LLDB_LOGF(
log,
"Process::%s after looping through modules, target executable is %s",
__FUNCTION__,
exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str()
: "<none>");
}
}
// Since we hijacked the event stream, we will have we won't have run the
// stop hooks. Make sure we do that here:
GetTarget().RunStopHooks(/* at_initial_stop= */ true);
}
Status Process::ConnectRemote(llvm::StringRef remote_url) {
m_abi_sp.reset();
{
std::lock_guard<std::mutex> guard(m_process_input_reader_mutex);
m_process_input_reader.reset();
}
// Find the process and its architecture. Make sure it matches the
// architecture of the current Target, and if not adjust it.
Status error(DoConnectRemote(remote_url));
if (error.Success()) {
if (GetID() != LLDB_INVALID_PROCESS_ID) {
EventSP event_sp;
StateType state = WaitForProcessStopPrivate(event_sp, std::nullopt);
if (state == eStateStopped || state == eStateCrashed) {
// If we attached and actually have a process on the other end, then
// this ended up being the equivalent of an attach.
CompleteAttach();
// This delays passing the stopped event to listeners till
// CompleteAttach gets a chance to complete...
HandlePrivateEvent(event_sp);
}
}
if (PrivateStateThreadIsValid())
ResumePrivateStateThread();
else
StartPrivateStateThread();
}
return error;
}
void Process::SetBaseDirection(RunDirection direction) {
if (m_base_direction == direction)
return;
m_thread_list.DiscardThreadPlans();
m_base_direction = direction;
}
Status Process::PrivateResume() {
Log *log(GetLog(LLDBLog::Process | LLDBLog::Step));
LLDB_LOGF(log,
"Process::PrivateResume() m_stop_id = %u, public state: %s "
"private state: %s",
m_mod_id.GetStopID(), StateAsCString(m_public_state.GetValue()),
StateAsCString(m_private_state.GetValue()));
// If signals handing status changed we might want to update our signal
// filters before resuming.
UpdateAutomaticSignalFiltering();
// Clear any crash info we accumulated for this stop, but don't do so if we
// are running functions; we don't want to wipe out the real stop's info.
if (!GetModID().IsLastResumeForUserExpression())
ResetExtendedCrashInfoDict();
Status error(WillResume());
// Tell the process it is about to resume before the thread list
if (error.Success()) {
// Now let the thread list know we are about to resume so it can let all of
// our threads know that they are about to be resumed. Threads will each be
// called with Thread::WillResume(StateType) where StateType contains the
// state that they are supposed to have when the process is resumed
// (suspended/running/stepping). Threads should also check their resume
// signal in lldb::Thread::GetResumeSignal() to see if they are supposed to
// start back up with a signal.
RunDirection direction;
if (m_thread_list.WillResume(direction)) {
LLDB_LOGF(log, "Process::PrivateResume WillResume direction=%d",
direction);
// Last thing, do the PreResumeActions.
if (!RunPreResumeActions()) {
error = Status::FromErrorString(
"Process::PrivateResume PreResumeActions failed, not resuming.");
LLDB_LOGF(
log,
"Process::PrivateResume PreResumeActions failed, not resuming.");
} else {
m_mod_id.BumpResumeID();
error = DoResume(direction);
if (error.Success()) {
DidResume();
m_thread_list.DidResume();
LLDB_LOGF(log,
"Process::PrivateResume thinks the process has resumed.");
} else {
LLDB_LOGF(log, "Process::PrivateResume() DoResume failed.");
return error;
}
}
} else {
// Somebody wanted to run without running (e.g. we were faking a step
// from one frame of a set of inlined frames that share the same PC to
// another.) So generate a continue & a stopped event, and let the world
// handle them.
LLDB_LOGF(log,
"Process::PrivateResume() asked to simulate a start & stop.");
SetPrivateState(eStateRunning);
SetPrivateState(eStateStopped);
}
} else
LLDB_LOGF(log, "Process::PrivateResume() got an error \"%s\".",
error.AsCString("<unknown error>"));
return error;
}
Status Process::Halt(bool clear_thread_plans, bool use_run_lock) {
if (!StateIsRunningState(m_public_state.GetValue()))
return Status::FromErrorString("Process is not running.");
// Don't clear the m_clear_thread_plans_on_stop, only set it to true if in
// case it was already set and some thread plan logic calls halt on its own.
m_clear_thread_plans_on_stop |= clear_thread_plans;
ListenerSP halt_listener_sp(
Listener::MakeListener("lldb.process.halt_listener"));
HijackProcessEvents(halt_listener_sp);
EventSP event_sp;
SendAsyncInterrupt();
if (m_public_state.GetValue() == eStateAttaching) {
// Don't hijack and eat the eStateExited as the code that was doing the
// attach will be waiting for this event...
RestoreProcessEvents();
Destroy(false);
SetExitStatus(SIGKILL, "Cancelled async attach.");
return Status();
}
// Wait for the process halt timeout seconds for the process to stop.
// If we are going to use the run lock, that means we're stopping out to the
// user, so we should also select the most relevant frame.
SelectMostRelevant select_most_relevant =
use_run_lock ? SelectMostRelevantFrame : DoNoSelectMostRelevantFrame;
StateType state = WaitForProcessToStop(GetInterruptTimeout(), &event_sp, true,
halt_listener_sp, nullptr,
use_run_lock, select_most_relevant);
RestoreProcessEvents();
if (state == eStateInvalid || !event_sp) {
// We timed out and didn't get a stop event...
return Status::FromErrorStringWithFormat("Halt timed out. State = %s",
StateAsCString(GetState()));
}
BroadcastEvent(event_sp);
return Status();
}
lldb::addr_t Process::FindInMemory(lldb::addr_t low, lldb::addr_t high,
const uint8_t *buf, size_t size) {
const size_t region_size = high - low;
if (region_size < size)
return LLDB_INVALID_ADDRESS;
// See "Boyer-Moore string search algorithm".
std::vector<size_t> bad_char_heuristic(256, size);
for (size_t idx = 0; idx < size - 1; idx++) {
decltype(bad_char_heuristic)::size_type bcu_idx = buf[idx];
bad_char_heuristic[bcu_idx] = size - idx - 1;
}
// Memory we're currently searching through.
llvm::SmallVector<uint8_t, 0> mem;
// Position of the memory buffer.
addr_t mem_pos = low;
// Maximum number of bytes read (and buffered). We need to read at least
// `size` bytes for a successful match.
const size_t max_read_size = std::max<size_t>(size, 0x10000);
for (addr_t cur_addr = low; cur_addr <= (high - size);) {
if (cur_addr + size > mem_pos + mem.size()) {
// We need to read more data. We don't attempt to reuse the data we've
// already read (up to `size-1` bytes from `cur_addr` to
// `mem_pos+mem.size()`). This is fine for patterns much smaller than
// max_read_size. For very
// long patterns we may need to do something more elaborate.
mem.resize_for_overwrite(max_read_size);
Status error;
mem.resize(ReadMemory(cur_addr, mem.data(),
std::min<addr_t>(mem.size(), high - cur_addr),
error));
mem_pos = cur_addr;
if (size > mem.size()) {
// We didn't read enough data. Skip to the next memory region.
MemoryRegionInfo info;
error = GetMemoryRegionInfo(mem_pos + mem.size(), info);
if (error.Fail())
break;
cur_addr = info.GetRange().GetRangeEnd();
continue;
}
}
int64_t j = size - 1;
while (j >= 0 && buf[j] == mem[cur_addr + j - mem_pos])
j--;
if (j < 0)
return cur_addr; // We have a match.
cur_addr += bad_char_heuristic[mem[cur_addr + size - 1 - mem_pos]];
}
return LLDB_INVALID_ADDRESS;
}
Status Process::StopForDestroyOrDetach(lldb::EventSP &exit_event_sp) {
Status error;
// Check both the public & private states here. If we're hung evaluating an
// expression, for instance, then the public state will be stopped, but we
// still need to interrupt.
if (m_public_state.GetValue() == eStateRunning ||
m_private_state.GetValue() == eStateRunning) {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOGF(log, "Process::%s() About to stop.", __FUNCTION__);
ListenerSP listener_sp(
Listener::MakeListener("lldb.Process.StopForDestroyOrDetach.hijack"));
HijackProcessEvents(listener_sp);
SendAsyncInterrupt();
// Consume the interrupt event.
StateType state = WaitForProcessToStop(GetInterruptTimeout(),
&exit_event_sp, true, listener_sp);
RestoreProcessEvents();
// If the process exited while we were waiting for it to stop, put the
// exited event into the shared pointer passed in and return. Our caller
// doesn't need to do anything else, since they don't have a process
// anymore...
if (state == eStateExited || m_private_state.GetValue() == eStateExited) {
LLDB_LOGF(log, "Process::%s() Process exited while waiting to stop.",
__FUNCTION__);
return error;
} else
exit_event_sp.reset(); // It is ok to consume any non-exit stop events
if (state != eStateStopped) {
LLDB_LOGF(log, "Process::%s() failed to stop, state is: %s", __FUNCTION__,
StateAsCString(state));
// If we really couldn't stop the process then we should just error out
// here, but if the lower levels just bobbled sending the event and we
// really are stopped, then continue on.
StateType private_state = m_private_state.GetValue();
if (private_state != eStateStopped) {
return Status::FromErrorStringWithFormat(
"Attempt to stop the target in order to detach timed out. "
"State = %s",
StateAsCString(GetState()));
}
}
}
return error;
}
Status Process::Detach(bool keep_stopped) {
EventSP exit_event_sp;
Status error;
m_destroy_in_process = true;
error = WillDetach();
if (error.Success()) {
if (DetachRequiresHalt()) {
error = StopForDestroyOrDetach(exit_event_sp);
if (!error.Success()) {
m_destroy_in_process = false;
return error;
} else if (exit_event_sp) {
// We shouldn't need to do anything else here. There's no process left
// to detach from...
StopPrivateStateThread();
m_destroy_in_process = false;
return error;
}
}
m_thread_list.DiscardThreadPlans();
DisableAllBreakpointSites();
error = DoDetach(keep_stopped);
if (error.Success()) {
DidDetach();
StopPrivateStateThread();
} else {
return error;
}
}
m_destroy_in_process = false;
// If we exited when we were waiting for a process to stop, then forward the
// event here so we don't lose the event
if (exit_event_sp) {
// Directly broadcast our exited event because we shut down our private
// state thread above
BroadcastEvent(exit_event_sp);
}
// If we have been interrupted (to kill us) in the middle of running, we may
// not end up propagating the last events through the event system, in which
// case we might strand the write lock. Unlock it here so when we do to tear
// down the process we don't get an error destroying the lock.
m_public_run_lock.SetStopped();
return error;
}
Status Process::Destroy(bool force_kill) {
// If we've already called Process::Finalize then there's nothing useful to
// be done here. Finalize has actually called Destroy already.
if (m_finalizing)
return {};
return DestroyImpl(force_kill);
}
Status Process::DestroyImpl(bool force_kill) {
// Tell ourselves we are in the process of destroying the process, so that we
// don't do any unnecessary work that might hinder the destruction. Remember
// to set this back to false when we are done. That way if the attempt
// failed and the process stays around for some reason it won't be in a
// confused state.
if (force_kill)
m_should_detach = false;
if (GetShouldDetach()) {
// FIXME: This will have to be a process setting:
bool keep_stopped = false;
Detach(keep_stopped);
}
m_destroy_in_process = true;
Status error(WillDestroy());
if (error.Success()) {
EventSP exit_event_sp;
if (DestroyRequiresHalt()) {
error = StopForDestroyOrDetach(exit_event_sp);
}
if (m_public_state.GetValue() == eStateStopped) {
// Ditch all thread plans, and remove all our breakpoints: in case we
// have to restart the target to kill it, we don't want it hitting a
// breakpoint... Only do this if we've stopped, however, since if we
// didn't manage to halt it above, then we're not going to have much luck
// doing this now.
m_thread_list.DiscardThreadPlans();
DisableAllBreakpointSites();
}
error = DoDestroy();
if (error.Success()) {
DidDestroy();
StopPrivateStateThread();
}
m_stdio_communication.StopReadThread();
m_stdio_communication.Disconnect();
m_stdin_forward = false;
{
std::lock_guard<std::mutex> guard(m_process_input_reader_mutex);
if (m_process_input_reader) {
m_process_input_reader->SetIsDone(true);
m_process_input_reader->Cancel();
m_process_input_reader.reset();
}
}
// If we exited when we were waiting for a process to stop, then forward
// the event here so we don't lose the event
if (exit_event_sp) {
// Directly broadcast our exited event because we shut down our private
// state thread above
BroadcastEvent(exit_event_sp);
}
// If we have been interrupted (to kill us) in the middle of running, we
// may not end up propagating the last events through the event system, in
// which case we might strand the write lock. Unlock it here so when we do
// to tear down the process we don't get an error destroying the lock.
m_public_run_lock.SetStopped();
}
m_destroy_in_process = false;
return error;
}
Status Process::Signal(int signal) {
Status error(WillSignal());
if (error.Success()) {
error = DoSignal(signal);
if (error.Success())
DidSignal();
}
return error;
}
void Process::SetUnixSignals(UnixSignalsSP &&signals_sp) {
assert(signals_sp && "null signals_sp");
m_unix_signals_sp = std::move(signals_sp);
}
const lldb::UnixSignalsSP &Process::GetUnixSignals() {
assert(m_unix_signals_sp && "null m_unix_signals_sp");
return m_unix_signals_sp;
}
lldb::ByteOrder Process::GetByteOrder() const {
return GetTarget().GetArchitecture().GetByteOrder();
}
uint32_t Process::GetAddressByteSize() const {
return GetTarget().GetArchitecture().GetAddressByteSize();
}
bool Process::ShouldBroadcastEvent(Event *event_ptr) {
const StateType state =
Process::ProcessEventData::GetStateFromEvent(event_ptr);
bool return_value = true;
Log *log(GetLog(LLDBLog::Events | LLDBLog::Process));
switch (state) {
case eStateDetached:
case eStateExited:
case eStateUnloaded:
m_stdio_communication.SynchronizeWithReadThread();
m_stdio_communication.StopReadThread();
m_stdio_communication.Disconnect();
m_stdin_forward = false;
[[fallthrough]];
case eStateConnected:
case eStateAttaching:
case eStateLaunching:
// These events indicate changes in the state of the debugging session,
// always report them.
return_value = true;
break;
case eStateInvalid:
// We stopped for no apparent reason, don't report it.
return_value = false;
break;
case eStateRunning:
case eStateStepping:
// If we've started the target running, we handle the cases where we are
// already running and where there is a transition from stopped to running
// differently. running -> running: Automatically suppress extra running
// events stopped -> running: Report except when there is one or more no
// votes
// and no yes votes.
SynchronouslyNotifyStateChanged(state);
if (m_force_next_event_delivery)
return_value = true;
else {
switch (m_last_broadcast_state) {
case eStateRunning:
case eStateStepping:
// We always suppress multiple runnings with no PUBLIC stop in between.
return_value = false;
break;
default:
// TODO: make this work correctly. For now always report
// run if we aren't running so we don't miss any running events. If I
// run the lldb/test/thread/a.out file and break at main.cpp:58, run
// and hit the breakpoints on multiple threads, then somehow during the
// stepping over of all breakpoints no run gets reported.
// This is a transition from stop to run.
switch (m_thread_list.ShouldReportRun(event_ptr)) {
case eVoteYes:
case eVoteNoOpinion:
return_value = true;
break;
case eVoteNo:
return_value = false;
break;
}
break;
}
}
break;
case eStateStopped:
case eStateCrashed:
case eStateSuspended:
// We've stopped. First see if we're going to restart the target. If we
// are going to stop, then we always broadcast the event. If we aren't
// going to stop, let the thread plans decide if we're going to report this
// event. If no thread has an opinion, we don't report it.
m_stdio_communication.SynchronizeWithReadThread();
RefreshStateAfterStop();
if (ProcessEventData::GetInterruptedFromEvent(event_ptr)) {
LLDB_LOGF(log,
"Process::ShouldBroadcastEvent (%p) stopped due to an "
"interrupt, state: %s",
static_cast<void *>(event_ptr), StateAsCString(state));
// Even though we know we are going to stop, we should let the threads
// have a look at the stop, so they can properly set their state.
m_thread_list.ShouldStop(event_ptr);
return_value = true;
} else {
bool was_restarted = ProcessEventData::GetRestartedFromEvent(event_ptr);
bool should_resume = false;
// It makes no sense to ask "ShouldStop" if we've already been
// restarted... Asking the thread list is also not likely to go well,
// since we are running again. So in that case just report the event.
if (!was_restarted)
should_resume = !m_thread_list.ShouldStop(event_ptr);
if (was_restarted || should_resume || m_resume_requested) {
Vote report_stop_vote = m_thread_list.ShouldReportStop(event_ptr);
LLDB_LOGF(log,
"Process::ShouldBroadcastEvent: should_resume: %i state: "
"%s was_restarted: %i report_stop_vote: %d.",
should_resume, StateAsCString(state), was_restarted,
report_stop_vote);
switch (report_stop_vote) {
case eVoteYes:
return_value = true;
break;
case eVoteNoOpinion:
case eVoteNo:
return_value = false;
break;
}
if (!was_restarted) {
LLDB_LOGF(log,
"Process::ShouldBroadcastEvent (%p) Restarting process "
"from state: %s",
static_cast<void *>(event_ptr), StateAsCString(state));
ProcessEventData::SetRestartedInEvent(event_ptr, true);
PrivateResume();
}
} else {
return_value = true;
SynchronouslyNotifyStateChanged(state);
}
}
break;
}
// Forcing the next event delivery is a one shot deal. So reset it here.
m_force_next_event_delivery = false;
// We do some coalescing of events (for instance two consecutive running
// events get coalesced.) But we only coalesce against events we actually
// broadcast. So we use m_last_broadcast_state to track that. NB - you
// can't use "m_public_state.GetValue()" for that purpose, as was originally
// done, because the PublicState reflects the last event pulled off the
// queue, and there may be several events stacked up on the queue unserviced.
// So the PublicState may not reflect the last broadcasted event yet.
// m_last_broadcast_state gets updated here.
if (return_value)
m_last_broadcast_state = state;
LLDB_LOGF(log,
"Process::ShouldBroadcastEvent (%p) => new state: %s, last "
"broadcast state: %s - %s",
static_cast<void *>(event_ptr), StateAsCString(state),
StateAsCString(m_last_broadcast_state),
return_value ? "YES" : "NO");
return return_value;
}
bool Process::StartPrivateStateThread(bool is_secondary_thread) {
Log *log = GetLog(LLDBLog::Events);
bool already_running = PrivateStateThreadIsValid();
LLDB_LOGF(log, "Process::%s()%s ", __FUNCTION__,
already_running ? " already running"
: " starting private state thread");
if (!is_secondary_thread && already_running)
return true;
// Create a thread that watches our internal state and controls which events
// make it to clients (into the DCProcess event queue).
char thread_name[1024];
uint32_t max_len = llvm::get_max_thread_name_length();
if (max_len > 0 && max_len <= 30) {
// On platforms with abbreviated thread name lengths, choose thread names
// that fit within the limit.
if (already_running)
snprintf(thread_name, sizeof(thread_name), "intern-state-OV");
else
snprintf(thread_name, sizeof(thread_name), "intern-state");
} else {
if (already_running)
snprintf(thread_name, sizeof(thread_name),
"<lldb.process.internal-state-override(pid=%" PRIu64 ")>",
GetID());
else
snprintf(thread_name, sizeof(thread_name),
"<lldb.process.internal-state(pid=%" PRIu64 ")>", GetID());
}
llvm::Expected<HostThread> private_state_thread =
ThreadLauncher::LaunchThread(
thread_name,
[this, is_secondary_thread] {
return RunPrivateStateThread(is_secondary_thread);
},
8 * 1024 * 1024);
if (!private_state_thread) {
LLDB_LOG_ERROR(GetLog(LLDBLog::Host), private_state_thread.takeError(),
"failed to launch host thread: {0}");
return false;
}
assert(private_state_thread->IsJoinable());
m_private_state_thread = *private_state_thread;
ResumePrivateStateThread();
return true;
}
void Process::PausePrivateStateThread() {
ControlPrivateStateThread(eBroadcastInternalStateControlPause);
}
void Process::ResumePrivateStateThread() {
ControlPrivateStateThread(eBroadcastInternalStateControlResume);
}
void Process::StopPrivateStateThread() {
if (m_private_state_thread.IsJoinable())
ControlPrivateStateThread(eBroadcastInternalStateControlStop);
else {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOGF(
log,
"Went to stop the private state thread, but it was already invalid.");
}
}
void Process::ControlPrivateStateThread(uint32_t signal) {
Log *log = GetLog(LLDBLog::Process);
assert(signal == eBroadcastInternalStateControlStop ||
signal == eBroadcastInternalStateControlPause ||
signal == eBroadcastInternalStateControlResume);
LLDB_LOGF(log, "Process::%s (signal = %d)", __FUNCTION__, signal);
// Signal the private state thread
if (m_private_state_thread.IsJoinable()) {
// Broadcast the event.
// It is important to do this outside of the if below, because it's
// possible that the thread state is invalid but that the thread is waiting
// on a control event instead of simply being on its way out (this should
// not happen, but it apparently can).
LLDB_LOGF(log, "Sending control event of type: %d.", signal);
std::shared_ptr<EventDataReceipt> event_receipt_sp(new EventDataReceipt());
m_private_state_control_broadcaster.BroadcastEvent(signal,
event_receipt_sp);
// Wait for the event receipt or for the private state thread to exit
bool receipt_received = false;
if (PrivateStateThreadIsValid()) {
while (!receipt_received) {
// Check for a receipt for n seconds and then check if the private
// state thread is still around.
receipt_received =
event_receipt_sp->WaitForEventReceived(GetUtilityExpressionTimeout());
if (!receipt_received) {
// Check if the private state thread is still around. If it isn't
// then we are done waiting
if (!PrivateStateThreadIsValid())
break; // Private state thread exited or is exiting, we are done
}
}
}
if (signal == eBroadcastInternalStateControlStop) {
thread_result_t result = {};
m_private_state_thread.Join(&result);
m_private_state_thread.Reset();
}
} else {
LLDB_LOGF(
log,
"Private state thread already dead, no need to signal it to stop.");
}
}
void Process::SendAsyncInterrupt(Thread *thread) {
if (thread != nullptr)
m_interrupt_tid = thread->GetProtocolID();
else
m_interrupt_tid = LLDB_INVALID_THREAD_ID;
if (PrivateStateThreadIsValid())
m_private_state_broadcaster.BroadcastEvent(Process::eBroadcastBitInterrupt,
nullptr);
else
BroadcastEvent(Process::eBroadcastBitInterrupt, nullptr);
}
void Process::HandlePrivateEvent(EventSP &event_sp) {
Log *log = GetLog(LLDBLog::Process);
m_resume_requested = false;
const StateType new_state =
Process::ProcessEventData::GetStateFromEvent(event_sp.get());
// First check to see if anybody wants a shot at this event:
if (m_next_event_action_up) {
NextEventAction::EventActionResult action_result =
m_next_event_action_up->PerformAction(event_sp);
LLDB_LOGF(log, "Ran next event action, result was %d.", action_result);
switch (action_result) {
case NextEventAction::eEventActionSuccess:
SetNextEventAction(nullptr);
break;
case NextEventAction::eEventActionRetry:
break;
case NextEventAction::eEventActionExit:
// Handle Exiting Here. If we already got an exited event, we should
// just propagate it. Otherwise, swallow this event, and set our state
// to exit so the next event will kill us.
if (new_state != eStateExited) {
// FIXME: should cons up an exited event, and discard this one.
SetExitStatus(0, m_next_event_action_up->GetExitString());
SetNextEventAction(nullptr);
return;
}
SetNextEventAction(nullptr);
break;
}
}
// See if we should broadcast this state to external clients?
const bool should_broadcast = ShouldBroadcastEvent(event_sp.get());
if (should_broadcast) {
const bool is_hijacked = IsHijackedForEvent(eBroadcastBitStateChanged);
if (log) {
LLDB_LOGF(log,
"Process::%s (pid = %" PRIu64
") broadcasting new state %s (old state %s) to %s",
__FUNCTION__, GetID(), StateAsCString(new_state),
StateAsCString(GetState()),
is_hijacked ? "hijacked" : "public");
}
Process::ProcessEventData::SetUpdateStateOnRemoval(event_sp.get());
if (StateIsRunningState(new_state)) {
// Only push the input handler if we aren't fowarding events, as this
// means the curses GUI is in use... Or don't push it if we are launching
// since it will come up stopped.
if (!GetTarget().GetDebugger().IsForwardingEvents() &&
new_state != eStateLaunching && new_state != eStateAttaching) {
PushProcessIOHandler();
m_iohandler_sync.SetValue(m_iohandler_sync.GetValue() + 1,
eBroadcastAlways);
LLDB_LOGF(log, "Process::%s updated m_iohandler_sync to %d",
__FUNCTION__, m_iohandler_sync.GetValue());
}
} else if (StateIsStoppedState(new_state, false)) {
if (!Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) {
// If the lldb_private::Debugger is handling the events, we don't want
// to pop the process IOHandler here, we want to do it when we receive
// the stopped event so we can carefully control when the process
// IOHandler is popped because when we stop we want to display some
// text stating how and why we stopped, then maybe some
// process/thread/frame info, and then we want the "(lldb) " prompt to
// show up. If we pop the process IOHandler here, then we will cause
// the command interpreter to become the top IOHandler after the
// process pops off and it will update its prompt right away... See the
// Debugger.cpp file where it calls the function as
// "process_sp->PopProcessIOHandler()" to see where I am talking about.
// Otherwise we end up getting overlapping "(lldb) " prompts and
// garbled output.
//
// If we aren't handling the events in the debugger (which is indicated
// by "m_target.GetDebugger().IsHandlingEvents()" returning false) or
// we are hijacked, then we always pop the process IO handler manually.
// Hijacking happens when the internal process state thread is running
// thread plans, or when commands want to run in synchronous mode and
// they call "process->WaitForProcessToStop()". An example of something
// that will hijack the events is a simple expression:
//
// (lldb) expr (int)puts("hello")
//
// This will cause the internal process state thread to resume and halt
// the process (and _it_ will hijack the eBroadcastBitStateChanged
// events) and we do need the IO handler to be pushed and popped
// correctly.
if (is_hijacked || !GetTarget().GetDebugger().IsHandlingEvents())
PopProcessIOHandler();
}
}
BroadcastEvent(event_sp);
} else {
if (log) {
LLDB_LOGF(
log,
"Process::%s (pid = %" PRIu64
") suppressing state %s (old state %s): should_broadcast == false",
__FUNCTION__, GetID(), StateAsCString(new_state),
StateAsCString(GetState()));
}
}
}
Status Process::HaltPrivate() {
EventSP event_sp;
Status error(WillHalt());
if (error.Fail())
return error;
// Ask the process subclass to actually halt our process
bool caused_stop;
error = DoHalt(caused_stop);
DidHalt();
return error;
}
thread_result_t Process::RunPrivateStateThread(bool is_secondary_thread) {
bool control_only = true;
Log *log = GetLog(LLDBLog::Process);
LLDB_LOGF(log, "Process::%s (arg = %p, pid = %" PRIu64 ") thread starting...",
__FUNCTION__, static_cast<void *>(this), GetID());
bool exit_now = false;
bool interrupt_requested = false;
while (!exit_now) {
EventSP event_sp;
GetEventsPrivate(event_sp, std::nullopt, control_only);
if (event_sp->BroadcasterIs(&m_private_state_control_broadcaster)) {
LLDB_LOGF(log,
"Process::%s (arg = %p, pid = %" PRIu64
") got a control event: %d",
__FUNCTION__, static_cast<void *>(this), GetID(),
event_sp->GetType());
switch (event_sp->GetType()) {
case eBroadcastInternalStateControlStop:
exit_now = true;
break; // doing any internal state management below
case eBroadcastInternalStateControlPause:
control_only = true;
break;
case eBroadcastInternalStateControlResume:
control_only = false;
break;
}
continue;
} else if (event_sp->GetType() == eBroadcastBitInterrupt) {
if (m_public_state.GetValue() == eStateAttaching) {
LLDB_LOGF(log,
"Process::%s (arg = %p, pid = %" PRIu64
") woke up with an interrupt while attaching - "
"forwarding interrupt.",
__FUNCTION__, static_cast<void *>(this), GetID());
// The server may be spinning waiting for a process to appear, in which
// case we should tell it to stop doing that. Normally, we don't NEED
// to do that because we will next close the communication to the stub
// and that will get it to shut down. But there are remote debugging
// cases where relying on that side-effect causes the shutdown to be
// flakey, so we should send a positive signal to interrupt the wait.
Status error = HaltPrivate();
BroadcastEvent(eBroadcastBitInterrupt, nullptr);
} else if (StateIsRunningState(m_last_broadcast_state)) {
LLDB_LOGF(log,
"Process::%s (arg = %p, pid = %" PRIu64
") woke up with an interrupt - Halting.",
__FUNCTION__, static_cast<void *>(this), GetID());
Status error = HaltPrivate();
if (error.Fail() && log)
LLDB_LOGF(log,
"Process::%s (arg = %p, pid = %" PRIu64
") failed to halt the process: %s",
__FUNCTION__, static_cast<void *>(this), GetID(),
error.AsCString());
// Halt should generate a stopped event. Make a note of the fact that
// we were doing the interrupt, so we can set the interrupted flag
// after we receive the event. We deliberately set this to true even if
// HaltPrivate failed, so that we can interrupt on the next natural
// stop.
interrupt_requested = true;
} else {
// This can happen when someone (e.g. Process::Halt) sees that we are
// running and sends an interrupt request, but the process actually
// stops before we receive it. In that case, we can just ignore the
// request. We use m_last_broadcast_state, because the Stopped event
// may not have been popped of the event queue yet, which is when the
// public state gets updated.
LLDB_LOGF(log,
"Process::%s ignoring interrupt as we have already stopped.",
__FUNCTION__);
}
continue;
}
const StateType internal_state =
Process::ProcessEventData::GetStateFromEvent(event_sp.get());
if (internal_state != eStateInvalid) {
if (m_clear_thread_plans_on_stop &&
StateIsStoppedState(internal_state, true)) {
m_clear_thread_plans_on_stop = false;
m_thread_list.DiscardThreadPlans();
}
if (interrupt_requested) {
if (StateIsStoppedState(internal_state, true)) {
// Only mark interrupt event if it is not thread specific async
// interrupt.
if (m_interrupt_tid == LLDB_INVALID_THREAD_ID) {
// We requested the interrupt, so mark this as such in the stop
// event so clients can tell an interrupted process from a natural
// stop
ProcessEventData::SetInterruptedInEvent(event_sp.get(), true);
}
interrupt_requested = false;
} else if (log) {
LLDB_LOGF(log,
"Process::%s interrupt_requested, but a non-stopped "
"state '%s' received.",
__FUNCTION__, StateAsCString(internal_state));
}
}
HandlePrivateEvent(event_sp);
}
if (internal_state == eStateInvalid || internal_state == eStateExited ||
internal_state == eStateDetached) {
LLDB_LOGF(log,
"Process::%s (arg = %p, pid = %" PRIu64
") about to exit with internal state %s...",
__FUNCTION__, static_cast<void *>(this), GetID(),
StateAsCString(internal_state));
break;
}
}
// Verify log is still enabled before attempting to write to it...
LLDB_LOGF(log, "Process::%s (arg = %p, pid = %" PRIu64 ") thread exiting...",
__FUNCTION__, static_cast<void *>(this), GetID());
// If we are a secondary thread, then the primary thread we are working for
// will have already acquired the public_run_lock, and isn't done with what
// it was doing yet, so don't try to change it on the way out.
if (!is_secondary_thread)
m_public_run_lock.SetStopped();
return {};
}
// Process Event Data
Process::ProcessEventData::ProcessEventData() : EventData(), m_process_wp() {}
Process::ProcessEventData::ProcessEventData(const ProcessSP &process_sp,
StateType state)
: EventData(), m_process_wp(), m_state(state) {
if (process_sp)
m_process_wp = process_sp;
}
Process::ProcessEventData::~ProcessEventData() = default;
llvm::StringRef Process::ProcessEventData::GetFlavorString() {
return "Process::ProcessEventData";
}
llvm::StringRef Process::ProcessEventData::GetFlavor() const {
return ProcessEventData::GetFlavorString();
}
bool Process::ProcessEventData::ShouldStop(Event *event_ptr,
bool &found_valid_stopinfo) {
found_valid_stopinfo = false;
ProcessSP process_sp(m_process_wp.lock());
if (!process_sp)
return false;
ThreadList &curr_thread_list = process_sp->GetThreadList();
uint32_t num_threads = curr_thread_list.GetSize();
// The actions might change one of the thread's stop_info's opinions about
// whether we should stop the process, so we need to query that as we go.
// One other complication here, is that we try to catch any case where the
// target has run (except for expressions) and immediately exit, but if we
// get that wrong (which is possible) then the thread list might have
// changed, and that would cause our iteration here to crash. We could
// make a copy of the thread list, but we'd really like to also know if it
// has changed at all, so we store the original thread ID's of all threads and
// check what we get back against this list & bag out if anything differs.
std::vector<std::pair<ThreadSP, size_t>> not_suspended_threads;
for (uint32_t idx = 0; idx < num_threads; ++idx) {
lldb::ThreadSP thread_sp = curr_thread_list.GetThreadAtIndex(idx);
/*
Filter out all suspended threads, they could not be the reason
of stop and no need to perform any actions on them.
*/
if (thread_sp->GetResumeState() != eStateSuspended)
not_suspended_threads.emplace_back(thread_sp, thread_sp->GetIndexID());
}
// Use this to track whether we should continue from here. We will only
// continue the target running if no thread says we should stop. Of course
// if some thread's PerformAction actually sets the target running, then it
// doesn't matter what the other threads say...
bool still_should_stop = false;
// Sometimes - for instance if we have a bug in the stub we are talking to,
// we stop but no thread has a valid stop reason. In that case we should
// just stop, because we have no way of telling what the right thing to do
// is, and it's better to let the user decide than continue behind their
// backs.
for (auto [thread_sp, thread_index] : not_suspended_threads) {
if (curr_thread_list.GetSize() != num_threads) {
Log *log(GetLog(LLDBLog::Step | LLDBLog::Process));
LLDB_LOGF(
log,
"Number of threads changed from %u to %u while processing event.",
num_threads, curr_thread_list.GetSize());
break;
}
if (thread_sp->GetIndexID() != thread_index) {
Log *log(GetLog(LLDBLog::Step | LLDBLog::Process));
LLDB_LOG(log,
"The thread {0} changed from {1} to {2} while processing event.",
thread_sp.get(), thread_index, thread_sp->GetIndexID());
break;
}
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
if (stop_info_sp && stop_info_sp->IsValid()) {
found_valid_stopinfo = true;
bool this_thread_wants_to_stop;
if (stop_info_sp->GetOverrideShouldStop()) {
this_thread_wants_to_stop =
stop_info_sp->GetOverriddenShouldStopValue();
} else {
stop_info_sp->PerformAction(event_ptr);
// The stop action might restart the target. If it does, then we
// want to mark that in the event so that whoever is receiving it
// will know to wait for the running event and reflect that state
// appropriately. We also need to stop processing actions, since they
// aren't expecting the target to be running.
// FIXME: we might have run.
if (stop_info_sp->HasTargetRunSinceMe()) {
SetRestarted(true);
break;
}
this_thread_wants_to_stop = stop_info_sp->ShouldStop(event_ptr);
}
if (!still_should_stop)
still_should_stop = this_thread_wants_to_stop;
}
}
return still_should_stop;
}
bool Process::ProcessEventData::ForwardEventToPendingListeners(
Event *event_ptr) {
// STDIO and the other async event notifications should always be forwarded.
if (event_ptr->GetType() != Process::eBroadcastBitStateChanged)
return true;
// For state changed events, if the update state is zero, we are handling
// this on the private state thread. We should wait for the public event.
return m_update_state == 1;
}
void Process::ProcessEventData::DoOnRemoval(Event *event_ptr) {
// We only have work to do for state changed events:
if (event_ptr->GetType() != Process::eBroadcastBitStateChanged)
return;
ProcessSP process_sp(m_process_wp.lock());
if (!process_sp)
return;
// This function gets called twice for each event, once when the event gets
// pulled off of the private process event queue, and then any number of
// times, first when it gets pulled off of the public event queue, then other
// times when we're pretending that this is where we stopped at the end of
// expression evaluation. m_update_state is used to distinguish these three
// cases; it is 0 when we're just pulling it off for private handling, and >
// 1 for expression evaluation, and we don't want to do the breakpoint
// command handling then.
if (m_update_state != 1)
return;
process_sp->SetPublicState(
m_state, Process::ProcessEventData::GetRestartedFromEvent(event_ptr));
if (m_state == eStateStopped && !m_restarted) {
// Let process subclasses know we are about to do a public stop and do
// anything they might need to in order to speed up register and memory
// accesses.
process_sp->WillPublicStop();
}
// If this is a halt event, even if the halt stopped with some reason other
// than a plain interrupt (e.g. we had already stopped for a breakpoint when
// the halt request came through) don't do the StopInfo actions, as they may
// end up restarting the process.
if (m_interrupted)
return;
// If we're not stopped or have restarted, then skip the StopInfo actions:
if (m_state != eStateStopped || m_restarted) {
return;
}
bool does_anybody_have_an_opinion = false;
bool still_should_stop = ShouldStop(event_ptr, does_anybody_have_an_opinion);
if (GetRestarted()) {
return;
}
if (!still_should_stop && does_anybody_have_an_opinion) {
// We've been asked to continue, so do that here.
SetRestarted(true);
// Use the private resume method here, since we aren't changing the run
// lock state.
process_sp->PrivateResume();
} else {
bool hijacked = process_sp->IsHijackedForEvent(eBroadcastBitStateChanged) &&
!process_sp->StateChangedIsHijackedForSynchronousResume();
if (!hijacked) {
// If we didn't restart, run the Stop Hooks here.
// Don't do that if state changed events aren't hooked up to the
// public (or SyncResume) broadcasters. StopHooks are just for
// real public stops. They might also restart the target,
// so watch for that.
if (process_sp->GetTarget().RunStopHooks())
SetRestarted(true);
}
}
}
void Process::ProcessEventData::Dump(Stream *s) const {
ProcessSP process_sp(m_process_wp.lock());
if (process_sp)
s->Printf(" process = %p (pid = %" PRIu64 "), ",
static_cast<void *>(process_sp.get()), process_sp->GetID());
else
s->PutCString(" process = NULL, ");
s->Printf("state = %s", StateAsCString(GetState()));
}
const Process::ProcessEventData *
Process::ProcessEventData::GetEventDataFromEvent(const Event *event_ptr) {
if (event_ptr) {
const EventData *event_data = event_ptr->GetData();
if (event_data &&
event_data->GetFlavor() == ProcessEventData::GetFlavorString())
return static_cast<const ProcessEventData *>(event_ptr->GetData());
}
return nullptr;
}
ProcessSP
Process::ProcessEventData::GetProcessFromEvent(const Event *event_ptr) {
ProcessSP process_sp;
const ProcessEventData *data = GetEventDataFromEvent(event_ptr);
if (data)
process_sp = data->GetProcessSP();
return process_sp;
}
StateType Process::ProcessEventData::GetStateFromEvent(const Event *event_ptr) {
const ProcessEventData *data = GetEventDataFromEvent(event_ptr);
if (data == nullptr)
return eStateInvalid;
else
return data->GetState();
}
bool Process::ProcessEventData::GetRestartedFromEvent(const Event *event_ptr) {
const ProcessEventData *data = GetEventDataFromEvent(event_ptr);
if (data == nullptr)
return false;
else
return data->GetRestarted();
}
void Process::ProcessEventData::SetRestartedInEvent(Event *event_ptr,
bool new_value) {
ProcessEventData *data =
const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr));
if (data != nullptr)
data->SetRestarted(new_value);
}
size_t
Process::ProcessEventData::GetNumRestartedReasons(const Event *event_ptr) {
ProcessEventData *data =
const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr));
if (data != nullptr)
return data->GetNumRestartedReasons();
else
return 0;
}
const char *
Process::ProcessEventData::GetRestartedReasonAtIndex(const Event *event_ptr,
size_t idx) {
ProcessEventData *data =
const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr));
if (data != nullptr)
return data->GetRestartedReasonAtIndex(idx);
else
return nullptr;
}
void Process::ProcessEventData::AddRestartedReason(Event *event_ptr,
const char *reason) {
ProcessEventData *data =
const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr));
if (data != nullptr)
data->AddRestartedReason(reason);
}
bool Process::ProcessEventData::GetInterruptedFromEvent(
const Event *event_ptr) {
const ProcessEventData *data = GetEventDataFromEvent(event_ptr);
if (data == nullptr)
return false;
else
return data->GetInterrupted();
}
void Process::ProcessEventData::SetInterruptedInEvent(Event *event_ptr,
bool new_value) {
ProcessEventData *data =
const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr));
if (data != nullptr)
data->SetInterrupted(new_value);
}
bool Process::ProcessEventData::SetUpdateStateOnRemoval(Event *event_ptr) {
ProcessEventData *data =
const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr));
if (data) {
data->SetUpdateStateOnRemoval();
return true;
}
return false;
}
lldb::TargetSP Process::CalculateTarget() { return m_target_wp.lock(); }
void Process::CalculateExecutionContext(ExecutionContext &exe_ctx) {
exe_ctx.SetTargetPtr(&GetTarget());
exe_ctx.SetProcessPtr(this);
exe_ctx.SetThreadPtr(nullptr);
exe_ctx.SetFramePtr(nullptr);
}
// uint32_t
// Process::ListProcessesMatchingName (const char *name, StringList &matches,
// std::vector<lldb::pid_t> &pids)
//{
// return 0;
//}
//
// ArchSpec
// Process::GetArchSpecForExistingProcess (lldb::pid_t pid)
//{
// return Host::GetArchSpecForExistingProcess (pid);
//}
//
// ArchSpec
// Process::GetArchSpecForExistingProcess (const char *process_name)
//{
// return Host::GetArchSpecForExistingProcess (process_name);
//}
EventSP Process::CreateEventFromProcessState(uint32_t event_type) {
auto event_data_sp =
std::make_shared<ProcessEventData>(shared_from_this(), GetState());
return std::make_shared<Event>(event_type, event_data_sp);
}
void Process::AppendSTDOUT(const char *s, size_t len) {
std::lock_guard<std::recursive_mutex> guard(m_stdio_communication_mutex);
m_stdout_data.append(s, len);
auto event_sp = CreateEventFromProcessState(eBroadcastBitSTDOUT);
BroadcastEventIfUnique(event_sp);
}
void Process::AppendSTDERR(const char *s, size_t len) {
std::lock_guard<std::recursive_mutex> guard(m_stdio_communication_mutex);
m_stderr_data.append(s, len);
auto event_sp = CreateEventFromProcessState(eBroadcastBitSTDERR);
BroadcastEventIfUnique(event_sp);
}
void Process::BroadcastAsyncProfileData(const std::string &one_profile_data) {
std::lock_guard<std::recursive_mutex> guard(m_profile_data_comm_mutex);
m_profile_data.push_back(one_profile_data);
auto event_sp = CreateEventFromProcessState(eBroadcastBitProfileData);
BroadcastEventIfUnique(event_sp);
}
void Process::BroadcastStructuredData(const StructuredData::ObjectSP &object_sp,
const StructuredDataPluginSP &plugin_sp) {
auto data_sp = std::make_shared<EventDataStructuredData>(
shared_from_this(), object_sp, plugin_sp);
BroadcastEvent(eBroadcastBitStructuredData, data_sp);
}
StructuredDataPluginSP
Process::GetStructuredDataPlugin(llvm::StringRef type_name) const {
auto find_it = m_structured_data_plugin_map.find(type_name);
if (find_it != m_structured_data_plugin_map.end())
return find_it->second;
else
return StructuredDataPluginSP();
}
size_t Process::GetAsyncProfileData(char *buf, size_t buf_size, Status &error) {
std::lock_guard<std::recursive_mutex> guard(m_profile_data_comm_mutex);
if (m_profile_data.empty())
return 0;
std::string &one_profile_data = m_profile_data.front();
size_t bytes_available = one_profile_data.size();
if (bytes_available > 0) {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOGF(log, "Process::GetProfileData (buf = %p, size = %" PRIu64 ")",
static_cast<void *>(buf), static_cast<uint64_t>(buf_size));
if (bytes_available > buf_size) {
memcpy(buf, one_profile_data.c_str(), buf_size);
one_profile_data.erase(0, buf_size);
bytes_available = buf_size;
} else {
memcpy(buf, one_profile_data.c_str(), bytes_available);
m_profile_data.erase(m_profile_data.begin());
}
}
return bytes_available;
}
// Process STDIO
size_t Process::GetSTDOUT(char *buf, size_t buf_size, Status &error) {
std::lock_guard<std::recursive_mutex> guard(m_stdio_communication_mutex);
size_t bytes_available = m_stdout_data.size();
if (bytes_available > 0) {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOGF(log, "Process::GetSTDOUT (buf = %p, size = %" PRIu64 ")",
static_cast<void *>(buf), static_cast<uint64_t>(buf_size));
if (bytes_available > buf_size) {
memcpy(buf, m_stdout_data.c_str(), buf_size);
m_stdout_data.erase(0, buf_size);
bytes_available = buf_size;
} else {
memcpy(buf, m_stdout_data.c_str(), bytes_available);
m_stdout_data.clear();
}
}
return bytes_available;
}
size_t Process::GetSTDERR(char *buf, size_t buf_size, Status &error) {
std::lock_guard<std::recursive_mutex> gaurd(m_stdio_communication_mutex);
size_t bytes_available = m_stderr_data.size();
if (bytes_available > 0) {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOGF(log, "Process::GetSTDERR (buf = %p, size = %" PRIu64 ")",
static_cast<void *>(buf), static_cast<uint64_t>(buf_size));
if (bytes_available > buf_size) {
memcpy(buf, m_stderr_data.c_str(), buf_size);
m_stderr_data.erase(0, buf_size);
bytes_available = buf_size;
} else {
memcpy(buf, m_stderr_data.c_str(), bytes_available);
m_stderr_data.clear();
}
}
return bytes_available;
}
void Process::STDIOReadThreadBytesReceived(void *baton, const void *src,
size_t src_len) {
Process *process = (Process *)baton;
process->AppendSTDOUT(static_cast<const char *>(src), src_len);
}
class IOHandlerProcessSTDIO : public IOHandler {
public:
IOHandlerProcessSTDIO(Process *process, int write_fd)
: IOHandler(process->GetTarget().GetDebugger(),
IOHandler::Type::ProcessIO),
m_process(process),
m_read_file(GetInputFD(), File::eOpenOptionReadOnly, false),
m_write_file(write_fd, File::eOpenOptionWriteOnly, false) {
m_pipe.CreateNew();
}
~IOHandlerProcessSTDIO() override = default;
void SetIsRunning(bool running) {
std::lock_guard<std::mutex> guard(m_mutex);
SetIsDone(!running);
m_is_running = running;
}
// Each IOHandler gets to run until it is done. It should read data from the
// "in" and place output into "out" and "err and return when done.
void Run() override {
if (!m_read_file.IsValid() || !m_write_file.IsValid() ||
!m_pipe.CanRead() || !m_pipe.CanWrite()) {
SetIsDone(true);
return;
}
SetIsDone(false);
const int read_fd = m_read_file.GetDescriptor();
Terminal terminal(read_fd);
TerminalState terminal_state(terminal, false);
// FIXME: error handling?
llvm::consumeError(terminal.SetCanonical(false));
llvm::consumeError(terminal.SetEcho(false));
// FD_ZERO, FD_SET are not supported on windows
#ifndef _WIN32
const int pipe_read_fd = m_pipe.GetReadFileDescriptor();
SetIsRunning(true);
while (true) {
{
std::lock_guard<std::mutex> guard(m_mutex);
if (GetIsDone())
break;
}
SelectHelper select_helper;
select_helper.FDSetRead(read_fd);
select_helper.FDSetRead(pipe_read_fd);
Status error = select_helper.Select();
if (error.Fail())
break;
char ch = 0;
size_t n;
if (select_helper.FDIsSetRead(read_fd)) {
n = 1;
if (m_read_file.Read(&ch, n).Success() && n == 1) {
if (m_write_file.Write(&ch, n).Fail() || n != 1)
break;
} else
break;
}
if (select_helper.FDIsSetRead(pipe_read_fd)) {
// Consume the interrupt byte
if (llvm::Expected<size_t> bytes_read = m_pipe.Read(&ch, 1)) {
if (ch == 'q')
break;
if (ch == 'i')
if (StateIsRunningState(m_process->GetState()))
m_process->SendAsyncInterrupt();
} else {
LLDB_LOG_ERROR(GetLog(LLDBLog::Process), bytes_read.takeError(),
"Pipe read failed: {0}");
}
}
}
SetIsRunning(false);
#endif
}
void Cancel() override {
std::lock_guard<std::mutex> guard(m_mutex);
SetIsDone(true);
// Only write to our pipe to cancel if we are in
// IOHandlerProcessSTDIO::Run(). We can end up with a python command that
// is being run from the command interpreter:
//
// (lldb) step_process_thousands_of_times
//
// In this case the command interpreter will be in the middle of handling
// the command and if the process pushes and pops the IOHandler thousands
// of times, we can end up writing to m_pipe without ever consuming the
// bytes from the pipe in IOHandlerProcessSTDIO::Run() and end up
// deadlocking when the pipe gets fed up and blocks until data is consumed.
if (m_is_running) {
char ch = 'q'; // Send 'q' for quit
if (llvm::Error err = m_pipe.Write(&ch, 1).takeError()) {
LLDB_LOG_ERROR(GetLog(LLDBLog::Process), std::move(err),
"Pipe write failed: {0}");
}
}
}
bool Interrupt() override {
// Do only things that are safe to do in an interrupt context (like in a
// SIGINT handler), like write 1 byte to a file descriptor. This will
// interrupt the IOHandlerProcessSTDIO::Run() and we can look at the byte
// that was written to the pipe and then call
// m_process->SendAsyncInterrupt() from a much safer location in code.
if (m_active) {
char ch = 'i'; // Send 'i' for interrupt
return !errorToBool(m_pipe.Write(&ch, 1).takeError());
} else {
// This IOHandler might be pushed on the stack, but not being run
// currently so do the right thing if we aren't actively watching for
// STDIN by sending the interrupt to the process. Otherwise the write to
// the pipe above would do nothing. This can happen when the command
// interpreter is running and gets a "expression ...". It will be on the
// IOHandler thread and sending the input is complete to the delegate
// which will cause the expression to run, which will push the process IO
// handler, but not run it.
if (StateIsRunningState(m_process->GetState())) {
m_process->SendAsyncInterrupt();
return true;
}
}
return false;
}
void GotEOF() override {}
protected:
Process *m_process;
NativeFile m_read_file; // Read from this file (usually actual STDIN for LLDB
NativeFile m_write_file; // Write to this file (usually the primary pty for
// getting io to debuggee)
Pipe m_pipe;
std::mutex m_mutex;
bool m_is_running = false;
};
void Process::SetSTDIOFileDescriptor(int fd) {
// First set up the Read Thread for reading/handling process I/O
m_stdio_communication.SetConnection(
std::make_unique<ConnectionFileDescriptor>(fd, true));
if (m_stdio_communication.IsConnected()) {
m_stdio_communication.SetReadThreadBytesReceivedCallback(
STDIOReadThreadBytesReceived, this);
m_stdio_communication.StartReadThread();
// Now read thread is set up, set up input reader.
{
std::lock_guard<std::mutex> guard(m_process_input_reader_mutex);
if (!m_process_input_reader)
m_process_input_reader =
std::make_shared<IOHandlerProcessSTDIO>(this, fd);
}
}
}
bool Process::ProcessIOHandlerIsActive() {
std::lock_guard<std::mutex> guard(m_process_input_reader_mutex);
IOHandlerSP io_handler_sp(m_process_input_reader);
if (io_handler_sp)
return GetTarget().GetDebugger().IsTopIOHandler(io_handler_sp);
return false;
}
bool Process::PushProcessIOHandler() {
std::lock_guard<std::mutex> guard(m_process_input_reader_mutex);
IOHandlerSP io_handler_sp(m_process_input_reader);
if (io_handler_sp) {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOGF(log, "Process::%s pushing IO handler", __FUNCTION__);
io_handler_sp->SetIsDone(false);
// If we evaluate an utility function, then we don't cancel the current
// IOHandler. Our IOHandler is non-interactive and shouldn't disturb the
// existing IOHandler that potentially provides the user interface (e.g.
// the IOHandler for Editline).
bool cancel_top_handler = !m_mod_id.IsRunningUtilityFunction();
GetTarget().GetDebugger().RunIOHandlerAsync(io_handler_sp,
cancel_top_handler);
return true;
}
return false;
}
bool Process::PopProcessIOHandler() {
std::lock_guard<std::mutex> guard(m_process_input_reader_mutex);
IOHandlerSP io_handler_sp(m_process_input_reader);
if (io_handler_sp)
return GetTarget().GetDebugger().RemoveIOHandler(io_handler_sp);
return false;
}
// The process needs to know about installed plug-ins
void Process::SettingsInitialize() { Thread::SettingsInitialize(); }
void Process::SettingsTerminate() { Thread::SettingsTerminate(); }
namespace {
// RestorePlanState is used to record the "is private", "is controlling" and
// "okay
// to discard" fields of the plan we are running, and reset it on Clean or on
// destruction. It will only reset the state once, so you can call Clean and
// then monkey with the state and it won't get reset on you again.
class RestorePlanState {
public:
RestorePlanState(lldb::ThreadPlanSP thread_plan_sp)
: m_thread_plan_sp(thread_plan_sp) {
if (m_thread_plan_sp) {
m_private = m_thread_plan_sp->GetPrivate();
m_is_controlling = m_thread_plan_sp->IsControllingPlan();
m_okay_to_discard = m_thread_plan_sp->OkayToDiscard();
}
}
~RestorePlanState() { Clean(); }
void Clean() {
if (!m_already_reset && m_thread_plan_sp) {
m_already_reset = true;
m_thread_plan_sp->SetPrivate(m_private);
m_thread_plan_sp->SetIsControllingPlan(m_is_controlling);
m_thread_plan_sp->SetOkayToDiscard(m_okay_to_discard);
}
}
private:
lldb::ThreadPlanSP m_thread_plan_sp;
bool m_already_reset = false;
bool m_private = false;
bool m_is_controlling = false;
bool m_okay_to_discard = false;
};
} // anonymous namespace
static microseconds
GetOneThreadExpressionTimeout(const EvaluateExpressionOptions &options) {
const milliseconds default_one_thread_timeout(250);
// If the overall wait is forever, then we don't need to worry about it.
if (!options.GetTimeout()) {
return options.GetOneThreadTimeout() ? *options.GetOneThreadTimeout()
: default_one_thread_timeout;
}
// If the one thread timeout is set, use it.
if (options.GetOneThreadTimeout())
return *options.GetOneThreadTimeout();
// Otherwise use half the total timeout, bounded by the
// default_one_thread_timeout.
return std::min<microseconds>(default_one_thread_timeout,
*options.GetTimeout() / 2);
}
static Timeout<std::micro>
GetExpressionTimeout(const EvaluateExpressionOptions &options,
bool before_first_timeout) {
// If we are going to run all threads the whole time, or if we are only going
// to run one thread, we can just return the overall timeout.
if (!options.GetStopOthers() || !options.GetTryAllThreads())
return options.GetTimeout();
if (before_first_timeout)
return GetOneThreadExpressionTimeout(options);
if (!options.GetTimeout())
return std::nullopt;
else
return *options.GetTimeout() - GetOneThreadExpressionTimeout(options);
}
static std::optional<ExpressionResults>
HandleStoppedEvent(lldb::tid_t thread_id, const ThreadPlanSP &thread_plan_sp,
RestorePlanState &restorer, const EventSP &event_sp,
EventSP &event_to_broadcast_sp,
const EvaluateExpressionOptions &options,
bool handle_interrupts) {
Log *log = GetLog(LLDBLog::Step | LLDBLog::Process);
ThreadSP thread_sp = thread_plan_sp->GetTarget()
.GetProcessSP()
->GetThreadList()
.FindThreadByID(thread_id);
if (!thread_sp) {
LLDB_LOG(log,
"The thread on which we were running the "
"expression: tid = {0}, exited while "
"the expression was running.",
thread_id);
return eExpressionThreadVanished;
}
ThreadPlanSP plan = thread_sp->GetCompletedPlan();
if (plan == thread_plan_sp && plan->PlanSucceeded()) {
LLDB_LOG(log, "execution completed successfully");
// Restore the plan state so it will get reported as intended when we are
// done.
restorer.Clean();
return eExpressionCompleted;
}
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonBreakpoint &&
stop_info_sp->ShouldNotify(event_sp.get())) {
LLDB_LOG(log, "stopped for breakpoint: {0}.", stop_info_sp->GetDescription());
if (!options.DoesIgnoreBreakpoints()) {
// Restore the plan state and then force Private to false. We are going
// to stop because of this plan so we need it to become a public plan or
// it won't report correctly when we continue to its termination later
// on.
restorer.Clean();
thread_plan_sp->SetPrivate(false);
event_to_broadcast_sp = event_sp;
}
return eExpressionHitBreakpoint;
}
if (!handle_interrupts &&
Process::ProcessEventData::GetInterruptedFromEvent(event_sp.get()))
return std::nullopt;
LLDB_LOG(log, "thread plan did not successfully complete");
if (!options.DoesUnwindOnError())
event_to_broadcast_sp = event_sp;
return eExpressionInterrupted;
}
ExpressionResults
Process::RunThreadPlan(ExecutionContext &exe_ctx,
lldb::ThreadPlanSP &thread_plan_sp,
const EvaluateExpressionOptions &options,
DiagnosticManager &diagnostic_manager) {
ExpressionResults return_value = eExpressionSetupError;
std::lock_guard<std::mutex> run_thread_plan_locker(m_run_thread_plan_lock);
if (!thread_plan_sp) {
diagnostic_manager.PutString(
lldb::eSeverityError, "RunThreadPlan called with empty thread plan.");
return eExpressionSetupError;
}
if (!thread_plan_sp->ValidatePlan(nullptr)) {
diagnostic_manager.PutString(
lldb::eSeverityError,
"RunThreadPlan called with an invalid thread plan.");
return eExpressionSetupError;
}
if (exe_ctx.GetProcessPtr() != this) {
diagnostic_manager.PutString(lldb::eSeverityError,
"RunThreadPlan called on wrong process.");
return eExpressionSetupError;
}
Thread *thread = exe_ctx.GetThreadPtr();
if (thread == nullptr) {
diagnostic_manager.PutString(lldb::eSeverityError,
"RunThreadPlan called with invalid thread.");
return eExpressionSetupError;
}
// Record the thread's id so we can tell when a thread we were using
// to run the expression exits during the expression evaluation.
lldb::tid_t expr_thread_id = thread->GetID();
// We need to change some of the thread plan attributes for the thread plan
// runner. This will restore them when we are done:
RestorePlanState thread_plan_restorer(thread_plan_sp);
// We rely on the thread plan we are running returning "PlanCompleted" if
// when it successfully completes. For that to be true the plan can't be
// private - since private plans suppress themselves in the GetCompletedPlan
// call.
thread_plan_sp->SetPrivate(false);
// The plans run with RunThreadPlan also need to be terminal controlling plans
// or when they are done we will end up asking the plan above us whether we
// should stop, which may give the wrong answer.
thread_plan_sp->SetIsControllingPlan(true);
thread_plan_sp->SetOkayToDiscard(false);
// If we are running some utility expression for LLDB, we now have to mark
// this in the ProcesModID of this process. This RAII takes care of marking
// and reverting the mark it once we are done running the expression.
UtilityFunctionScope util_scope(options.IsForUtilityExpr() ? this : nullptr);
if (m_private_state.GetValue() != eStateStopped) {
diagnostic_manager.PutString(
lldb::eSeverityError,
"RunThreadPlan called while the private state was not stopped.");
return eExpressionSetupError;
}
// Save the thread & frame from the exe_ctx for restoration after we run
const uint32_t thread_idx_id = thread->GetIndexID();
StackFrameSP selected_frame_sp =
thread->GetSelectedFrame(DoNoSelectMostRelevantFrame);
if (!selected_frame_sp) {
thread->SetSelectedFrame(nullptr);
selected_frame_sp = thread->GetSelectedFrame(DoNoSelectMostRelevantFrame);
if (!selected_frame_sp) {
diagnostic_manager.Printf(
lldb::eSeverityError,
"RunThreadPlan called without a selected frame on thread %d",
thread_idx_id);
return eExpressionSetupError;
}
}
// Make sure the timeout values make sense. The one thread timeout needs to
// be smaller than the overall timeout.
if (options.GetOneThreadTimeout() && options.GetTimeout() &&
*options.GetTimeout() < *options.GetOneThreadTimeout()) {
diagnostic_manager.PutString(lldb::eSeverityError,
"RunThreadPlan called with one thread "
"timeout greater than total timeout");
return eExpressionSetupError;
}
// If the ExecutionContext has a frame, we want to make sure to save/restore
// that frame into exe_ctx. This can happen when we run expressions from a
// non-selected SBFrame, in which case we don't want some thread-plan
// to overwrite the ExecutionContext frame.
StackID ctx_frame_id = exe_ctx.HasFrameScope()
? exe_ctx.GetFrameRef().GetStackID()
: selected_frame_sp->GetStackID();
// N.B. Running the target may unset the currently selected thread and frame.
// We don't want to do that either, so we should arrange to reset them as
// well.
lldb::ThreadSP selected_thread_sp = GetThreadList().GetSelectedThread();
uint32_t selected_tid;
StackID selected_stack_id;
if (selected_thread_sp) {
selected_tid = selected_thread_sp->GetIndexID();
selected_stack_id =
selected_thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame)
->GetStackID();
} else {
selected_tid = LLDB_INVALID_THREAD_ID;
}
HostThread backup_private_state_thread;
lldb::StateType old_state = eStateInvalid;
lldb::ThreadPlanSP stopper_base_plan_sp;
Log *log(GetLog(LLDBLog::Step | LLDBLog::Process));
if (m_private_state_thread.EqualsThread(Host::GetCurrentThread())) {
// Yikes, we are running on the private state thread! So we can't wait for
// public events on this thread, since we are the thread that is generating
// public events. The simplest thing to do is to spin up a temporary thread
// to handle private state thread events while we are fielding public
// events here.
LLDB_LOGF(log, "Running thread plan on private state thread, spinning up "
"another state thread to handle the events.");
backup_private_state_thread = m_private_state_thread;
// One other bit of business: we want to run just this thread plan and
// anything it pushes, and then stop, returning control here. But in the
// normal course of things, the plan above us on the stack would be given a
// shot at the stop event before deciding to stop, and we don't want that.
// So we insert a "stopper" base plan on the stack before the plan we want
// to run. Since base plans always stop and return control to the user,
// that will do just what we want.
stopper_base_plan_sp.reset(new ThreadPlanBase(*thread));
thread->QueueThreadPlan(stopper_base_plan_sp, false);
// Have to make sure our public state is stopped, since otherwise the
// reporting logic below doesn't work correctly.
old_state = m_public_state.GetValue();
m_public_state.SetValueNoLock(eStateStopped);
// Now spin up the private state thread:
StartPrivateStateThread(true);
}
thread->QueueThreadPlan(
thread_plan_sp, false); // This used to pass "true" does that make sense?
if (options.GetDebug()) {
// In this case, we aren't actually going to run, we just want to stop
// right away. Flush this thread so we will refetch the stacks and show the
// correct backtrace.
// FIXME: To make this prettier we should invent some stop reason for this,
// but that
// is only cosmetic, and this functionality is only of use to lldb
// developers who can live with not pretty...
thread->Flush();
return eExpressionStoppedForDebug;
}
ListenerSP listener_sp(
Listener::MakeListener("lldb.process.listener.run-thread-plan"));
lldb::EventSP event_to_broadcast_sp;
{
// This process event hijacker Hijacks the Public events and its destructor
// makes sure that the process events get restored on exit to the function.
//
// If the event needs to propagate beyond the hijacker (e.g., the process
// exits during execution), then the event is put into
// event_to_broadcast_sp for rebroadcasting.
ProcessEventHijacker run_thread_plan_hijacker(*this, listener_sp);
if (log) {
StreamString s;
thread_plan_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
LLDB_LOGF(log,
"Process::RunThreadPlan(): Resuming thread %u - 0x%4.4" PRIx64
" to run thread plan \"%s\".",
thread_idx_id, expr_thread_id, s.GetData());
}
bool got_event;
lldb::EventSP event_sp;
lldb::StateType stop_state = lldb::eStateInvalid;
bool before_first_timeout = true; // This is set to false the first time
// that we have to halt the target.
bool do_resume = true;
bool handle_running_event = true;
// This is just for accounting:
uint32_t num_resumes = 0;
// If we are going to run all threads the whole time, or if we are only
// going to run one thread, then we don't need the first timeout. So we
// pretend we are after the first timeout already.
if (!options.GetStopOthers() || !options.GetTryAllThreads())
before_first_timeout = false;
LLDB_LOGF(log, "Stop others: %u, try all: %u, before_first: %u.\n",
options.GetStopOthers(), options.GetTryAllThreads(),
before_first_timeout);
// This isn't going to work if there are unfetched events on the queue. Are
// there cases where we might want to run the remaining events here, and
// then try to call the function? That's probably being too tricky for our
// own good.
Event *other_events = listener_sp->PeekAtNextEvent();
if (other_events != nullptr) {
diagnostic_manager.PutString(
lldb::eSeverityError,
"RunThreadPlan called with pending events on the queue.");
return eExpressionSetupError;
}
// We also need to make sure that the next event is delivered. We might be
// calling a function as part of a thread plan, in which case the last
// delivered event could be the running event, and we don't want event
// coalescing to cause us to lose OUR running event...
ForceNextEventDelivery();
// This while loop must exit out the bottom, there's cleanup that we need to do
// when we are done. So don't call return anywhere within it.
#ifdef LLDB_RUN_THREAD_HALT_WITH_EVENT
// It's pretty much impossible to write test cases for things like: One
// thread timeout expires, I go to halt, but the process already stopped on
// the function call stop breakpoint. Turning on this define will make us
// not fetch the first event till after the halt. So if you run a quick
// function, it will have completed, and the completion event will be
// waiting, when you interrupt for halt. The expression evaluation should
// still succeed.
bool miss_first_event = true;
#endif
while (true) {
// We usually want to resume the process if we get to the top of the
// loop. The only exception is if we get two running events with no
// intervening stop, which can happen, we will just wait for then next
// stop event.
LLDB_LOGF(log,
"Top of while loop: do_resume: %i handle_running_event: %i "
"before_first_timeout: %i.",
do_resume, handle_running_event, before_first_timeout);
if (do_resume || handle_running_event) {
// Do the initial resume and wait for the running event before going
// further.
if (do_resume) {
num_resumes++;
Status resume_error = PrivateResume();
if (!resume_error.Success()) {
diagnostic_manager.Printf(
lldb::eSeverityError,
"couldn't resume inferior the %d time: \"%s\".", num_resumes,
resume_error.AsCString());
return_value = eExpressionSetupError;
break;
}
}
got_event =
listener_sp->GetEvent(event_sp, GetUtilityExpressionTimeout());
if (!got_event) {
LLDB_LOGF(log,
"Process::RunThreadPlan(): didn't get any event after "
"resume %" PRIu32 ", exiting.",
num_resumes);
diagnostic_manager.Printf(lldb::eSeverityError,
"didn't get any event after resume %" PRIu32
", exiting.",
num_resumes);
return_value = eExpressionSetupError;
break;
}
stop_state =
Process::ProcessEventData::GetStateFromEvent(event_sp.get());
if (stop_state != eStateRunning) {
bool restarted = false;
if (stop_state == eStateStopped) {
restarted = Process::ProcessEventData::GetRestartedFromEvent(
event_sp.get());
LLDB_LOGF(
log,
"Process::RunThreadPlan(): didn't get running event after "
"resume %d, got %s instead (restarted: %i, do_resume: %i, "
"handle_running_event: %i).",
num_resumes, StateAsCString(stop_state), restarted, do_resume,
handle_running_event);
}
if (restarted) {
// This is probably an overabundance of caution, I don't think I
// should ever get a stopped & restarted event here. But if I do,
// the best thing is to Halt and then get out of here.
const bool clear_thread_plans = false;
const bool use_run_lock = false;
Halt(clear_thread_plans, use_run_lock);
}
diagnostic_manager.Printf(
lldb::eSeverityError,
"didn't get running event after initial resume, got %s instead.",
StateAsCString(stop_state));
return_value = eExpressionSetupError;
break;
}
if (log)
log->PutCString("Process::RunThreadPlan(): resuming succeeded.");
// We need to call the function synchronously, so spin waiting for it
// to return. If we get interrupted while executing, we're going to
// lose our context, and won't be able to gather the result at this
// point. We set the timeout AFTER the resume, since the resume takes
// some time and we don't want to charge that to the timeout.
} else {
if (log)
log->PutCString("Process::RunThreadPlan(): waiting for next event.");
}
do_resume = true;
handle_running_event = true;
// Now wait for the process to stop again:
event_sp.reset();
Timeout<std::micro> timeout =
GetExpressionTimeout(options, before_first_timeout);
if (log) {
if (timeout) {
auto now = system_clock::now();
LLDB_LOGF(log,
"Process::RunThreadPlan(): about to wait - now is %s - "
"endpoint is %s",
llvm::to_string(now).c_str(),
llvm::to_string(now + *timeout).c_str());
} else {
LLDB_LOGF(log, "Process::RunThreadPlan(): about to wait forever.");
}
}
#ifdef LLDB_RUN_THREAD_HALT_WITH_EVENT
// See comment above...
if (miss_first_event) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
miss_first_event = false;
got_event = false;
} else
#endif
got_event = listener_sp->GetEvent(event_sp, timeout);
if (got_event) {
if (event_sp) {
bool keep_going = false;
if (event_sp->GetType() == eBroadcastBitInterrupt) {
const bool clear_thread_plans = false;
const bool use_run_lock = false;
Halt(clear_thread_plans, use_run_lock);
return_value = eExpressionInterrupted;
diagnostic_manager.PutString(lldb::eSeverityInfo,
"execution halted by user interrupt.");
LLDB_LOGF(log, "Process::RunThreadPlan(): Got interrupted by "
"eBroadcastBitInterrupted, exiting.");
break;
} else {
stop_state =
Process::ProcessEventData::GetStateFromEvent(event_sp.get());
LLDB_LOGF(log,
"Process::RunThreadPlan(): in while loop, got event: %s.",
StateAsCString(stop_state));
switch (stop_state) {
case lldb::eStateStopped: {
if (Process::ProcessEventData::GetRestartedFromEvent(
event_sp.get())) {
// If we were restarted, we just need to go back up to fetch
// another event.
LLDB_LOGF(log, "Process::RunThreadPlan(): Got a stop and "
"restart, so we'll continue waiting.");
keep_going = true;
do_resume = false;
handle_running_event = true;
} else {
const bool handle_interrupts = true;
return_value = *HandleStoppedEvent(
expr_thread_id, thread_plan_sp, thread_plan_restorer,
event_sp, event_to_broadcast_sp, options,
handle_interrupts);
if (return_value == eExpressionThreadVanished)
keep_going = false;
}
} break;
case lldb::eStateRunning:
// This shouldn't really happen, but sometimes we do get two
// running events without an intervening stop, and in that case
// we should just go back to waiting for the stop.
do_resume = false;
keep_going = true;
handle_running_event = false;
break;
default:
LLDB_LOGF(log,
"Process::RunThreadPlan(): execution stopped with "
"unexpected state: %s.",
StateAsCString(stop_state));
if (stop_state == eStateExited)
event_to_broadcast_sp = event_sp;
diagnostic_manager.PutString(
lldb::eSeverityError,
"execution stopped with unexpected state.");
return_value = eExpressionInterrupted;
break;
}
}
if (keep_going)
continue;
else
break;
} else {
if (log)
log->PutCString("Process::RunThreadPlan(): got_event was true, but "
"the event pointer was null. How odd...");
return_value = eExpressionInterrupted;
break;
}
} else {
// If we didn't get an event that means we've timed out... We will
// interrupt the process here. Depending on what we were asked to do
// we will either exit, or try with all threads running for the same
// timeout.
if (log) {
if (options.GetTryAllThreads()) {
if (before_first_timeout) {
LLDB_LOG(log,
"Running function with one thread timeout timed out.");
} else
LLDB_LOG(log, "Restarting function with all threads enabled and "
"timeout: {0} timed out, abandoning execution.",
timeout);
} else
LLDB_LOG(log, "Running function with timeout: {0} timed out, "
"abandoning execution.",
timeout);
}
// It is possible that between the time we issued the Halt, and we get
// around to calling Halt the target could have stopped. That's fine,
// Halt will figure that out and send the appropriate Stopped event.
// BUT it is also possible that we stopped & restarted (e.g. hit a
// signal with "stop" set to false.) In
// that case, we'll get the stopped & restarted event, and we should go
// back to waiting for the Halt's stopped event. That's what this
// while loop does.
bool back_to_top = true;
uint32_t try_halt_again = 0;
bool do_halt = true;
const uint32_t num_retries = 5;
while (try_halt_again < num_retries) {
Status halt_error;
if (do_halt) {
LLDB_LOGF(log, "Process::RunThreadPlan(): Running Halt.");
const bool clear_thread_plans = false;
const bool use_run_lock = false;
Halt(clear_thread_plans, use_run_lock);
}
if (halt_error.Success()) {
if (log)
log->PutCString("Process::RunThreadPlan(): Halt succeeded.");
got_event =
listener_sp->GetEvent(event_sp, GetUtilityExpressionTimeout());
if (got_event) {
stop_state =
Process::ProcessEventData::GetStateFromEvent(event_sp.get());
if (log) {
LLDB_LOGF(log,
"Process::RunThreadPlan(): Stopped with event: %s",
StateAsCString(stop_state));
if (stop_state == lldb::eStateStopped &&
Process::ProcessEventData::GetInterruptedFromEvent(
event_sp.get()))
log->PutCString(" Event was the Halt interruption event.");
}
if (stop_state == lldb::eStateStopped) {
if (Process::ProcessEventData::GetRestartedFromEvent(
event_sp.get())) {
if (log)
log->PutCString("Process::RunThreadPlan(): Went to halt "
"but got a restarted event, there must be "
"an un-restarted stopped event so try "
"again... "
"Exiting wait loop.");
try_halt_again++;
do_halt = false;
continue;
}
// Between the time we initiated the Halt and the time we
// delivered it, the process could have already finished its
// job. Check that here:
const bool handle_interrupts = false;
if (auto result = HandleStoppedEvent(
expr_thread_id, thread_plan_sp, thread_plan_restorer,
event_sp, event_to_broadcast_sp, options,
handle_interrupts)) {
return_value = *result;
back_to_top = false;
break;
}
if (!options.GetTryAllThreads()) {
if (log)
log->PutCString("Process::RunThreadPlan(): try_all_threads "
"was false, we stopped so now we're "
"quitting.");
return_value = eExpressionInterrupted;
back_to_top = false;
break;
}
if (before_first_timeout) {
// Set all the other threads to run, and return to the top of
// the loop, which will continue;
before_first_timeout = false;
thread_plan_sp->SetStopOthers(false);
if (log)
log->PutCString(
"Process::RunThreadPlan(): about to resume.");
back_to_top = true;
break;
} else {
// Running all threads failed, so return Interrupted.
if (log)
log->PutCString("Process::RunThreadPlan(): running all "
"threads timed out.");
return_value = eExpressionInterrupted;
back_to_top = false;
break;
}
}
} else {
if (log)
log->PutCString("Process::RunThreadPlan(): halt said it "
"succeeded, but I got no event. "
"I'm getting out of here passing Interrupted.");
return_value = eExpressionInterrupted;
back_to_top = false;
break;
}
} else {
try_halt_again++;
continue;
}
}
if (!back_to_top || try_halt_again > num_retries)
break;
else
continue;
}
} // END WAIT LOOP
// If we had to start up a temporary private state thread to run this
// thread plan, shut it down now.
if (backup_private_state_thread.IsJoinable()) {
StopPrivateStateThread();
Status error;
m_private_state_thread = backup_private_state_thread;
if (stopper_base_plan_sp) {
thread->DiscardThreadPlansUpToPlan(stopper_base_plan_sp);
}
if (old_state != eStateInvalid)
m_public_state.SetValueNoLock(old_state);
}
// If our thread went away on us, we need to get out of here without
// doing any more work. We don't have to clean up the thread plan, that
// will have happened when the Thread was destroyed.
if (return_value == eExpressionThreadVanished) {
return return_value;
}
if (return_value != eExpressionCompleted && log) {
// Print a backtrace into the log so we can figure out where we are:
StreamString s;
s.PutCString("Thread state after unsuccessful completion: \n");
thread->GetStackFrameStatus(s, 0, UINT32_MAX, true, UINT32_MAX,
/*show_hidden*/ true);
log->PutString(s.GetString());
}
// Restore the thread state if we are going to discard the plan execution.
// There are three cases where this could happen: 1) The execution
// successfully completed 2) We hit a breakpoint, and ignore_breakpoints
// was true 3) We got some other error, and discard_on_error was true
bool should_unwind = (return_value == eExpressionInterrupted &&
options.DoesUnwindOnError()) ||
(return_value == eExpressionHitBreakpoint &&
options.DoesIgnoreBreakpoints());
if (return_value == eExpressionCompleted || should_unwind) {
thread_plan_sp->RestoreThreadState();
}
// Now do some processing on the results of the run:
if (return_value == eExpressionInterrupted ||
return_value == eExpressionHitBreakpoint) {
if (log) {
StreamString s;
if (event_sp)
event_sp->Dump(&s);
else {
log->PutCString("Process::RunThreadPlan(): Stop event that "
"interrupted us is NULL.");
}
StreamString ts;
const char *event_explanation = nullptr;
do {
if (!event_sp) {
event_explanation = "<no event>";
break;
} else if (event_sp->GetType() == eBroadcastBitInterrupt) {
event_explanation = "<user interrupt>";
break;
} else {
const Process::ProcessEventData *event_data =
Process::ProcessEventData::GetEventDataFromEvent(
event_sp.get());
if (!event_data) {
event_explanation = "<no event data>";
break;
}
Process *process = event_data->GetProcessSP().get();
if (!process) {
event_explanation = "<no process>";
break;
}
ThreadList &thread_list = process->GetThreadList();
uint32_t num_threads = thread_list.GetSize();
uint32_t thread_index;
ts.Printf("<%u threads> ", num_threads);
for (thread_index = 0; thread_index < num_threads; ++thread_index) {
Thread *thread = thread_list.GetThreadAtIndex(thread_index).get();
if (!thread) {
ts.Printf("<?> ");
continue;
}
ts.Printf("<0x%4.4" PRIx64 " ", thread->GetID());
RegisterContext *register_context =
thread->GetRegisterContext().get();
if (register_context)
ts.Printf("[ip 0x%" PRIx64 "] ", register_context->GetPC());
else
ts.Printf("[ip unknown] ");
// Show the private stop info here, the public stop info will be
// from the last natural stop.
lldb::StopInfoSP stop_info_sp = thread->GetPrivateStopInfo();
if (stop_info_sp) {
const char *stop_desc = stop_info_sp->GetDescription();
if (stop_desc)
ts.PutCString(stop_desc);
}
ts.Printf(">");
}
event_explanation = ts.GetData();
}
} while (false);
if (event_explanation)
LLDB_LOGF(log,
"Process::RunThreadPlan(): execution interrupted: %s %s",
s.GetData(), event_explanation);
else
LLDB_LOGF(log, "Process::RunThreadPlan(): execution interrupted: %s",
s.GetData());
}
if (should_unwind) {
LLDB_LOGF(log,
"Process::RunThreadPlan: ExecutionInterrupted - "
"discarding thread plans up to %p.",
static_cast<void *>(thread_plan_sp.get()));
thread->DiscardThreadPlansUpToPlan(thread_plan_sp);
} else {
LLDB_LOGF(log,
"Process::RunThreadPlan: ExecutionInterrupted - for "
"plan: %p not discarding.",
static_cast<void *>(thread_plan_sp.get()));
}
} else if (return_value == eExpressionSetupError) {
if (log)
log->PutCString("Process::RunThreadPlan(): execution set up error.");
if (options.DoesUnwindOnError()) {
thread->DiscardThreadPlansUpToPlan(thread_plan_sp);
}
} else {
if (thread->IsThreadPlanDone(thread_plan_sp.get())) {
if (log)
log->PutCString("Process::RunThreadPlan(): thread plan is done");
return_value = eExpressionCompleted;
} else if (thread->WasThreadPlanDiscarded(thread_plan_sp.get())) {
if (log)
log->PutCString(
"Process::RunThreadPlan(): thread plan was discarded");
return_value = eExpressionDiscarded;
} else {
if (log)
log->PutCString(
"Process::RunThreadPlan(): thread plan stopped in mid course");
if (options.DoesUnwindOnError() && thread_plan_sp) {
if (log)
log->PutCString("Process::RunThreadPlan(): discarding thread plan "
"'cause unwind_on_error is set.");
thread->DiscardThreadPlansUpToPlan(thread_plan_sp);
}
}
}
// Thread we ran the function in may have gone away because we ran the
// target Check that it's still there, and if it is put it back in the
// context. Also restore the frame in the context if it is still present.
thread = GetThreadList().FindThreadByIndexID(thread_idx_id, true).get();
if (thread) {
exe_ctx.SetFrameSP(thread->GetFrameWithStackID(ctx_frame_id));
}
// Also restore the current process'es selected frame & thread, since this
// function calling may be done behind the user's back.
if (selected_tid != LLDB_INVALID_THREAD_ID) {
if (GetThreadList().SetSelectedThreadByIndexID(selected_tid) &&
selected_stack_id.IsValid()) {
// We were able to restore the selected thread, now restore the frame:
std::lock_guard<std::recursive_mutex> guard(GetThreadList().GetMutex());
StackFrameSP old_frame_sp =
GetThreadList().GetSelectedThread()->GetFrameWithStackID(
selected_stack_id);
if (old_frame_sp)
GetThreadList().GetSelectedThread()->SetSelectedFrame(
old_frame_sp.get());
}
}
}
// If the process exited during the run of the thread plan, notify everyone.
if (event_to_broadcast_sp) {
if (log)
log->PutCString("Process::RunThreadPlan(): rebroadcasting event.");
BroadcastEvent(event_to_broadcast_sp);
}
return return_value;
}
void Process::GetStatus(Stream &strm) {
const StateType state = GetState();
if (StateIsStoppedState(state, false)) {
if (state == eStateExited) {
int exit_status = GetExitStatus();
const char *exit_description = GetExitDescription();
strm.Printf("Process %" PRIu64 " exited with status = %i (0x%8.8x) %s\n",
GetID(), exit_status, exit_status,
exit_description ? exit_description : "");
} else {
if (state == eStateConnected)
strm.Printf("Connected to remote target.\n");
else
strm.Printf("Process %" PRIu64 " %s\n", GetID(), StateAsCString(state));
}
} else {
strm.Printf("Process %" PRIu64 " is running.\n", GetID());
}
}
size_t Process::GetThreadStatus(Stream &strm,
bool only_threads_with_stop_reason,
uint32_t start_frame, uint32_t num_frames,
uint32_t num_frames_with_source,
bool stop_format) {
size_t num_thread_infos_dumped = 0;
// You can't hold the thread list lock while calling Thread::GetStatus. That
// very well might run code (e.g. if we need it to get return values or
// arguments.) For that to work the process has to be able to acquire it.
// So instead copy the thread ID's, and look them up one by one:
uint32_t num_threads;
std::vector<lldb::tid_t> thread_id_array;
// Scope for thread list locker;
{
std::lock_guard<std::recursive_mutex> guard(GetThreadList().GetMutex());
ThreadList &curr_thread_list = GetThreadList();
num_threads = curr_thread_list.GetSize();
uint32_t idx;
thread_id_array.resize(num_threads);
for (idx = 0; idx < num_threads; ++idx)
thread_id_array[idx] = curr_thread_list.GetThreadAtIndex(idx)->GetID();
}
for (uint32_t i = 0; i < num_threads; i++) {
ThreadSP thread_sp(GetThreadList().FindThreadByID(thread_id_array[i]));
if (thread_sp) {
if (only_threads_with_stop_reason) {
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
if (!stop_info_sp || !stop_info_sp->ShouldShow())
continue;
}
thread_sp->GetStatus(strm, start_frame, num_frames,
num_frames_with_source, stop_format,
/*show_hidden*/ num_frames <= 1);
++num_thread_infos_dumped;
} else {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOGF(log, "Process::GetThreadStatus - thread 0x" PRIu64
" vanished while running Thread::GetStatus.");
}
}
return num_thread_infos_dumped;
}
void Process::AddInvalidMemoryRegion(const LoadRange &region) {
m_memory_cache.AddInvalidRange(region.GetRangeBase(), region.GetByteSize());
}
bool Process::RemoveInvalidMemoryRange(const LoadRange &region) {
return m_memory_cache.RemoveInvalidRange(region.GetRangeBase(),
region.GetByteSize());
}
void Process::AddPreResumeAction(PreResumeActionCallback callback,
void *baton) {
m_pre_resume_actions.push_back(PreResumeCallbackAndBaton(callback, baton));
}
bool Process::RunPreResumeActions() {
bool result = true;
while (!m_pre_resume_actions.empty()) {
struct PreResumeCallbackAndBaton action = m_pre_resume_actions.back();
m_pre_resume_actions.pop_back();
bool this_result = action.callback(action.baton);
if (result)
result = this_result;
}
return result;
}
void Process::ClearPreResumeActions() { m_pre_resume_actions.clear(); }
void Process::ClearPreResumeAction(PreResumeActionCallback callback, void *baton)
{
PreResumeCallbackAndBaton element(callback, baton);
auto found_iter = llvm::find(m_pre_resume_actions, element);
if (found_iter != m_pre_resume_actions.end())
{
m_pre_resume_actions.erase(found_iter);
}
}
ProcessRunLock &Process::GetRunLock() {
if (Process::CurrentThreadIsPrivateStateThread())
return m_private_run_lock;
return m_public_run_lock;
}
bool Process::CurrentThreadIsPrivateStateThread()
{
return m_private_state_thread.EqualsThread(Host::GetCurrentThread());
}
bool Process::CurrentThreadPosesAsPrivateStateThread() {
// If we haven't started up the private state thread yet, then whatever thread
// is fetching this event should be temporarily the private state thread.
if (!m_private_state_thread.HasThread())
return true;
return m_private_state_thread.EqualsThread(Host::GetCurrentThread());
}
void Process::Flush() {
m_thread_list.Flush();
m_extended_thread_list.Flush();
m_extended_thread_stop_id = 0;
m_queue_list.Clear();
m_queue_list_stop_id = 0;
}
lldb::addr_t Process::GetCodeAddressMask() {
if (uint32_t num_bits_setting = GetVirtualAddressableBits())
return AddressableBits::AddressableBitToMask(num_bits_setting);
return m_code_address_mask;
}
lldb::addr_t Process::GetDataAddressMask() {
if (uint32_t num_bits_setting = GetVirtualAddressableBits())
return AddressableBits::AddressableBitToMask(num_bits_setting);
return m_data_address_mask;
}
lldb::addr_t Process::GetHighmemCodeAddressMask() {
if (uint32_t num_bits_setting = GetHighmemVirtualAddressableBits())
return AddressableBits::AddressableBitToMask(num_bits_setting);
if (m_highmem_code_address_mask != LLDB_INVALID_ADDRESS_MASK)
return m_highmem_code_address_mask;
return GetCodeAddressMask();
}
lldb::addr_t Process::GetHighmemDataAddressMask() {
if (uint32_t num_bits_setting = GetHighmemVirtualAddressableBits())
return AddressableBits::AddressableBitToMask(num_bits_setting);
if (m_highmem_data_address_mask != LLDB_INVALID_ADDRESS_MASK)
return m_highmem_data_address_mask;
return GetDataAddressMask();
}
void Process::SetCodeAddressMask(lldb::addr_t code_address_mask) {
LLDB_LOG(GetLog(LLDBLog::Process),
"Setting Process code address mask to {0:x}", code_address_mask);
m_code_address_mask = code_address_mask;
}
void Process::SetDataAddressMask(lldb::addr_t data_address_mask) {
LLDB_LOG(GetLog(LLDBLog::Process),
"Setting Process data address mask to {0:x}", data_address_mask);
m_data_address_mask = data_address_mask;
}
void Process::SetHighmemCodeAddressMask(lldb::addr_t code_address_mask) {
LLDB_LOG(GetLog(LLDBLog::Process),
"Setting Process highmem code address mask to {0:x}",
code_address_mask);
m_highmem_code_address_mask = code_address_mask;
}
void Process::SetHighmemDataAddressMask(lldb::addr_t data_address_mask) {
LLDB_LOG(GetLog(LLDBLog::Process),
"Setting Process highmem data address mask to {0:x}",
data_address_mask);
m_highmem_data_address_mask = data_address_mask;
}
addr_t Process::FixCodeAddress(addr_t addr) {
if (ABISP abi_sp = GetABI())
addr = abi_sp->FixCodeAddress(addr);
return addr;
}
addr_t Process::FixDataAddress(addr_t addr) {
if (ABISP abi_sp = GetABI())
addr = abi_sp->FixDataAddress(addr);
return addr;
}
addr_t Process::FixAnyAddress(addr_t addr) {
if (ABISP abi_sp = GetABI())
addr = abi_sp->FixAnyAddress(addr);
return addr;
}
void Process::DidExec() {
Log *log = GetLog(LLDBLog::Process);
LLDB_LOGF(log, "Process::%s()", __FUNCTION__);
Target &target = GetTarget();
target.CleanupProcess();
target.ClearModules(false);
m_dynamic_checkers_up.reset();
m_abi_sp.reset();
m_system_runtime_up.reset();
m_os_up.reset();
m_dyld_up.reset();
m_jit_loaders_up.reset();
m_image_tokens.clear();
// After an exec, the inferior is a new process and these memory regions are
// no longer allocated.
m_allocated_memory_cache.Clear(/*deallocte_memory=*/false);
{
std::lock_guard<std::recursive_mutex> guard(m_language_runtimes_mutex);
m_language_runtimes.clear();
}
m_instrumentation_runtimes.clear();
m_thread_list.DiscardThreadPlans();
m_memory_cache.Clear(true);
DoDidExec();
CompleteAttach();
// Flush the process (threads and all stack frames) after running
// CompleteAttach() in case the dynamic loader loaded things in new
// locations.
Flush();
// After we figure out what was loaded/unloaded in CompleteAttach, we need to
// let the target know so it can do any cleanup it needs to.
target.DidExec();
}
addr_t Process::ResolveIndirectFunction(const Address *address, Status &error) {
if (address == nullptr) {
error = Status::FromErrorString("Invalid address argument");
return LLDB_INVALID_ADDRESS;
}
addr_t function_addr = LLDB_INVALID_ADDRESS;
addr_t addr = address->GetLoadAddress(&GetTarget());
std::map<addr_t, addr_t>::const_iterator iter =
m_resolved_indirect_addresses.find(addr);
if (iter != m_resolved_indirect_addresses.end()) {
function_addr = (*iter).second;
} else {
if (!CallVoidArgVoidPtrReturn(address, function_addr)) {
Symbol *symbol = address->CalculateSymbolContextSymbol();
error = Status::FromErrorStringWithFormat(
"Unable to call resolver for indirect function %s",
symbol ? symbol->GetName().AsCString() : "<UNKNOWN>");
function_addr = LLDB_INVALID_ADDRESS;
} else {
if (ABISP abi_sp = GetABI())
function_addr = abi_sp->FixCodeAddress(function_addr);
m_resolved_indirect_addresses.insert(
std::pair<addr_t, addr_t>(addr, function_addr));
}
}
return function_addr;
}
void Process::ModulesDidLoad(ModuleList &module_list) {
// Inform the system runtime of the modified modules.
SystemRuntime *sys_runtime = GetSystemRuntime();
if (sys_runtime)
sys_runtime->ModulesDidLoad(module_list);
GetJITLoaders().ModulesDidLoad(module_list);
// Give the instrumentation runtimes a chance to be created before informing
// them of the modified modules.
InstrumentationRuntime::ModulesDidLoad(module_list, this,
m_instrumentation_runtimes);
for (auto &runtime : m_instrumentation_runtimes)
runtime.second->ModulesDidLoad(module_list);
// Give the language runtimes a chance to be created before informing them of
// the modified modules.
for (const lldb::LanguageType lang_type : Language::GetSupportedLanguages()) {
if (LanguageRuntime *runtime = GetLanguageRuntime(lang_type))
runtime->ModulesDidLoad(module_list);
}
// If we don't have an operating system plug-in, try to load one since
// loading shared libraries might cause a new one to try and load
if (!m_os_up)
LoadOperatingSystemPlugin(false);
// Inform the structured-data plugins of the modified modules.
for (auto &pair : m_structured_data_plugin_map) {
if (pair.second)
pair.second->ModulesDidLoad(*this, module_list);
}
}
void Process::PrintWarningOptimization(const SymbolContext &sc) {
if (!GetWarningsOptimization())
return;
if (!sc.module_sp || !sc.function || !sc.function->GetIsOptimized())
return;
sc.module_sp->ReportWarningOptimization(GetTarget().GetDebugger().GetID());
}
void Process::PrintWarningUnsupportedLanguage(const SymbolContext &sc) {
if (!GetWarningsUnsupportedLanguage())
return;
if (!sc.module_sp)
return;
LanguageType language = sc.GetLanguage();
if (language == eLanguageTypeUnknown ||
language == lldb::eLanguageTypeAssembly ||
language == lldb::eLanguageTypeMipsAssembler)
return;
LanguageSet plugins =
PluginManager::GetAllTypeSystemSupportedLanguagesForTypes();
if (plugins[language])
return;
sc.module_sp->ReportWarningUnsupportedLanguage(
language, GetTarget().GetDebugger().GetID());
}
bool Process::GetProcessInfo(ProcessInstanceInfo &info) {
info.Clear();
PlatformSP platform_sp = GetTarget().GetPlatform();
if (!platform_sp)
return false;
return platform_sp->GetProcessInfo(GetID(), info);
}
lldb_private::UUID Process::FindModuleUUID(const llvm::StringRef path) {
return lldb_private::UUID();
}
ThreadCollectionSP Process::GetHistoryThreads(lldb::addr_t addr) {
ThreadCollectionSP threads;
const MemoryHistorySP &memory_history =
MemoryHistory::FindPlugin(shared_from_this());
if (!memory_history) {
return threads;
}
threads = std::make_shared<ThreadCollection>(
memory_history->GetHistoryThreads(addr));
return threads;
}
InstrumentationRuntimeSP
Process::GetInstrumentationRuntime(lldb::InstrumentationRuntimeType type) {
InstrumentationRuntimeCollection::iterator pos;
pos = m_instrumentation_runtimes.find(type);
if (pos == m_instrumentation_runtimes.end()) {
return InstrumentationRuntimeSP();
} else
return (*pos).second;
}
bool Process::GetModuleSpec(const FileSpec &module_file_spec,
const ArchSpec &arch, ModuleSpec &module_spec) {
module_spec.Clear();
return false;
}
size_t Process::AddImageToken(lldb::addr_t image_ptr) {
m_image_tokens.push_back(image_ptr);
return m_image_tokens.size() - 1;
}
lldb::addr_t Process::GetImagePtrFromToken(size_t token) const {
if (token < m_image_tokens.size())
return m_image_tokens[token];
return LLDB_INVALID_IMAGE_TOKEN;
}
void Process::ResetImageToken(size_t token) {
if (token < m_image_tokens.size())
m_image_tokens[token] = LLDB_INVALID_IMAGE_TOKEN;
}
Address
Process::AdvanceAddressToNextBranchInstruction(Address default_stop_addr,
AddressRange range_bounds) {
Target &target = GetTarget();
DisassemblerSP disassembler_sp;
InstructionList *insn_list = nullptr;
Address retval = default_stop_addr;
if (!target.GetUseFastStepping())
return retval;
if (!default_stop_addr.IsValid())
return retval;
const char *plugin_name = nullptr;
const char *flavor = nullptr;
const char *cpu = nullptr;
const char *features = nullptr;
disassembler_sp = Disassembler::DisassembleRange(
target.GetArchitecture(), plugin_name, flavor, cpu, features, GetTarget(),
range_bounds);
if (disassembler_sp)
insn_list = &disassembler_sp->GetInstructionList();
if (insn_list == nullptr) {
return retval;
}
size_t insn_offset =
insn_list->GetIndexOfInstructionAtAddress(default_stop_addr);
if (insn_offset == UINT32_MAX) {
return retval;
}
uint32_t branch_index = insn_list->GetIndexOfNextBranchInstruction(
insn_offset, false /* ignore_calls*/, nullptr);
if (branch_index == UINT32_MAX) {
return retval;
}
if (branch_index > insn_offset) {
Address next_branch_insn_address =
insn_list->GetInstructionAtIndex(branch_index)->GetAddress();
if (next_branch_insn_address.IsValid() &&
range_bounds.ContainsFileAddress(next_branch_insn_address)) {
retval = next_branch_insn_address;
}
}
return retval;
}
Status Process::GetMemoryRegionInfo(lldb::addr_t load_addr,
MemoryRegionInfo &range_info) {
if (const lldb::ABISP &abi = GetABI())
load_addr = abi->FixAnyAddress(load_addr);
Status error = DoGetMemoryRegionInfo(load_addr, range_info);
// Reject a region that does not contain the requested address.
if (error.Success() && !range_info.GetRange().Contains(load_addr))
error = Status::FromErrorString("Invalid memory region");
return error;
}
Status Process::GetMemoryRegions(lldb_private::MemoryRegionInfos &region_list) {
Status error;
lldb::addr_t range_end = 0;
const lldb::ABISP &abi = GetABI();
region_list.clear();
do {
lldb_private::MemoryRegionInfo region_info;
error = GetMemoryRegionInfo(range_end, region_info);
// GetMemoryRegionInfo should only return an error if it is unimplemented.
if (error.Fail()) {
region_list.clear();
break;
}
// We only check the end address, not start and end, because we assume that
// the start will not have non-address bits until the first unmappable
// region. We will have exited the loop by that point because the previous
// region, the last mappable region, will have non-address bits in its end
// address.
range_end = region_info.GetRange().GetRangeEnd();
if (region_info.GetMapped() == MemoryRegionInfo::eYes) {
region_list.push_back(std::move(region_info));
}
} while (
// For a process with no non-address bits, all address bits
// set means the end of memory.
range_end != LLDB_INVALID_ADDRESS &&
// If we have non-address bits and some are set then the end
// is at or beyond the end of mappable memory.
!(abi && (abi->FixAnyAddress(range_end) != range_end)));
return error;
}
Status
Process::ConfigureStructuredData(llvm::StringRef type_name,
const StructuredData::ObjectSP &config_sp) {
// If you get this, the Process-derived class needs to implement a method to
// enable an already-reported asynchronous structured data feature. See
// ProcessGDBRemote for an example implementation over gdb-remote.
return Status::FromErrorString("unimplemented");
}
void Process::MapSupportedStructuredDataPlugins(
const StructuredData::Array &supported_type_names) {
Log *log = GetLog(LLDBLog::Process);
// Bail out early if there are no type names to map.
if (supported_type_names.GetSize() == 0) {
LLDB_LOG(log, "no structured data types supported");
return;
}
// These StringRefs are backed by the input parameter.
std::set<llvm::StringRef> type_names;
LLDB_LOG(log,
"the process supports the following async structured data types:");
supported_type_names.ForEach(
[&type_names, &log](StructuredData::Object *object) {
// There shouldn't be null objects in the array.
if (!object)
return false;
// All type names should be strings.
const llvm::StringRef type_name = object->GetStringValue();
if (type_name.empty())
return false;
type_names.insert(type_name);
LLDB_LOG(log, "- {0}", type_name);
return true;
});
// For each StructuredDataPlugin, if the plugin handles any of the types in
// the supported_type_names, map that type name to that plugin. Stop when
// we've consumed all the type names.
// FIXME: should we return an error if there are type names nobody
// supports?
for (uint32_t plugin_index = 0; !type_names.empty(); plugin_index++) {
auto create_instance =
PluginManager::GetStructuredDataPluginCreateCallbackAtIndex(
plugin_index);
if (!create_instance)
break;
// Create the plugin.
StructuredDataPluginSP plugin_sp = (*create_instance)(*this);
if (!plugin_sp) {
// This plugin doesn't think it can work with the process. Move on to the
// next.
continue;
}
// For any of the remaining type names, map any that this plugin supports.
std::vector<llvm::StringRef> names_to_remove;
for (llvm::StringRef type_name : type_names) {
if (plugin_sp->SupportsStructuredDataType(type_name)) {
m_structured_data_plugin_map.insert(
std::make_pair(type_name, plugin_sp));
names_to_remove.push_back(type_name);
LLDB_LOG(log, "using plugin {0} for type name {1}",
plugin_sp->GetPluginName(), type_name);
}
}
// Remove the type names that were consumed by this plugin.
for (llvm::StringRef type_name : names_to_remove)
type_names.erase(type_name);
}
}
bool Process::RouteAsyncStructuredData(
const StructuredData::ObjectSP object_sp) {
// Nothing to do if there's no data.
if (!object_sp)
return false;
// The contract is this must be a dictionary, so we can look up the routing
// key via the top-level 'type' string value within the dictionary.
StructuredData::Dictionary *dictionary = object_sp->GetAsDictionary();
if (!dictionary)
return false;
// Grab the async structured type name (i.e. the feature/plugin name).
llvm::StringRef type_name;
if (!dictionary->GetValueForKeyAsString("type", type_name))
return false;
// Check if there's a plugin registered for this type name.
auto find_it = m_structured_data_plugin_map.find(type_name);
if (find_it == m_structured_data_plugin_map.end()) {
// We don't have a mapping for this structured data type.
return false;
}
// Route the structured data to the plugin.
find_it->second->HandleArrivalOfStructuredData(*this, type_name, object_sp);
return true;
}
Status Process::UpdateAutomaticSignalFiltering() {
// Default implementation does nothign.
// No automatic signal filtering to speak of.
return Status();
}
UtilityFunction *Process::GetLoadImageUtilityFunction(
Platform *platform,
llvm::function_ref<std::unique_ptr<UtilityFunction>()> factory) {
if (platform != GetTarget().GetPlatform().get())
return nullptr;
llvm::call_once(m_dlopen_utility_func_flag_once,
[&] { m_dlopen_utility_func_up = factory(); });
return m_dlopen_utility_func_up.get();
}
llvm::Expected<TraceSupportedResponse> Process::TraceSupported() {
if (!IsLiveDebugSession())
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Can't trace a non-live process.");
return llvm::make_error<UnimplementedError>();
}
bool Process::CallVoidArgVoidPtrReturn(const Address *address,
addr_t &returned_func,
bool trap_exceptions) {
Thread *thread = GetThreadList().GetExpressionExecutionThread().get();
if (thread == nullptr || address == nullptr)
return false;
EvaluateExpressionOptions options;
options.SetStopOthers(true);
options.SetUnwindOnError(true);
options.SetIgnoreBreakpoints(true);
options.SetTryAllThreads(true);
options.SetDebug(false);
options.SetTimeout(GetUtilityExpressionTimeout());
options.SetTrapExceptions(trap_exceptions);
auto type_system_or_err =
GetTarget().GetScratchTypeSystemForLanguage(eLanguageTypeC);
if (!type_system_or_err) {
llvm::consumeError(type_system_or_err.takeError());
return false;
}
auto ts = *type_system_or_err;
if (!ts)
return false;
CompilerType void_ptr_type =
ts->GetBasicTypeFromAST(eBasicTypeVoid).GetPointerType();
lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallFunction(
*thread, *address, void_ptr_type, llvm::ArrayRef<addr_t>(), options));
if (call_plan_sp) {
DiagnosticManager diagnostics;
StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
if (frame) {
ExecutionContext exe_ctx;
frame->CalculateExecutionContext(exe_ctx);
ExpressionResults result =
RunThreadPlan(exe_ctx, call_plan_sp, options, diagnostics);
if (result == eExpressionCompleted) {
returned_func =
call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned(
LLDB_INVALID_ADDRESS);
if (GetAddressByteSize() == 4) {
if (returned_func == UINT32_MAX)
return false;
} else if (GetAddressByteSize() == 8) {
if (returned_func == UINT64_MAX)
return false;
}
return true;
}
}
}
return false;
}
llvm::Expected<const MemoryTagManager *> Process::GetMemoryTagManager() {
Architecture *arch = GetTarget().GetArchitecturePlugin();
const MemoryTagManager *tag_manager =
arch ? arch->GetMemoryTagManager() : nullptr;
if (!arch || !tag_manager) {
return llvm::createStringError(
llvm::inconvertibleErrorCode(),
"This architecture does not support memory tagging");
}
if (!SupportsMemoryTagging()) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Process does not support memory tagging");
}
return tag_manager;
}
llvm::Expected<std::vector<lldb::addr_t>>
Process::ReadMemoryTags(lldb::addr_t addr, size_t len) {
llvm::Expected<const MemoryTagManager *> tag_manager_or_err =
GetMemoryTagManager();
if (!tag_manager_or_err)
return tag_manager_or_err.takeError();
const MemoryTagManager *tag_manager = *tag_manager_or_err;
llvm::Expected<std::vector<uint8_t>> tag_data =
DoReadMemoryTags(addr, len, tag_manager->GetAllocationTagType());
if (!tag_data)
return tag_data.takeError();
return tag_manager->UnpackTagsData(*tag_data,
len / tag_manager->GetGranuleSize());
}
Status Process::WriteMemoryTags(lldb::addr_t addr, size_t len,
const std::vector<lldb::addr_t> &tags) {
llvm::Expected<const MemoryTagManager *> tag_manager_or_err =
GetMemoryTagManager();
if (!tag_manager_or_err)
return Status::FromError(tag_manager_or_err.takeError());
const MemoryTagManager *tag_manager = *tag_manager_or_err;
llvm::Expected<std::vector<uint8_t>> packed_tags =
tag_manager->PackTags(tags);
if (!packed_tags) {
return Status::FromError(packed_tags.takeError());
}
return DoWriteMemoryTags(addr, len, tag_manager->GetAllocationTagType(),
*packed_tags);
}
// Create a CoreFileMemoryRange from a MemoryRegionInfo
static CoreFileMemoryRange
CreateCoreFileMemoryRange(const MemoryRegionInfo &region) {
const addr_t addr = region.GetRange().GetRangeBase();
llvm::AddressRange range(addr, addr + region.GetRange().GetByteSize());
return {range, region.GetLLDBPermissions()};
}
// Add dirty pages to the core file ranges and return true if dirty pages
// were added. Return false if the dirty page information is not valid or in
// the region.
static bool AddDirtyPages(const MemoryRegionInfo &region,
CoreFileMemoryRanges &ranges) {
const auto &dirty_page_list = region.GetDirtyPageList();
if (!dirty_page_list)
return false;
const uint32_t lldb_permissions = region.GetLLDBPermissions();
const addr_t page_size = region.GetPageSize();
if (page_size == 0)
return false;
llvm::AddressRange range(0, 0);
for (addr_t page_addr : *dirty_page_list) {
if (range.empty()) {
// No range yet, initialize the range with the current dirty page.
range = llvm::AddressRange(page_addr, page_addr + page_size);
} else {
if (range.end() == page_addr) {
// Combine consective ranges.
range = llvm::AddressRange(range.start(), page_addr + page_size);
} else {
// Add previous contiguous range and init the new range with the
// current dirty page.
ranges.Append(range.start(), range.size(), {range, lldb_permissions});
range = llvm::AddressRange(page_addr, page_addr + page_size);
}
}
}
// The last range
if (!range.empty())
ranges.Append(range.start(), range.size(), {range, lldb_permissions});
return true;
}
// Given a region, add the region to \a ranges.
//
// Only add the region if it isn't empty and if it has some permissions.
// If \a try_dirty_pages is true, then try to add only the dirty pages for a
// given region. If the region has dirty page information, only dirty pages
// will be added to \a ranges, else the entire range will be added to \a
// ranges.
static void AddRegion(const MemoryRegionInfo &region, bool try_dirty_pages,
CoreFileMemoryRanges &ranges) {
// Don't add empty ranges.
if (region.GetRange().GetByteSize() == 0)
return;
// Don't add ranges with no read permissions.
if ((region.GetLLDBPermissions() & lldb::ePermissionsReadable) == 0)
return;
if (try_dirty_pages && AddDirtyPages(region, ranges))
return;
ranges.Append(region.GetRange().GetRangeBase(),
region.GetRange().GetByteSize(),
CreateCoreFileMemoryRange(region));
}
static void SaveDynamicLoaderSections(Process &process,
const SaveCoreOptions &options,
CoreFileMemoryRanges &ranges,
std::set<addr_t> &stack_ends) {
DynamicLoader *dyld = process.GetDynamicLoader();
if (!dyld)
return;
std::vector<MemoryRegionInfo> dynamic_loader_mem_regions;
std::function<bool(const lldb_private::Thread &)> save_thread_predicate =
[&](const lldb_private::Thread &t) -> bool {
return options.ShouldThreadBeSaved(t.GetID());
};
dyld->CalculateDynamicSaveCoreRanges(process, dynamic_loader_mem_regions,
save_thread_predicate);
for (const auto &region : dynamic_loader_mem_regions) {
// The Dynamic Loader can give us regions that could include a truncated
// stack
if (stack_ends.count(region.GetRange().GetRangeEnd()) == 0)
AddRegion(region, true, ranges);
}
}
static void SaveOffRegionsWithStackPointers(Process &process,
const SaveCoreOptions &core_options,
const MemoryRegionInfos &regions,
CoreFileMemoryRanges &ranges,
std::set<addr_t> &stack_ends) {
const bool try_dirty_pages = true;
// Before we take any dump, we want to save off the used portions of the
// stacks and mark those memory regions as saved. This prevents us from saving
// the unused portion of the stack below the stack pointer. Saving space on
// the dump.
for (lldb::ThreadSP thread_sp : process.GetThreadList().Threads()) {
if (!thread_sp)
continue;
StackFrameSP frame_sp = thread_sp->GetStackFrameAtIndex(0);
if (!frame_sp)
continue;
RegisterContextSP reg_ctx_sp = frame_sp->GetRegisterContext();
if (!reg_ctx_sp)
continue;
const addr_t sp = reg_ctx_sp->GetSP();
const size_t red_zone = process.GetABI()->GetRedZoneSize();
lldb_private::MemoryRegionInfo sp_region;
if (process.GetMemoryRegionInfo(sp, sp_region).Success()) {
const size_t stack_head = (sp - red_zone);
const size_t stack_size = sp_region.GetRange().GetRangeEnd() - stack_head;
// Even if the SaveCoreOption doesn't want us to save the stack
// we still need to populate the stack_ends set so it doesn't get saved
// off in other calls
sp_region.GetRange().SetRangeBase(stack_head);
sp_region.GetRange().SetByteSize(stack_size);
const addr_t range_end = sp_region.GetRange().GetRangeEnd();
stack_ends.insert(range_end);
// This will return true if the threadlist the user specified is empty,
// or contains the thread id from thread_sp.
if (core_options.ShouldThreadBeSaved(thread_sp->GetID())) {
AddRegion(sp_region, try_dirty_pages, ranges);
}
}
}
}
// Save all memory regions that are not empty or have at least some permissions
// for a full core file style.
static void GetCoreFileSaveRangesFull(Process &process,
const MemoryRegionInfos &regions,
CoreFileMemoryRanges &ranges,
std::set<addr_t> &stack_ends) {
// Don't add only dirty pages, add full regions.
const bool try_dirty_pages = false;
for (const auto &region : regions)
if (stack_ends.count(region.GetRange().GetRangeEnd()) == 0)
AddRegion(region, try_dirty_pages, ranges);
}
// Save only the dirty pages to the core file. Make sure the process has at
// least some dirty pages, as some OS versions don't support reporting what
// pages are dirty within an memory region. If no memory regions have dirty
// page information fall back to saving out all ranges with write permissions.
static void GetCoreFileSaveRangesDirtyOnly(Process &process,
const MemoryRegionInfos &regions,
CoreFileMemoryRanges &ranges,
std::set<addr_t> &stack_ends) {
// Iterate over the regions and find all dirty pages.
bool have_dirty_page_info = false;
for (const auto &region : regions) {
if (stack_ends.count(region.GetRange().GetRangeEnd()) == 0 &&
AddDirtyPages(region, ranges))
have_dirty_page_info = true;
}
if (!have_dirty_page_info) {
// We didn't find support for reporting dirty pages from the process
// plug-in so fall back to any region with write access permissions.
const bool try_dirty_pages = false;
for (const auto &region : regions)
if (stack_ends.count(region.GetRange().GetRangeEnd()) == 0 &&
region.GetWritable() == MemoryRegionInfo::eYes)
AddRegion(region, try_dirty_pages, ranges);
}
}
// Save all thread stacks to the core file. Some OS versions support reporting
// when a memory region is stack related. We check on this information, but we
// also use the stack pointers of each thread and add those in case the OS
// doesn't support reporting stack memory. This function also attempts to only
// emit dirty pages from the stack if the memory regions support reporting
// dirty regions as this will make the core file smaller. If the process
// doesn't support dirty regions, then it will fall back to adding the full
// stack region.
static void GetCoreFileSaveRangesStackOnly(Process &process,
const MemoryRegionInfos &regions,
CoreFileMemoryRanges &ranges,
std::set<addr_t> &stack_ends) {
const bool try_dirty_pages = true;
// Some platforms support annotating the region information that tell us that
// it comes from a thread stack. So look for those regions first.
for (const auto &region : regions) {
// Save all the stack memory ranges not associated with a stack pointer.
if (stack_ends.count(region.GetRange().GetRangeEnd()) == 0 &&
region.IsStackMemory() == MemoryRegionInfo::eYes)
AddRegion(region, try_dirty_pages, ranges);
}
}
// TODO: We should refactor CoreFileMemoryRanges to use the lldb range type, and
// then add an intersect method on it, or MemoryRegionInfo.
static MemoryRegionInfo Intersect(const MemoryRegionInfo &lhs,
const MemoryRegionInfo::RangeType &rhs) {
MemoryRegionInfo region_info;
region_info.SetLLDBPermissions(lhs.GetLLDBPermissions());
region_info.GetRange() = lhs.GetRange().Intersect(rhs);
return region_info;
}
static void GetUserSpecifiedCoreFileSaveRanges(Process &process,
const MemoryRegionInfos &regions,
const SaveCoreOptions &options,
CoreFileMemoryRanges &ranges) {
const auto &option_ranges = options.GetCoreFileMemoryRanges();
if (option_ranges.IsEmpty())
return;
for (const auto &range : regions) {
auto *entry = option_ranges.FindEntryThatIntersects(range.GetRange());
if (entry) {
if (*entry != range.GetRange()) {
AddRegion(Intersect(range, *entry), true, ranges);
} else {
// If they match, add the range directly.
AddRegion(range, true, ranges);
}
}
}
}
Status Process::CalculateCoreFileSaveRanges(const SaveCoreOptions &options,
CoreFileMemoryRanges &ranges) {
lldb_private::MemoryRegionInfos regions;
Status err = GetMemoryRegions(regions);
SaveCoreStyle core_style = options.GetStyle();
if (err.Fail())
return err;
if (regions.empty())
return Status::FromErrorString(
"failed to get any valid memory regions from the process");
if (core_style == eSaveCoreUnspecified)
return Status::FromErrorString(
"callers must set the core_style to something other than "
"eSaveCoreUnspecified");
GetUserSpecifiedCoreFileSaveRanges(*this, regions, options, ranges);
std::set<addr_t> stack_ends;
// For fully custom set ups, we don't want to even look at threads if there
// are no threads specified.
if (core_style != lldb::eSaveCoreCustomOnly ||
options.HasSpecifiedThreads()) {
SaveOffRegionsWithStackPointers(*this, options, regions, ranges,
stack_ends);
// Save off the dynamic loader sections, so if we are on an architecture
// that supports Thread Locals, that we include those as well.
SaveDynamicLoaderSections(*this, options, ranges, stack_ends);
}
switch (core_style) {
case eSaveCoreUnspecified:
case eSaveCoreCustomOnly:
break;
case eSaveCoreFull:
GetCoreFileSaveRangesFull(*this, regions, ranges, stack_ends);
break;
case eSaveCoreDirtyOnly:
GetCoreFileSaveRangesDirtyOnly(*this, regions, ranges, stack_ends);
break;
case eSaveCoreStackOnly:
GetCoreFileSaveRangesStackOnly(*this, regions, ranges, stack_ends);
break;
}
if (err.Fail())
return err;
if (ranges.IsEmpty())
return Status::FromErrorStringWithFormat(
"no valid address ranges found for core style");
return ranges.FinalizeCoreFileSaveRanges();
}
std::vector<ThreadSP>
Process::CalculateCoreFileThreadList(const SaveCoreOptions &core_options) {
std::vector<ThreadSP> thread_list;
for (const lldb::ThreadSP &thread_sp : m_thread_list.Threads()) {
if (core_options.ShouldThreadBeSaved(thread_sp->GetID())) {
thread_list.push_back(thread_sp);
}
}
return thread_list;
}
void Process::SetAddressableBitMasks(AddressableBits bit_masks) {
uint32_t low_memory_addr_bits = bit_masks.GetLowmemAddressableBits();
uint32_t high_memory_addr_bits = bit_masks.GetHighmemAddressableBits();
if (low_memory_addr_bits == 0 && high_memory_addr_bits == 0)
return;
if (low_memory_addr_bits != 0) {
addr_t low_addr_mask =
AddressableBits::AddressableBitToMask(low_memory_addr_bits);
SetCodeAddressMask(low_addr_mask);
SetDataAddressMask(low_addr_mask);
}
if (high_memory_addr_bits != 0) {
addr_t high_addr_mask =
AddressableBits::AddressableBitToMask(high_memory_addr_bits);
SetHighmemCodeAddressMask(high_addr_mask);
SetHighmemDataAddressMask(high_addr_mask);
}
}