2010-06-09 00:52:24 +08:00
|
|
|
//===-- Process.cpp ---------------------------------------------*- C++ -*-===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2015-10-24 02:39:37 +08:00
|
|
|
// C Includes
|
|
|
|
// C++ Includes
|
2016-02-27 03:41:49 +08:00
|
|
|
#include <atomic>
|
2016-02-27 03:38:18 +08:00
|
|
|
#include <mutex>
|
2016-03-01 08:55:51 +08:00
|
|
|
|
2015-10-24 02:39:37 +08:00
|
|
|
// Other libraries and framework includes
|
|
|
|
// Project includes
|
2016-03-19 08:03:59 +08:00
|
|
|
#include "Plugins/Process/Utility/InferiorCallPOSIX.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Breakpoint/BreakpointLocation.h"
|
2016-03-19 08:03:59 +08:00
|
|
|
#include "lldb/Breakpoint/StoppointCallbackContext.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Core/Debugger.h"
|
2016-03-19 08:03:59 +08:00
|
|
|
#include "lldb/Core/Event.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Core/Log.h"
|
<rdar://problem/11757916>
Make breakpoint setting by file and line much more efficient by only looking for inlined breakpoint locations if we are setting a breakpoint in anything but a source implementation file. Implementing this complex for a many reasons. Turns out that parsing compile units lazily had some issues with respect to how we need to do things with DWARF in .o files. So the fixes in the checkin for this makes these changes:
- Add a new setting called "target.inline-breakpoint-strategy" which can be set to "never", "always", or "headers". "never" will never try and set any inlined breakpoints (fastest). "always" always looks for inlined breakpoint locations (slowest, but most accurate). "headers", which is the default setting, will only look for inlined breakpoint locations if the breakpoint is set in what are consudered to be header files, which is realy defined as "not in an implementation source file".
- modify the breakpoint setting by file and line to check the current "target.inline-breakpoint-strategy" setting and act accordingly
- Modify compile units to be able to get their language and other info lazily. This allows us to create compile units from the debug map and not have to fill all of the details in, and then lazily discover this information as we go on debuggging. This is needed to avoid parsing all .o files when setting breakpoints in implementation only files (no inlines). Otherwise we would need to parse the .o file, the object file (mach-o in our case) and the symbol file (DWARF in the object file) just to see what the compile unit was.
- modify the "SymbolFileDWARFDebugMap" to subclass lldb_private::Module so that the virtual "GetObjectFile()" and "GetSymbolVendor()" functions can be intercepted when the .o file contenst are later lazilly needed. Prior to this fix, when we first instantiated the "SymbolFileDWARFDebugMap" class, we would also make modules, object files and symbol files for every .o file in the debug map because we needed to fix up the sections in the .o files with information that is in the executable debug map. Now we lazily do this in the DebugMapModule::GetObjectFile()
Cleaned up header includes a bit as well.
llvm-svn: 162860
2012-08-30 05:13:06 +08:00
|
|
|
#include "lldb/Core/Module.h"
|
2015-03-24 19:15:23 +08:00
|
|
|
#include "lldb/Core/ModuleSpec.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Core/PluginManager.h"
|
|
|
|
#include "lldb/Core/State.h"
|
2014-01-28 07:43:24 +08:00
|
|
|
#include "lldb/Core/StreamFile.h"
|
2016-03-19 08:03:59 +08:00
|
|
|
#include "lldb/Expression/DiagnosticManager.h"
|
2015-03-04 05:51:25 +08:00
|
|
|
#include "lldb/Expression/IRDynamicChecks.h"
|
2016-03-19 08:03:59 +08:00
|
|
|
#include "lldb/Expression/UserExpression.h"
|
2014-10-07 05:22:36 +08:00
|
|
|
#include "lldb/Host/ConnectionFileDescriptor.h"
|
2015-10-15 05:37:36 +08:00
|
|
|
#include "lldb/Host/FileSystem.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Host/Host.h"
|
2014-09-10 04:54:56 +08:00
|
|
|
#include "lldb/Host/HostInfo.h"
|
2014-07-03 05:10:39 +08:00
|
|
|
#include "lldb/Host/Pipe.h"
|
2014-01-28 07:43:24 +08:00
|
|
|
#include "lldb/Host/Terminal.h"
|
2014-09-10 04:54:56 +08:00
|
|
|
#include "lldb/Host/ThreadLauncher.h"
|
2014-10-07 05:22:36 +08:00
|
|
|
#include "lldb/Interpreter/CommandInterpreter.h"
|
2015-03-04 09:58:01 +08:00
|
|
|
#include "lldb/Interpreter/OptionValueProperties.h"
|
2015-08-10 15:55:25 +08:00
|
|
|
#include "lldb/Symbol/Function.h"
|
2014-10-07 05:22:36 +08:00
|
|
|
#include "lldb/Symbol/Symbol.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Target/ABI.h"
|
2016-03-19 08:03:59 +08:00
|
|
|
#include "lldb/Target/CPPLanguageRuntime.h"
|
2010-11-04 09:54:29 +08:00
|
|
|
#include "lldb/Target/DynamicLoader.h"
|
2015-03-04 05:51:25 +08:00
|
|
|
#include "lldb/Target/InstrumentationRuntime.h"
|
2014-03-05 18:12:43 +08:00
|
|
|
#include "lldb/Target/JITLoader.h"
|
2015-03-04 05:51:25 +08:00
|
|
|
#include "lldb/Target/JITLoaderList.h"
|
2016-03-19 08:03:59 +08:00
|
|
|
#include "lldb/Target/LanguageRuntime.h"
|
2014-09-06 09:33:13 +08:00
|
|
|
#include "lldb/Target/MemoryHistory.h"
|
2015-03-04 05:51:25 +08:00
|
|
|
#include "lldb/Target/MemoryRegionInfo.h"
|
2010-09-23 10:01:19 +08:00
|
|
|
#include "lldb/Target/ObjCLanguageRuntime.h"
|
2016-03-19 08:03:59 +08:00
|
|
|
#include "lldb/Target/OperatingSystem.h"
|
2011-03-09 06:40:15 +08:00
|
|
|
#include "lldb/Target/Platform.h"
|
2016-03-19 08:03:59 +08:00
|
|
|
#include "lldb/Target/Process.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Target/RegisterContext.h"
|
2010-08-04 09:40:35 +08:00
|
|
|
#include "lldb/Target/StopInfo.h"
|
2016-08-19 12:21:48 +08:00
|
|
|
#include "lldb/Target/StructuredDataPlugin.h"
|
2013-11-05 11:57:19 +08:00
|
|
|
#include "lldb/Target/SystemRuntime.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Target/Target.h"
|
|
|
|
#include "lldb/Target/TargetList.h"
|
|
|
|
#include "lldb/Target/Thread.h"
|
|
|
|
#include "lldb/Target/ThreadPlan.h"
|
2012-04-10 09:21:57 +08:00
|
|
|
#include "lldb/Target/ThreadPlanBase.h"
|
2015-03-04 05:51:25 +08:00
|
|
|
#include "lldb/Target/UnixSignals.h"
|
2015-03-19 05:31:45 +08:00
|
|
|
#include "lldb/Utility/NameMatches.h"
|
2016-08-11 06:43:48 +08:00
|
|
|
#include "lldb/Utility/SelectHelper.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
using namespace lldb;
|
|
|
|
using namespace lldb_private;
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Comment out line below to disable memory caching, overriding the process
|
|
|
|
// setting
|
2012-08-23 01:17:09 +08:00
|
|
|
// target.process.disable-memory-cache
|
|
|
|
#define ENABLE_MEMORY_CACHING
|
|
|
|
|
|
|
|
#ifdef ENABLE_MEMORY_CACHING
|
|
|
|
#define DISABLE_MEM_CACHE_DEFAULT false
|
|
|
|
#else
|
|
|
|
#define DISABLE_MEM_CACHE_DEFAULT true
|
|
|
|
#endif
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
class ProcessOptionValueProperties : public OptionValueProperties {
|
2012-08-23 01:17:09 +08:00
|
|
|
public:
|
2016-09-07 04:57:50 +08:00
|
|
|
ProcessOptionValueProperties(const ConstString &name)
|
|
|
|
: OptionValueProperties(name) {}
|
|
|
|
|
|
|
|
// This constructor is used when creating ProcessOptionValueProperties when it
|
|
|
|
// is part of a new lldb_private::Process instance. It will copy all current
|
|
|
|
// global property values as needed
|
|
|
|
ProcessOptionValueProperties(ProcessProperties *global_properties)
|
|
|
|
: OptionValueProperties(*global_properties->GetValueProperties()) {}
|
|
|
|
|
|
|
|
const Property *GetPropertyAtIndex(const ExecutionContext *exe_ctx,
|
|
|
|
bool will_modify,
|
|
|
|
uint32_t idx) 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);
|
|
|
|
}
|
2012-08-23 01:17:09 +08:00
|
|
|
};
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
static PropertyDefinition g_properties[] = {
|
|
|
|
{"disable-memory-cache", OptionValue::eTypeBoolean, false,
|
|
|
|
DISABLE_MEM_CACHE_DEFAULT, nullptr, nullptr,
|
|
|
|
"Disable reading and caching of memory in fixed-size units."},
|
|
|
|
{"extra-startup-command", OptionValue::eTypeArray, false,
|
|
|
|
OptionValue::eTypeString, nullptr, nullptr,
|
|
|
|
"A list containing extra commands understood by the particular process "
|
|
|
|
"plugin used. "
|
|
|
|
"For instance, to turn on debugserver logging set this to "
|
|
|
|
"\"QSetLogging:bitmask=LOG_DEFAULT;\""},
|
|
|
|
{"ignore-breakpoints-in-expressions", OptionValue::eTypeBoolean, true, true,
|
|
|
|
nullptr, nullptr,
|
|
|
|
"If true, breakpoints will be ignored during expression evaluation."},
|
|
|
|
{"unwind-on-error-in-expressions", OptionValue::eTypeBoolean, true, true,
|
|
|
|
nullptr, nullptr, "If true, errors in expression evaluation will unwind "
|
|
|
|
"the stack back to the state before the call."},
|
|
|
|
{"python-os-plugin-path", OptionValue::eTypeFileSpec, false, true, nullptr,
|
|
|
|
nullptr, "A path to a python OS plug-in module file that contains a "
|
|
|
|
"OperatingSystemPlugIn class."},
|
|
|
|
{"stop-on-sharedlibrary-events", OptionValue::eTypeBoolean, true, false,
|
|
|
|
nullptr, nullptr,
|
|
|
|
"If true, stop when a shared library is loaded or unloaded."},
|
|
|
|
{"detach-keeps-stopped", OptionValue::eTypeBoolean, true, false, nullptr,
|
|
|
|
nullptr, "If true, detach will attempt to keep the process stopped."},
|
|
|
|
{"memory-cache-line-size", OptionValue::eTypeUInt64, false, 512, nullptr,
|
|
|
|
nullptr, "The memory cache line size"},
|
|
|
|
{"optimization-warnings", OptionValue::eTypeBoolean, false, true, nullptr,
|
|
|
|
nullptr, "If true, warn when stopped in code that is optimized where "
|
|
|
|
"stepping and variable availability may not behave as expected."},
|
|
|
|
{nullptr, OptionValue::eTypeInvalid, false, 0, nullptr, nullptr, nullptr}};
|
2012-08-23 01:17:09 +08:00
|
|
|
|
|
|
|
enum {
|
2016-09-07 04:57:50 +08:00
|
|
|
ePropertyDisableMemCache,
|
|
|
|
ePropertyExtraStartCommand,
|
|
|
|
ePropertyIgnoreBreakpointsInExpressions,
|
|
|
|
ePropertyUnwindOnErrorInExpressions,
|
|
|
|
ePropertyPythonOSPluginPath,
|
|
|
|
ePropertyStopOnSharedLibraryEvents,
|
|
|
|
ePropertyDetachKeepsStopped,
|
|
|
|
ePropertyMemCacheLineSize,
|
|
|
|
ePropertyWarningOptimization
|
2012-08-23 01:17:09 +08:00
|
|
|
};
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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.reset(
|
|
|
|
new ProcessOptionValueProperties(ConstString("process")));
|
|
|
|
m_collection_sp->Initialize(g_properties);
|
|
|
|
m_collection_sp->AppendProperty(
|
|
|
|
ConstString("thread"), ConstString("Settings specific to threads."),
|
|
|
|
true, Thread::GetGlobalProperties()->GetValueProperties());
|
|
|
|
} else {
|
|
|
|
m_collection_sp.reset(
|
|
|
|
new ProcessOptionValueProperties(Process::GetGlobalProperties().get()));
|
|
|
|
m_collection_sp->SetValueChangedCallback(
|
|
|
|
ePropertyPythonOSPluginPath,
|
|
|
|
ProcessProperties::OptionValueChangedCallback, this);
|
|
|
|
}
|
2012-08-23 01:17:09 +08:00
|
|
|
}
|
|
|
|
|
2015-10-24 02:39:37 +08:00
|
|
|
ProcessProperties::~ProcessProperties() = default;
|
2012-08-23 01:17:09 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void ProcessProperties::OptionValueChangedCallback(void *baton,
|
|
|
|
OptionValue *option_value) {
|
|
|
|
ProcessProperties *properties = (ProcessProperties *)baton;
|
|
|
|
if (properties->m_process)
|
|
|
|
properties->m_process->LoadOperatingSystemPlugin(true);
|
2015-01-14 05:13:08 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool ProcessProperties::GetDisableMemoryCache() const {
|
|
|
|
const uint32_t idx = ePropertyDisableMemCache;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsBoolean(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value != 0);
|
2012-08-23 01:17:09 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
uint64_t ProcessProperties::GetMemoryCacheLineSize() const {
|
|
|
|
const uint32_t idx = ePropertyMemCacheLineSize;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsUInt64(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value);
|
2014-09-04 06:30:54 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Args ProcessProperties::GetExtraStartupCommands() const {
|
|
|
|
Args args;
|
|
|
|
const uint32_t idx = ePropertyExtraStartCommand;
|
|
|
|
m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, args);
|
|
|
|
return args;
|
2012-08-23 01:17:09 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void ProcessProperties::SetExtraStartupCommands(const Args &args) {
|
|
|
|
const uint32_t idx = ePropertyExtraStartCommand;
|
|
|
|
m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, args);
|
2012-08-23 01:17:09 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
FileSpec ProcessProperties::GetPythonOSPluginPath() const {
|
|
|
|
const uint32_t idx = ePropertyPythonOSPluginPath;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsFileSpec(nullptr, idx);
|
2012-10-19 06:40:37 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void ProcessProperties::SetPythonOSPluginPath(const FileSpec &file) {
|
|
|
|
const uint32_t idx = ePropertyPythonOSPluginPath;
|
|
|
|
m_collection_sp->SetPropertyAtIndexAsFileSpec(nullptr, idx, file);
|
2012-10-19 06:40:37 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool ProcessProperties::GetIgnoreBreakpointsInExpressions() const {
|
|
|
|
const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsBoolean(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value != 0);
|
2013-01-15 10:47:48 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
|
|
|
|
void ProcessProperties::SetIgnoreBreakpointsInExpressions(bool ignore) {
|
|
|
|
const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions;
|
|
|
|
m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, ignore);
|
2013-01-15 10:47:48 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool ProcessProperties::GetUnwindOnErrorInExpressions() const {
|
|
|
|
const uint32_t idx = ePropertyUnwindOnErrorInExpressions;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsBoolean(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value != 0);
|
2013-01-15 10:47:48 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
|
|
|
|
void ProcessProperties::SetUnwindOnErrorInExpressions(bool ignore) {
|
|
|
|
const uint32_t idx = ePropertyUnwindOnErrorInExpressions;
|
|
|
|
m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, ignore);
|
2013-01-15 10:47:48 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool ProcessProperties::GetStopOnSharedLibraryEvents() const {
|
|
|
|
const uint32_t idx = ePropertyStopOnSharedLibraryEvents;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsBoolean(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value != 0);
|
2013-01-26 10:19:28 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
|
|
|
|
void ProcessProperties::SetStopOnSharedLibraryEvents(bool stop) {
|
|
|
|
const uint32_t idx = ePropertyStopOnSharedLibraryEvents;
|
|
|
|
m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, stop);
|
2013-01-26 10:19:28 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool ProcessProperties::GetDetachKeepsStopped() const {
|
|
|
|
const uint32_t idx = ePropertyDetachKeepsStopped;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsBoolean(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value != 0);
|
2013-05-02 08:27:30 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
|
|
|
|
void ProcessProperties::SetDetachKeepsStopped(bool stop) {
|
|
|
|
const uint32_t idx = ePropertyDetachKeepsStopped;
|
|
|
|
m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, stop);
|
2013-05-02 08:27:30 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool ProcessProperties::GetWarningsOptimization() const {
|
|
|
|
const uint32_t idx = ePropertyWarningOptimization;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsBoolean(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value != 0);
|
2015-08-06 11:27:10 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void ProcessInstanceInfo::Dump(Stream &s, Platform *platform) const {
|
|
|
|
const char *cstr;
|
|
|
|
if (m_pid != LLDB_INVALID_PROCESS_ID)
|
|
|
|
s.Printf(" pid = %" PRIu64 "\n", m_pid);
|
|
|
|
|
|
|
|
if (m_parent_pid != LLDB_INVALID_PROCESS_ID)
|
|
|
|
s.Printf(" parent = %" PRIu64 "\n", m_parent_pid);
|
|
|
|
|
|
|
|
if (m_executable) {
|
|
|
|
s.Printf(" name = %s\n", m_executable.GetFilename().GetCString());
|
|
|
|
s.PutCString(" file = ");
|
|
|
|
m_executable.Dump(&s);
|
|
|
|
s.EOL();
|
|
|
|
}
|
|
|
|
const uint32_t argc = m_arguments.GetArgumentCount();
|
|
|
|
if (argc > 0) {
|
|
|
|
for (uint32_t i = 0; i < argc; i++) {
|
|
|
|
const char *arg = m_arguments.GetArgumentAtIndex(i);
|
|
|
|
if (i < 10)
|
|
|
|
s.Printf(" arg[%u] = %s\n", i, arg);
|
|
|
|
else
|
|
|
|
s.Printf("arg[%u] = %s\n", i, arg);
|
2011-04-01 08:29:43 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2011-04-12 13:54:46 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const uint32_t envc = m_environment.GetArgumentCount();
|
|
|
|
if (envc > 0) {
|
|
|
|
for (uint32_t i = 0; i < envc; i++) {
|
|
|
|
const char *env = m_environment.GetArgumentAtIndex(i);
|
|
|
|
if (i < 10)
|
|
|
|
s.Printf(" env[%u] = %s\n", i, env);
|
|
|
|
else
|
|
|
|
s.Printf("env[%u] = %s\n", i, env);
|
2011-04-12 13:54:46 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m_arch.IsValid()) {
|
|
|
|
s.Printf(" arch = ");
|
|
|
|
m_arch.DumpTriple(s);
|
|
|
|
s.EOL();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_uid != UINT32_MAX) {
|
|
|
|
cstr = platform->GetUserName(m_uid);
|
|
|
|
s.Printf(" uid = %-5u (%s)\n", m_uid, cstr ? cstr : "");
|
|
|
|
}
|
|
|
|
if (m_gid != UINT32_MAX) {
|
|
|
|
cstr = platform->GetGroupName(m_gid);
|
|
|
|
s.Printf(" gid = %-5u (%s)\n", m_gid, cstr ? cstr : "");
|
|
|
|
}
|
|
|
|
if (m_euid != UINT32_MAX) {
|
|
|
|
cstr = platform->GetUserName(m_euid);
|
|
|
|
s.Printf(" euid = %-5u (%s)\n", m_euid, cstr ? cstr : "");
|
|
|
|
}
|
|
|
|
if (m_egid != UINT32_MAX) {
|
|
|
|
cstr = platform->GetGroupName(m_egid);
|
|
|
|
s.Printf(" egid = %-5u (%s)\n", m_egid, cstr ? cstr : "");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessInstanceInfo::DumpTableHeader(Stream &s, Platform *platform,
|
|
|
|
bool show_args, bool verbose) {
|
|
|
|
const char *label;
|
|
|
|
if (show_args || verbose)
|
|
|
|
label = "ARGUMENTS";
|
|
|
|
else
|
|
|
|
label = "NAME";
|
|
|
|
|
|
|
|
if (verbose) {
|
|
|
|
s.Printf("PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE "
|
|
|
|
" %s\n",
|
|
|
|
label);
|
|
|
|
s.PutCString("====== ====== ========== ========== ========== ========== "
|
|
|
|
"======================== ============================\n");
|
|
|
|
} else {
|
|
|
|
s.Printf("PID PARENT USER TRIPLE %s\n", label);
|
|
|
|
s.PutCString("====== ====== ========== ======================== "
|
|
|
|
"============================\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessInstanceInfo::DumpAsTableRow(Stream &s, Platform *platform,
|
|
|
|
bool show_args, bool verbose) const {
|
|
|
|
if (m_pid != LLDB_INVALID_PROCESS_ID) {
|
|
|
|
const char *cstr;
|
|
|
|
s.Printf("%-6" PRIu64 " %-6" PRIu64 " ", m_pid, m_parent_pid);
|
2011-04-12 13:54:46 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
StreamString arch_strm;
|
2015-10-14 07:41:19 +08:00
|
|
|
if (m_arch.IsValid())
|
2016-09-07 04:57:50 +08:00
|
|
|
m_arch.DumpTriple(arch_strm);
|
|
|
|
|
|
|
|
if (verbose) {
|
|
|
|
cstr = platform->GetUserName(m_uid);
|
|
|
|
if (cstr &&
|
|
|
|
cstr[0]) // Watch for empty string that indicates lookup failed
|
|
|
|
s.Printf("%-10s ", cstr);
|
|
|
|
else
|
|
|
|
s.Printf("%-10u ", m_uid);
|
|
|
|
|
|
|
|
cstr = platform->GetGroupName(m_gid);
|
|
|
|
if (cstr &&
|
|
|
|
cstr[0]) // Watch for empty string that indicates lookup failed
|
|
|
|
s.Printf("%-10s ", cstr);
|
|
|
|
else
|
|
|
|
s.Printf("%-10u ", m_gid);
|
|
|
|
|
|
|
|
cstr = platform->GetUserName(m_euid);
|
|
|
|
if (cstr &&
|
|
|
|
cstr[0]) // Watch for empty string that indicates lookup failed
|
|
|
|
s.Printf("%-10s ", cstr);
|
|
|
|
else
|
|
|
|
s.Printf("%-10u ", m_euid);
|
|
|
|
|
|
|
|
cstr = platform->GetGroupName(m_egid);
|
|
|
|
if (cstr &&
|
|
|
|
cstr[0]) // Watch for empty string that indicates lookup failed
|
|
|
|
s.Printf("%-10s ", cstr);
|
|
|
|
else
|
|
|
|
s.Printf("%-10u ", m_egid);
|
|
|
|
|
|
|
|
s.Printf("%-24s ", arch_strm.GetString().c_str());
|
|
|
|
} else {
|
|
|
|
s.Printf("%-10s %-24s ", platform->GetUserName(m_euid),
|
|
|
|
arch_strm.GetString().c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbose || show_args) {
|
|
|
|
const uint32_t argc = m_arguments.GetArgumentCount();
|
|
|
|
if (argc > 0) {
|
|
|
|
for (uint32_t i = 0; i < argc; i++) {
|
|
|
|
if (i > 0)
|
|
|
|
s.PutChar(' ');
|
|
|
|
s.PutCString(m_arguments.GetArgumentAtIndex(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
s.PutCString(GetName());
|
|
|
|
}
|
|
|
|
|
|
|
|
s.EOL();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Error ProcessLaunchCommandOptions::SetOptionValue(
|
|
|
|
uint32_t option_idx, const char *option_arg,
|
|
|
|
ExecutionContext *execution_context) {
|
|
|
|
Error error;
|
|
|
|
const int short_option = m_getopt_table[option_idx].val;
|
2016-09-20 01:54:06 +08:00
|
|
|
auto option_strref = llvm::StringRef::withNullAsEmpty(option_arg);
|
2016-09-07 04:57:50 +08:00
|
|
|
|
|
|
|
switch (short_option) {
|
|
|
|
case 's': // Stop at program entry point
|
|
|
|
launch_info.GetFlags().Set(eLaunchFlagStopAtEntry);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'i': // STDIN for read only
|
|
|
|
{
|
|
|
|
FileAction action;
|
|
|
|
if (action.Open(STDIN_FILENO, FileSpec{option_arg, false}, true, false))
|
|
|
|
launch_info.AppendFileAction(action);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'o': // Open STDOUT for write only
|
|
|
|
{
|
|
|
|
FileAction action;
|
|
|
|
if (action.Open(STDOUT_FILENO, FileSpec{option_arg, false}, false, true))
|
|
|
|
launch_info.AppendFileAction(action);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'e': // STDERR for write only
|
|
|
|
{
|
|
|
|
FileAction action;
|
|
|
|
if (action.Open(STDERR_FILENO, FileSpec{option_arg, false}, false, true))
|
|
|
|
launch_info.AppendFileAction(action);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'p': // Process plug-in name
|
|
|
|
launch_info.SetProcessPluginName(option_arg);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'n': // Disable STDIO
|
|
|
|
{
|
|
|
|
FileAction action;
|
|
|
|
const FileSpec dev_null{FileSystem::DEV_NULL, false};
|
|
|
|
if (action.Open(STDIN_FILENO, dev_null, true, false))
|
|
|
|
launch_info.AppendFileAction(action);
|
|
|
|
if (action.Open(STDOUT_FILENO, dev_null, false, true))
|
|
|
|
launch_info.AppendFileAction(action);
|
|
|
|
if (action.Open(STDERR_FILENO, dev_null, false, true))
|
|
|
|
launch_info.AppendFileAction(action);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'w':
|
|
|
|
launch_info.SetWorkingDirectory(FileSpec{option_arg, false});
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 't': // Open process in new terminal window
|
|
|
|
launch_info.GetFlags().Set(eLaunchFlagLaunchInTTY);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'a': {
|
|
|
|
TargetSP target_sp =
|
|
|
|
execution_context ? execution_context->GetTargetSP() : TargetSP();
|
|
|
|
PlatformSP platform_sp =
|
|
|
|
target_sp ? target_sp->GetPlatform() : PlatformSP();
|
|
|
|
if (!launch_info.GetArchitecture().SetTriple(option_arg, platform_sp.get()))
|
|
|
|
launch_info.GetArchitecture().SetTriple(option_arg);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case 'A': // Disable ASLR.
|
|
|
|
{
|
|
|
|
bool success;
|
|
|
|
const bool disable_aslr_arg =
|
2016-09-20 01:54:06 +08:00
|
|
|
Args::StringToBoolean(option_strref, true, &success);
|
2016-09-07 04:57:50 +08:00
|
|
|
if (success)
|
|
|
|
disable_aslr = disable_aslr_arg ? eLazyBoolYes : eLazyBoolNo;
|
|
|
|
else
|
|
|
|
error.SetErrorStringWithFormat(
|
|
|
|
"Invalid boolean value for disable-aslr option: '%s'",
|
|
|
|
option_arg ? option_arg : "<null>");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'X': // shell expand args.
|
|
|
|
{
|
|
|
|
bool success;
|
2016-09-20 01:54:06 +08:00
|
|
|
const bool expand_args =
|
|
|
|
Args::StringToBoolean(option_strref, true, &success);
|
2016-09-07 04:57:50 +08:00
|
|
|
if (success)
|
|
|
|
launch_info.SetShellExpandArguments(expand_args);
|
|
|
|
else
|
|
|
|
error.SetErrorStringWithFormat(
|
|
|
|
"Invalid boolean value for shell-expand-args option: '%s'",
|
|
|
|
option_arg ? option_arg : "<null>");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'c':
|
|
|
|
if (option_arg && option_arg[0])
|
|
|
|
launch_info.SetShell(FileSpec(option_arg, false));
|
|
|
|
else
|
|
|
|
launch_info.SetShell(HostInfo::GetDefaultShell());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'v':
|
2016-09-20 01:54:06 +08:00
|
|
|
launch_info.GetEnvironmentEntries().AppendArgument(
|
|
|
|
llvm::StringRef::withNullAsEmpty(option_arg));
|
2016-09-07 04:57:50 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
error.SetErrorStringWithFormat("unrecognized short option character '%c'",
|
|
|
|
short_option);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
Convert option tables to ArrayRefs.
This change is very mechanical. All it does is change the
signature of `Options::GetDefinitions()` and `OptionGroup::
GetDefinitions()` to return an `ArrayRef<OptionDefinition>`
instead of a `const OptionDefinition *`. In the case of the
former, it deletes the sentinel entry from every table, and
in the case of the latter, it removes the `GetNumDefinitions()`
method from the interface. These are no longer necessary as
`ArrayRef` carries its own length.
In the former case, iteration was done by using a sentinel
entry, so there was no knowledge of length. Because of this
the individual option tables were allowed to be defined below
the corresponding class (after all, only a pointer was needed).
Now, however, the length must be known at compile time to
construct the `ArrayRef`, and as a result it is necessary to
move every option table before its corresponding class. This
results in this CL looking very big, but in terms of substance
there is not much here.
Differential revision: https://reviews.llvm.org/D24834
llvm-svn: 282188
2016-09-23 04:22:55 +08:00
|
|
|
static OptionDefinition g_process_launch_options[] = {
|
2016-09-07 04:57:50 +08:00
|
|
|
{LLDB_OPT_SET_ALL, false, "stop-at-entry", 's', OptionParser::eNoArgument,
|
|
|
|
nullptr, nullptr, 0, eArgTypeNone,
|
|
|
|
"Stop at the entry point of the program when launching a process."},
|
|
|
|
{LLDB_OPT_SET_ALL, false, "disable-aslr", 'A',
|
|
|
|
OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean,
|
|
|
|
"Set whether to disable address space layout randomization when launching "
|
|
|
|
"a process."},
|
|
|
|
{LLDB_OPT_SET_ALL, false, "plugin", 'p', OptionParser::eRequiredArgument,
|
|
|
|
nullptr, nullptr, 0, eArgTypePlugin,
|
|
|
|
"Name of the process plugin you want to use."},
|
|
|
|
{LLDB_OPT_SET_ALL, false, "working-dir", 'w',
|
|
|
|
OptionParser::eRequiredArgument, nullptr, nullptr, 0,
|
|
|
|
eArgTypeDirectoryName,
|
|
|
|
"Set the current working directory to <path> when running the inferior."},
|
|
|
|
{LLDB_OPT_SET_ALL, false, "arch", 'a', OptionParser::eRequiredArgument,
|
|
|
|
nullptr, nullptr, 0, eArgTypeArchitecture,
|
|
|
|
"Set the architecture for the process to launch when ambiguous."},
|
|
|
|
{LLDB_OPT_SET_ALL, false, "environment", 'v',
|
|
|
|
OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeNone,
|
|
|
|
"Specify an environment variable name/value string (--environment "
|
|
|
|
"NAME=VALUE). Can be specified multiple times for subsequent environment "
|
|
|
|
"entries."},
|
|
|
|
{LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "shell", 'c',
|
|
|
|
OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeFilename,
|
|
|
|
"Run the process in a shell (not supported on all platforms)."},
|
|
|
|
|
|
|
|
{LLDB_OPT_SET_1, false, "stdin", 'i', OptionParser::eRequiredArgument,
|
|
|
|
nullptr, nullptr, 0, eArgTypeFilename,
|
|
|
|
"Redirect stdin for the process to <filename>."},
|
|
|
|
{LLDB_OPT_SET_1, false, "stdout", 'o', OptionParser::eRequiredArgument,
|
|
|
|
nullptr, nullptr, 0, eArgTypeFilename,
|
|
|
|
"Redirect stdout for the process to <filename>."},
|
|
|
|
{LLDB_OPT_SET_1, false, "stderr", 'e', OptionParser::eRequiredArgument,
|
|
|
|
nullptr, nullptr, 0, eArgTypeFilename,
|
|
|
|
"Redirect stderr for the process to <filename>."},
|
|
|
|
|
|
|
|
{LLDB_OPT_SET_2, false, "tty", 't', OptionParser::eNoArgument, nullptr,
|
|
|
|
nullptr, 0, eArgTypeNone,
|
|
|
|
"Start the process in a terminal (not supported on all platforms)."},
|
|
|
|
|
|
|
|
{LLDB_OPT_SET_3, false, "no-stdio", 'n', OptionParser::eNoArgument, nullptr,
|
|
|
|
nullptr, 0, eArgTypeNone,
|
|
|
|
"Do not set up for terminal I/O to go to running process."},
|
|
|
|
{LLDB_OPT_SET_4, false, "shell-expand-args", 'X',
|
|
|
|
OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean,
|
|
|
|
"Set whether to shell expand arguments to the process when launching."},
|
Convert option tables to ArrayRefs.
This change is very mechanical. All it does is change the
signature of `Options::GetDefinitions()` and `OptionGroup::
GetDefinitions()` to return an `ArrayRef<OptionDefinition>`
instead of a `const OptionDefinition *`. In the case of the
former, it deletes the sentinel entry from every table, and
in the case of the latter, it removes the `GetNumDefinitions()`
method from the interface. These are no longer necessary as
`ArrayRef` carries its own length.
In the former case, iteration was done by using a sentinel
entry, so there was no knowledge of length. Because of this
the individual option tables were allowed to be defined below
the corresponding class (after all, only a pointer was needed).
Now, however, the length must be known at compile time to
construct the `ArrayRef`, and as a result it is necessary to
move every option table before its corresponding class. This
results in this CL looking very big, but in terms of substance
there is not much here.
Differential revision: https://reviews.llvm.org/D24834
llvm-svn: 282188
2016-09-23 04:22:55 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
llvm::ArrayRef<OptionDefinition> ProcessLaunchCommandOptions::GetDefinitions() {
|
2016-09-23 05:06:13 +08:00
|
|
|
return llvm::makeArrayRef(g_process_launch_options);
|
Convert option tables to ArrayRefs.
This change is very mechanical. All it does is change the
signature of `Options::GetDefinitions()` and `OptionGroup::
GetDefinitions()` to return an `ArrayRef<OptionDefinition>`
instead of a `const OptionDefinition *`. In the case of the
former, it deletes the sentinel entry from every table, and
in the case of the latter, it removes the `GetNumDefinitions()`
method from the interface. These are no longer necessary as
`ArrayRef` carries its own length.
In the former case, iteration was done by using a sentinel
entry, so there was no knowledge of length. Because of this
the individual option tables were allowed to be defined below
the corresponding class (after all, only a pointer was needed).
Now, however, the length must be known at compile time to
construct the `ArrayRef`, and as a result it is necessary to
move every option table before its corresponding class. This
results in this CL looking very big, but in terms of substance
there is not much here.
Differential revision: https://reviews.llvm.org/D24834
llvm-svn: 282188
2016-09-23 04:22:55 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
|
|
|
|
bool ProcessInstanceInfoMatch::NameMatches(const char *process_name) const {
|
|
|
|
if (m_name_match_type == eNameMatchIgnore || process_name == nullptr)
|
|
|
|
return true;
|
|
|
|
const char *match_name = m_match_info.GetName();
|
|
|
|
if (!match_name)
|
|
|
|
return true;
|
2011-04-01 08:29:43 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
return lldb_private::NameMatches(process_name, m_name_match_type, match_name);
|
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-31 02:16:51 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool ProcessInstanceInfoMatch::Matches(
|
|
|
|
const ProcessInstanceInfo &proc_info) const {
|
|
|
|
if (!NameMatches(proc_info.GetName()))
|
|
|
|
return false;
|
2011-04-12 13:54:46 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (m_match_info.ProcessIDIsValid() &&
|
|
|
|
m_match_info.GetProcessID() != proc_info.GetProcessID())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (m_match_info.ParentProcessIDIsValid() &&
|
|
|
|
m_match_info.GetParentProcessID() != proc_info.GetParentProcessID())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (m_match_info.UserIDIsValid() &&
|
|
|
|
m_match_info.GetUserID() != proc_info.GetUserID())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (m_match_info.GroupIDIsValid() &&
|
|
|
|
m_match_info.GetGroupID() != proc_info.GetGroupID())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (m_match_info.EffectiveUserIDIsValid() &&
|
|
|
|
m_match_info.GetEffectiveUserID() != proc_info.GetEffectiveUserID())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (m_match_info.EffectiveGroupIDIsValid() &&
|
|
|
|
m_match_info.GetEffectiveGroupID() != proc_info.GetEffectiveGroupID())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (m_match_info.GetArchitecture().IsValid() &&
|
|
|
|
!m_match_info.GetArchitecture().IsCompatibleMatch(
|
|
|
|
proc_info.GetArchitecture()))
|
|
|
|
return false;
|
|
|
|
return true;
|
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-31 02:16:51 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool ProcessInstanceInfoMatch::MatchAllProcesses() const {
|
|
|
|
if (m_name_match_type != eNameMatchIgnore)
|
|
|
|
return false;
|
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-31 02:16:51 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (m_match_info.ProcessIDIsValid())
|
|
|
|
return false;
|
2011-04-01 08:29:43 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (m_match_info.ParentProcessIDIsValid())
|
|
|
|
return false;
|
2015-10-14 07:41:19 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (m_match_info.UserIDIsValid())
|
|
|
|
return false;
|
2011-04-01 08:29:43 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (m_match_info.GroupIDIsValid())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (m_match_info.EffectiveUserIDIsValid())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (m_match_info.EffectiveGroupIDIsValid())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (m_match_info.GetArchitecture().IsValid())
|
|
|
|
return false;
|
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-31 02:16:51 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (m_match_all_users)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessInstanceInfoMatch::Clear() {
|
|
|
|
m_match_info.Clear();
|
|
|
|
m_name_match_type = eNameMatchIgnore;
|
|
|
|
m_match_all_users = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ProcessSP Process::FindPlugin(lldb::TargetSP target_sp, const char *plugin_name,
|
|
|
|
ListenerSP listener_sp,
|
|
|
|
const FileSpec *crash_file_path) {
|
|
|
|
static uint32_t g_process_unique_id = 0;
|
|
|
|
|
|
|
|
ProcessSP process_sp;
|
|
|
|
ProcessCreateInstance create_callback = nullptr;
|
|
|
|
if (plugin_name) {
|
|
|
|
ConstString const_plugin_name(plugin_name);
|
|
|
|
create_callback =
|
|
|
|
PluginManager::GetProcessCreateCallbackForPluginName(const_plugin_name);
|
|
|
|
if (create_callback) {
|
|
|
|
process_sp = create_callback(target_sp, listener_sp, crash_file_path);
|
|
|
|
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);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
ConstString &Process::GetStaticBroadcasterClass() {
|
|
|
|
static ConstString class_name("lldb.process");
|
|
|
|
return class_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp)
|
|
|
|
: Process(target_sp, listener_sp,
|
|
|
|
UnixSignals::Create(HostInfo::GetArchitecture())) {
|
|
|
|
// 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), UserID(LLDB_INVALID_PROCESS_ID),
|
|
|
|
Broadcaster((target_sp->GetDebugger().GetBroadcasterManager()),
|
|
|
|
Process::GetStaticBroadcasterClass().AsCString()),
|
|
|
|
m_target_sp(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_exit_string(),
|
|
|
|
m_exit_status_mutex(), m_thread_mutex(), m_thread_list_real(this),
|
|
|
|
m_thread_list(this), m_extended_thread_list(this),
|
|
|
|
m_extended_thread_stop_id(0), m_queue_list(this), m_queue_list_stop_id(0),
|
|
|
|
m_notifications(), m_image_tokens(), m_listener_sp(listener_sp),
|
|
|
|
m_breakpoint_site_list(), m_dynamic_checkers_ap(),
|
|
|
|
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_ap(), m_public_run_lock(),
|
|
|
|
m_private_run_lock(), m_stop_info_override_callback(nullptr),
|
|
|
|
m_finalizing(false), m_finalize_called(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_warnings_issued(),
|
|
|
|
m_run_thread_plan_lock(), m_can_jit(eCanJITDontKnow) {
|
|
|
|
CheckInWithManager();
|
|
|
|
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
|
|
|
|
if (log)
|
|
|
|
log->Printf("%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");
|
|
|
|
|
|
|
|
m_listener_sp->StartListeningForEvents(
|
|
|
|
this, eBroadcastBitStateChanged | eBroadcastBitInterrupt |
|
|
|
|
eBroadcastBitSTDOUT | eBroadcastBitSTDERR |
|
|
|
|
eBroadcastBitProfileData | eBroadcastBitStructuredData);
|
|
|
|
|
|
|
|
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(nullptr, true, ePropertyMemCacheLineSize)
|
|
|
|
->GetValue();
|
|
|
|
uint32_t platform_cache_line_size =
|
|
|
|
target_sp->GetPlatform()->GetDefaultMemoryCacheLineSize();
|
|
|
|
if (!value_sp->OptionWasSet() && platform_cache_line_size != 0)
|
|
|
|
value_sp->SetUInt64Value(platform_cache_line_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
Process::~Process() {
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
|
|
|
|
if (log)
|
|
|
|
log->Printf("%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();
|
|
|
|
}
|
|
|
|
|
|
|
|
const ProcessPropertiesSP &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
|
2016-10-19 23:12:45 +08:00
|
|
|
static ProcessPropertiesSP *g_settings_sp_ptr =
|
|
|
|
new ProcessPropertiesSP(new ProcessProperties(nullptr));
|
2016-09-07 04:57:50 +08:00
|
|
|
return *g_settings_sp_ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Process::Finalize() {
|
|
|
|
m_finalizing = true;
|
|
|
|
|
|
|
|
// Destroy this process if needed
|
|
|
|
switch (GetPrivateState()) {
|
|
|
|
case eStateConnected:
|
|
|
|
case eStateAttaching:
|
|
|
|
case eStateLaunching:
|
|
|
|
case eStateStopped:
|
|
|
|
case eStateRunning:
|
|
|
|
case eStateStepping:
|
|
|
|
case eStateCrashed:
|
|
|
|
case eStateSuspended:
|
|
|
|
Destroy(false);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eStateInvalid:
|
|
|
|
case eStateUnloaded:
|
|
|
|
case eStateDetached:
|
|
|
|
case eStateExited:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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_ap.reset();
|
|
|
|
m_abi_sp.reset();
|
|
|
|
m_os_ap.reset();
|
|
|
|
m_system_runtime_ap.reset();
|
|
|
|
m_dyld_ap.reset();
|
|
|
|
m_jit_loaders_ap.reset();
|
|
|
|
m_thread_list_real.Destroy();
|
|
|
|
m_thread_list.Destroy();
|
|
|
|
m_extended_thread_list.Destroy();
|
|
|
|
m_queue_list.Clear();
|
|
|
|
m_queue_list_stop_id = 0;
|
|
|
|
std::vector<Notifications> empty_notifications;
|
|
|
|
m_notifications.swap(empty_notifications);
|
|
|
|
m_image_tokens.clear();
|
|
|
|
m_memory_cache.Clear();
|
|
|
|
m_allocated_memory_cache.Clear();
|
|
|
|
m_language_runtimes.clear();
|
|
|
|
m_instrumentation_runtimes.clear();
|
|
|
|
m_next_event_action_ap.reset();
|
|
|
|
m_stop_info_override_callback = nullptr;
|
|
|
|
// Clear the last natural stop ID since it has a strong
|
|
|
|
// reference to this process
|
|
|
|
m_mod_id.SetStopEventForLastNaturalStopID(EventSP());
|
|
|
|
//#ifdef LLDB_CONFIGURATION_DEBUG
|
|
|
|
// StreamFile s(stdout, false);
|
|
|
|
// EventSP event_sp;
|
|
|
|
// while (m_private_state_listener_sp->GetNextEvent(event_sp))
|
|
|
|
// {
|
|
|
|
// event_sp->Dump (&s);
|
|
|
|
// s.EOL();
|
|
|
|
// }
|
|
|
|
//#endif
|
|
|
|
// 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.TrySetRunning(); // This will do nothing if already locked
|
|
|
|
m_public_run_lock.SetStopped();
|
|
|
|
m_private_run_lock.TrySetRunning(); // This will do nothing if already locked
|
|
|
|
m_private_run_lock.SetStopped();
|
|
|
|
m_finalize_called = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 (m_listener_sp->GetNextEventForBroadcaster(this, event_sp) && event_sp)
|
|
|
|
state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
|
|
|
|
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Process::SyncIOHandler(uint32_t iohandler_id, uint64_t timeout_msec) {
|
|
|
|
// don't sync (potentially context switch) in case where there is no process
|
|
|
|
// IO
|
|
|
|
if (!m_process_input_reader)
|
|
|
|
return;
|
|
|
|
|
|
|
|
uint32_t new_iohandler_id = 0;
|
|
|
|
m_iohandler_sync.WaitForValueNotEqualTo(
|
|
|
|
iohandler_id, new_iohandler_id, std::chrono::milliseconds(timeout_msec));
|
|
|
|
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s waited for m_iohandler_sync to change from %u, "
|
|
|
|
"new value is %u",
|
|
|
|
__FUNCTION__, iohandler_id, new_iohandler_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
StateType
|
|
|
|
Process::WaitForProcessToStop(const std::chrono::microseconds &timeout,
|
|
|
|
EventSP *event_sp_ptr, bool wait_always,
|
|
|
|
ListenerSP hijack_listener_sp, Stream *stream,
|
|
|
|
bool use_run_lock) {
|
|
|
|
// 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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s (timeout = %llu)", __FUNCTION__,
|
|
|
|
static_cast<unsigned long long>(timeout.count()));
|
|
|
|
|
|
|
|
if (!wait_always && StateIsStoppedState(state, true) &&
|
|
|
|
StateIsStoppedState(GetPrivateState(), true)) {
|
|
|
|
if (log)
|
|
|
|
log->Printf("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 = WaitForStateChangedEvents(timeout, event_sp, 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,
|
|
|
|
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;
|
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-31 02:16:51 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
return state;
|
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-31 02:16:51 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Process::HandleProcessStateChangedEvent(const EventSP &event_sp,
|
|
|
|
Stream *stream,
|
|
|
|
bool &pop_process_io_handler) {
|
|
|
|
const bool handle_pop = pop_process_io_handler;
|
2012-03-06 12:01:04 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
pop_process_io_handler = false;
|
|
|
|
ProcessSP process_sp =
|
|
|
|
Process::ProcessEventData::GetProcessFromEvent(event_sp.get());
|
2012-03-06 12:01:04 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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>");
|
|
|
|
}
|
|
|
|
}
|
2014-08-16 06:04:21 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
} 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());
|
|
|
|
ThreadSP thread;
|
|
|
|
StopReason curr_thread_stop_reason = eStopReasonInvalid;
|
|
|
|
if (curr_thread) {
|
|
|
|
curr_thread_stop_reason = curr_thread->GetStopReason();
|
|
|
|
curr_thread_stop_info_sp = curr_thread->GetStopInfo();
|
|
|
|
}
|
|
|
|
if (!curr_thread || !curr_thread->IsValid() ||
|
|
|
|
curr_thread_stop_reason == eStopReasonInvalid ||
|
|
|
|
curr_thread_stop_reason == eStopReasonNone) {
|
|
|
|
// Prefer a thread that has just completed its plan over another
|
|
|
|
// thread as current thread.
|
|
|
|
ThreadSP plan_thread;
|
|
|
|
ThreadSP other_thread;
|
|
|
|
|
|
|
|
const size_t num_threads = thread_list.GetSize();
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < num_threads; ++i) {
|
|
|
|
thread = thread_list.GetThreadAtIndex(i);
|
|
|
|
StopReason thread_stop_reason = thread->GetStopReason();
|
|
|
|
switch (thread_stop_reason) {
|
|
|
|
case eStopReasonInvalid:
|
|
|
|
case eStopReasonNone:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eStopReasonSignal: {
|
|
|
|
// Don't select a signal thread if we weren't going to stop at
|
|
|
|
// that
|
|
|
|
// signal. We have to have had another reason for stopping here,
|
|
|
|
// and
|
|
|
|
// the user doesn't want to see this thread.
|
|
|
|
uint64_t signo = thread->GetStopInfo()->GetValue();
|
|
|
|
if (process_sp->GetUnixSignals()->GetShouldStop(signo)) {
|
|
|
|
if (!other_thread)
|
|
|
|
other_thread = thread;
|
|
|
|
}
|
|
|
|
break;
|
2016-08-12 07:51:28 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
case eStopReasonTrace:
|
|
|
|
case eStopReasonBreakpoint:
|
|
|
|
case eStopReasonWatchpoint:
|
|
|
|
case eStopReasonException:
|
|
|
|
case eStopReasonExec:
|
|
|
|
case eStopReasonThreadExiting:
|
|
|
|
case eStopReasonInstrumentation:
|
|
|
|
if (!other_thread)
|
|
|
|
other_thread = thread;
|
|
|
|
break;
|
|
|
|
case eStopReasonPlanComplete:
|
|
|
|
if (!plan_thread)
|
|
|
|
plan_thread = thread;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (plan_thread)
|
|
|
|
thread_list.SetSelectedThreadByID(plan_thread->GetID());
|
|
|
|
else if (other_thread)
|
|
|
|
thread_list.SetSelectedThreadByID(other_thread->GetID());
|
|
|
|
else {
|
|
|
|
if (curr_thread && curr_thread->IsValid())
|
|
|
|
thread = curr_thread;
|
2014-08-20 01:40:43 +08:00
|
|
|
else
|
2016-09-07 04:57:50 +08:00
|
|
|
thread = thread_list.GetThreadAtIndex(0);
|
2014-08-20 01:40:43 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (thread)
|
|
|
|
thread_list.SetSelectedThreadByID(thread->GetID());
|
|
|
|
}
|
2015-02-10 11:06:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
// 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()) {
|
|
|
|
const bool only_threads_with_stop_reason = true;
|
|
|
|
const uint32_t start_frame = 0;
|
|
|
|
const uint32_t num_frames = 1;
|
|
|
|
const uint32_t num_frames_with_source = 1;
|
2016-11-09 04:36:40 +08:00
|
|
|
const bool stop_format = true;
|
2016-09-07 04:57:50 +08:00
|
|
|
process_sp->GetStatus(*stream);
|
|
|
|
process_sp->GetThreadStatus(*stream, only_threads_with_stop_reason,
|
|
|
|
start_frame, num_frames,
|
2016-11-09 04:36:40 +08:00
|
|
|
num_frames_with_source,
|
|
|
|
stop_format);
|
2016-09-07 04:57:50 +08:00
|
|
|
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 bool qualify_cxx_base_classes = false;
|
|
|
|
|
|
|
|
const ValueObject::GetExpressionPathFormat format =
|
|
|
|
ValueObject::GetExpressionPathFormat::
|
|
|
|
eGetExpressionPathFormatHonorPointers;
|
|
|
|
stream->PutCString("Likely cause: ");
|
|
|
|
valobj_sp->GetExpressionPath(*stream, qualify_cxx_base_classes,
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
}
|
2011-04-12 13:54:46 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Pop the process IO handler
|
|
|
|
pop_process_io_handler = true;
|
2011-04-12 13:54:46 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (handle_pop && pop_process_io_handler)
|
|
|
|
process_sp->PopProcessIOHandler();
|
|
|
|
|
|
|
|
return true;
|
2011-04-12 13:54:46 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
StateType Process::WaitForState(const std::chrono::microseconds &timeout,
|
|
|
|
const StateType *match_states,
|
|
|
|
const uint32_t num_match_states) {
|
|
|
|
EventSP event_sp;
|
|
|
|
StateType state = GetState();
|
|
|
|
while (state != eStateInvalid) {
|
|
|
|
// If we are exited or detached, we won't ever get back to any
|
|
|
|
// other valid state...
|
|
|
|
if (state == eStateDetached || state == eStateExited)
|
|
|
|
return state;
|
2011-04-12 13:54:46 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
state = WaitForStateChangedEvents(timeout, event_sp, nullptr);
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < num_match_states; ++i) {
|
|
|
|
if (match_states[i] == state)
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return state;
|
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-31 02:16:51 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Process::HijackProcessEvents(ListenerSP listener_sp) {
|
|
|
|
if (listener_sp) {
|
|
|
|
return HijackBroadcaster(listener_sp, eBroadcastBitStateChanged |
|
|
|
|
eBroadcastBitInterrupt);
|
|
|
|
} else
|
|
|
|
return false;
|
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-31 02:16:51 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::RestoreProcessEvents() { RestoreBroadcaster(); }
|
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-31 02:16:51 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
StateType
|
|
|
|
Process::WaitForStateChangedEvents(const std::chrono::microseconds &timeout,
|
|
|
|
EventSP &event_sp,
|
|
|
|
ListenerSP hijack_listener_sp) {
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s (timeout = %llu, event_sp)...", __FUNCTION__,
|
|
|
|
static_cast<unsigned long long>(timeout.count()));
|
|
|
|
|
|
|
|
ListenerSP listener_sp = hijack_listener_sp;
|
|
|
|
if (!listener_sp)
|
|
|
|
listener_sp = m_listener_sp;
|
|
|
|
|
|
|
|
StateType state = eStateInvalid;
|
|
|
|
if (listener_sp->WaitForEventForBroadcasterWithType(
|
|
|
|
timeout, this, eBroadcastBitStateChanged | eBroadcastBitInterrupt,
|
|
|
|
event_sp)) {
|
|
|
|
if (event_sp && event_sp->GetType() == eBroadcastBitStateChanged)
|
|
|
|
state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
|
|
|
|
else if (log)
|
|
|
|
log->Printf("Process::%s got no event or was interrupted.", __FUNCTION__);
|
|
|
|
}
|
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-31 02:16:51 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s (timeout = %llu, event_sp) => %s", __FUNCTION__,
|
|
|
|
static_cast<unsigned long long>(timeout.count()),
|
|
|
|
StateAsCString(state));
|
|
|
|
return state;
|
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-31 02:16:51 +08:00
|
|
|
}
|
2011-01-07 14:08:19 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Event *Process::PeekAtStateChangedEvents() {
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s...", __FUNCTION__);
|
|
|
|
|
|
|
|
Event *event_ptr;
|
|
|
|
event_ptr = m_listener_sp->PeekAtNextEventForBroadcasterWithType(
|
|
|
|
this, eBroadcastBitStateChanged);
|
|
|
|
if (log) {
|
|
|
|
if (event_ptr) {
|
|
|
|
log->Printf(
|
|
|
|
"Process::%s (event_ptr) => %s", __FUNCTION__,
|
|
|
|
StateAsCString(ProcessEventData::GetStateFromEvent(event_ptr)));
|
|
|
|
} else {
|
|
|
|
log->Printf("Process::%s no events found", __FUNCTION__);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
return event_ptr;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
StateType Process::WaitForStateChangedEventsPrivate(
|
|
|
|
const std::chrono::microseconds &timeout, EventSP &event_sp) {
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s (timeout = %llu, event_sp)...", __FUNCTION__,
|
|
|
|
static_cast<unsigned long long>(timeout.count()));
|
2014-08-30 01:35:57 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
StateType state = eStateInvalid;
|
|
|
|
if (m_private_state_listener_sp->WaitForEventForBroadcasterWithType(
|
|
|
|
timeout, &m_private_state_broadcaster,
|
|
|
|
eBroadcastBitStateChanged | eBroadcastBitInterrupt, event_sp))
|
|
|
|
if (event_sp && event_sp->GetType() == eBroadcastBitStateChanged)
|
|
|
|
state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
|
2016-05-19 13:13:57 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// This is a bit of a hack, but when we wait here we could very well return
|
|
|
|
// to the command-line, and that could disable the log, which would render the
|
|
|
|
// log we got above invalid.
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s (timeout = %llu, event_sp) => %s", __FUNCTION__,
|
|
|
|
static_cast<unsigned long long>(timeout.count()),
|
|
|
|
state == eStateInvalid ? "TIMEOUT" : StateAsCString(state));
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Process::WaitForEventsPrivate(const std::chrono::microseconds &timeout,
|
|
|
|
EventSP &event_sp, bool control_only) {
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s (timeout = %llu, event_sp)...", __FUNCTION__,
|
|
|
|
static_cast<unsigned long long>(timeout.count()));
|
2014-08-30 01:35:57 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (control_only)
|
|
|
|
return m_private_state_listener_sp->WaitForEventForBroadcaster(
|
|
|
|
timeout, &m_private_state_control_broadcaster, event_sp);
|
|
|
|
else
|
|
|
|
return m_private_state_listener_sp->WaitForEvent(timeout, event_sp);
|
|
|
|
}
|
2015-07-14 09:09:28 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Process::IsRunning() const {
|
|
|
|
return StateIsRunningState(m_public_state.GetValue());
|
|
|
|
}
|
2012-10-30 04:52:08 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
int Process::GetExitStatus() {
|
|
|
|
std::lock_guard<std::mutex> guard(m_exit_status_mutex);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (m_public_state.GetValue() == eStateExited)
|
|
|
|
return m_exit_status;
|
|
|
|
return -1;
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const char *Process::GetExitDescription() {
|
|
|
|
std::lock_guard<std::mutex> guard(m_exit_status_mutex);
|
2015-10-20 18:33:17 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (m_public_state.GetValue() == eStateExited && !m_exit_string.empty())
|
|
|
|
return m_exit_string.c_str();
|
|
|
|
return nullptr;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Process::SetExitStatus(int status, const char *cstr) {
|
|
|
|
// Use a mutex to protect setting the exit status.
|
|
|
|
std::lock_guard<std::mutex> guard(m_exit_status_mutex);
|
|
|
|
|
|
|
|
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE |
|
|
|
|
LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"Process::SetExitStatus (status=%i (0x%8.8x), description=%s%s%s)",
|
|
|
|
status, status, cstr ? "\"" : "", cstr ? cstr : "NULL",
|
|
|
|
cstr ? "\"" : "");
|
|
|
|
|
|
|
|
// We were already in the exited state
|
|
|
|
if (m_private_state.GetValue() == eStateExited) {
|
2010-06-09 00:52:24 +08:00
|
|
|
if (log)
|
2016-09-07 04:57:50 +08:00
|
|
|
log->Printf("Process::SetExitStatus () ignoring exit status because "
|
|
|
|
"state was already set to eStateExited");
|
|
|
|
return false;
|
|
|
|
}
|
2014-09-10 04:54:56 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
m_exit_status = status;
|
|
|
|
if (cstr)
|
|
|
|
m_exit_string = cstr;
|
|
|
|
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;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
|
|
|
}
|
2012-08-23 01:17:09 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// 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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("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) {
|
|
|
|
const char *signal_cstr = nullptr;
|
|
|
|
if (signo)
|
|
|
|
signal_cstr = process_sp->GetUnixSignals()->GetSignalAsCString(signo);
|
|
|
|
|
|
|
|
process_sp->SetExitStatus(exit_status, signal_cstr);
|
|
|
|
}
|
2015-03-26 15:40:40 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Process::UpdateThreadListIfNeeded() {
|
|
|
|
const uint32_t stop_id = GetStopID();
|
|
|
|
if (m_thread_list.GetSize(false) == 0 ||
|
|
|
|
stop_id != m_thread_list.GetStopID()) {
|
|
|
|
const StateType state = GetPrivateState();
|
|
|
|
if (StateIsStoppedState(state, true)) {
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(m_thread_list.GetMutex());
|
|
|
|
// 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();
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
2011-11-17 12:46:02 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
m_thread_list_real.Update(real_thread_list);
|
|
|
|
m_thread_list.Update(new_thread_list);
|
|
|
|
m_thread_list.SetStopID(stop_id);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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();
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
m_queue_list.Clear();
|
|
|
|
m_queue_list_stop_id = GetLastNaturalStopID();
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Process::UpdateQueueListIfNeeded() {
|
|
|
|
if (m_system_runtime_ap) {
|
|
|
|
if (m_queue_list.GetSize() == 0 ||
|
|
|
|
m_queue_list_stop_id != GetLastNaturalStopID()) {
|
|
|
|
const StateType state = GetPrivateState();
|
|
|
|
if (StateIsStoppedState(state, true)) {
|
|
|
|
m_system_runtime_ap->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) {
|
|
|
|
uint32_t result = 0;
|
|
|
|
std::map<uint64_t, uint32_t>::iterator iterator =
|
|
|
|
m_thread_id_to_index_id_map.find(thread_id);
|
|
|
|
if (iterator == m_thread_id_to_index_id_map.end()) {
|
|
|
|
result = ++m_thread_index_id;
|
|
|
|
m_thread_id_to_index_id_map[thread_id] = result;
|
|
|
|
} else {
|
|
|
|
result = iterator->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
StateType Process::GetState() {
|
|
|
|
// If any other threads access this we will need a mutex for it
|
|
|
|
return m_public_state.GetValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Process::StateChangedIsExternallyHijacked() {
|
|
|
|
if (IsHijackedForEvent(eBroadcastBitStateChanged)) {
|
|
|
|
const char *hijacking_name = GetHijackingListenerName();
|
|
|
|
if (hijacking_name &&
|
|
|
|
strcmp(hijacking_name, "lldb.Process.ResumeSynchronous.hijack"))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Process::SetPublicState(StateType new_state, bool restarted) {
|
|
|
|
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE |
|
|
|
|
LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::SetPublicState (state = %s, restarted = %i)",
|
|
|
|
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) {
|
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"Process::SetPublicState (%s) -- unlocking run lock for detach",
|
|
|
|
StateAsCString(new_state));
|
|
|
|
m_public_run_lock.SetStopped();
|
|
|
|
} else {
|
|
|
|
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 && !restarted) {
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::SetPublicState (%s) -- unlocking run lock",
|
|
|
|
StateAsCString(new_state));
|
|
|
|
m_public_run_lock.SetStopped();
|
|
|
|
}
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error Process::Resume() {
|
|
|
|
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE |
|
|
|
|
LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::Resume -- locking run lock");
|
|
|
|
if (!m_public_run_lock.TrySetRunning()) {
|
|
|
|
Error error("Resume request failed - process still running.");
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::Resume: -- TrySetRunning failed, not resuming.");
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
return PrivateResume();
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error Process::ResumeSynchronous(Stream *stream) {
|
|
|
|
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE |
|
|
|
|
LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::ResumeSynchronous -- locking run lock");
|
|
|
|
if (!m_public_run_lock.TrySetRunning()) {
|
|
|
|
Error error("Resume request failed - process still running.");
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::Resume: -- TrySetRunning failed, not resuming.");
|
|
|
|
return error;
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
ListenerSP listener_sp(
|
|
|
|
Listener::MakeListener("lldb.Process.ResumeSynchronous.hijack"));
|
|
|
|
HijackProcessEvents(listener_sp);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error error = PrivateResume();
|
|
|
|
if (error.Success()) {
|
|
|
|
StateType state = WaitForProcessToStop(std::chrono::microseconds(0), NULL,
|
|
|
|
true, listener_sp, stream);
|
|
|
|
const bool must_be_alive =
|
|
|
|
false; // eStateExited is ok, so this must be false
|
|
|
|
if (!StateIsStoppedState(state, must_be_alive))
|
|
|
|
error.SetErrorStringWithFormat(
|
|
|
|
"process not in stopped state after synchronous resume: %s",
|
|
|
|
StateAsCString(state));
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Undo the hijacking of process events...
|
|
|
|
RestoreProcessEvents();
|
|
|
|
|
|
|
|
return error;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
StateType Process::GetPrivateState() { return m_private_state.GetValue(); }
|
2014-08-12 22:33:19 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::SetPrivateState(StateType new_state) {
|
|
|
|
if (m_finalize_called)
|
|
|
|
return;
|
2014-08-12 22:33:19 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STATE |
|
|
|
|
LIBLLDB_LOG_PROCESS));
|
|
|
|
bool state_changed = false;
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (log)
|
|
|
|
log->Printf("Process::SetPrivateState (%s)", StateAsCString(new_state));
|
2011-08-09 10:12:22 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
std::lock_guard<std::recursive_mutex> thread_guard(m_thread_list.GetMutex());
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(m_private_state.GetMutex());
|
2013-10-10 00:56:28 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const StateType old_state = m_private_state.GetValueNoLock();
|
|
|
|
state_changed = old_state != new_state;
|
2013-10-10 00:56:28 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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();
|
|
|
|
|
|
|
|
m_mod_id.BumpStopID();
|
|
|
|
if (!m_mod_id.IsLastResumeForUserExpression())
|
|
|
|
m_mod_id.SetStopEventForLastNaturalStopID(event_sp);
|
|
|
|
m_memory_cache.Clear();
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::SetPrivateState (%s) stop_id = %u",
|
|
|
|
StateAsCString(new_state), m_mod_id.GetStopID());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use our target to get a shared pointer to ourselves...
|
|
|
|
if (m_finalize_called && !PrivateStateThreadIsValid())
|
|
|
|
BroadcastEvent(event_sp);
|
|
|
|
else
|
|
|
|
m_private_state_broadcaster.BroadcastEvent(event_sp);
|
|
|
|
} else {
|
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"Process::SetPrivateState (%s) state didn't change. Ignoring...",
|
|
|
|
StateAsCString(new_state));
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::SetRunningUserExpression(bool on) {
|
|
|
|
m_mod_id.SetRunningUserExpression(on);
|
|
|
|
}
|
2014-10-21 09:00:42 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
addr_t Process::GetImageInfoAddress() { return LLDB_INVALID_ADDRESS; }
|
2014-10-21 09:00:42 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const lldb::ABISP &Process::GetABI() {
|
|
|
|
if (!m_abi_sp)
|
|
|
|
m_abi_sp = ABI::FindPlugin(GetTarget().GetArchitecture());
|
|
|
|
return m_abi_sp;
|
|
|
|
}
|
2014-10-21 09:00:42 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
LanguageRuntime *Process::GetLanguageRuntime(lldb::LanguageType language,
|
|
|
|
bool retry_if_null) {
|
|
|
|
if (m_finalizing)
|
|
|
|
return nullptr;
|
2014-10-21 09:00:42 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
LanguageRuntimeCollection::iterator pos;
|
|
|
|
pos = m_language_runtimes.find(language);
|
|
|
|
if (pos == m_language_runtimes.end() || (retry_if_null && !(*pos).second)) {
|
|
|
|
lldb::LanguageRuntimeSP runtime_sp(
|
|
|
|
LanguageRuntime::FindPlugin(this, language));
|
2014-10-21 09:00:42 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
m_language_runtimes[language] = runtime_sp;
|
|
|
|
return runtime_sp.get();
|
|
|
|
} else
|
|
|
|
return (*pos).second.get();
|
2014-10-21 09:00:42 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
CPPLanguageRuntime *Process::GetCPPLanguageRuntime(bool retry_if_null) {
|
|
|
|
LanguageRuntime *runtime =
|
|
|
|
GetLanguageRuntime(eLanguageTypeC_plus_plus, retry_if_null);
|
|
|
|
if (runtime != nullptr &&
|
|
|
|
runtime->GetLanguageType() == eLanguageTypeC_plus_plus)
|
|
|
|
return static_cast<CPPLanguageRuntime *>(runtime);
|
|
|
|
return nullptr;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
ObjCLanguageRuntime *Process::GetObjCLanguageRuntime(bool retry_if_null) {
|
|
|
|
LanguageRuntime *runtime =
|
|
|
|
GetLanguageRuntime(eLanguageTypeObjC, retry_if_null);
|
|
|
|
if (runtime != nullptr && runtime->GetLanguageType() == eLanguageTypeObjC)
|
|
|
|
return static_cast<ObjCLanguageRuntime *>(runtime);
|
|
|
|
return nullptr;
|
2010-10-12 07:53:14 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Process::IsPossibleDynamicValue(ValueObject &in_value) {
|
|
|
|
if (m_finalizing)
|
|
|
|
return false;
|
2010-10-12 07:53:14 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (in_value.IsDynamic())
|
|
|
|
return false;
|
|
|
|
LanguageType known_type = in_value.GetObjectRuntimeLanguage();
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (known_type != eLanguageTypeUnknown && known_type != eLanguageTypeC) {
|
|
|
|
LanguageRuntime *runtime = GetLanguageRuntime(known_type);
|
|
|
|
return runtime ? runtime->CouldHaveDynamicValue(in_value) : false;
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
LanguageRuntime *cpp_runtime = GetLanguageRuntime(eLanguageTypeC_plus_plus);
|
|
|
|
if (cpp_runtime && cpp_runtime->CouldHaveDynamicValue(in_value))
|
|
|
|
return true;
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
LanguageRuntime *objc_runtime = GetLanguageRuntime(eLanguageTypeObjC);
|
|
|
|
return objc_runtime ? objc_runtime->CouldHaveDynamicValue(in_value) : false;
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::SetDynamicCheckers(DynamicCheckerFunctions *dynamic_checkers) {
|
|
|
|
m_dynamic_checkers_ap.reset(dynamic_checkers);
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
BreakpointSiteList &Process::GetBreakpointSiteList() {
|
|
|
|
return m_breakpoint_site_list;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const BreakpointSiteList &Process::GetBreakpointSiteList() const {
|
|
|
|
return m_breakpoint_site_list;
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::DisableAllBreakpointSites() {
|
|
|
|
m_breakpoint_site_list.ForEach([this](BreakpointSite *bp_site) -> void {
|
|
|
|
// bp_site->SetEnabled(true);
|
|
|
|
DisableBreakpointSite(bp_site);
|
|
|
|
});
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error Process::ClearBreakpointSiteByID(lldb::user_id_t break_id) {
|
|
|
|
Error error(DisableBreakpointSiteByID(break_id));
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (error.Success())
|
|
|
|
m_breakpoint_site_list.Remove(break_id);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
return error;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error Process::DisableBreakpointSiteByID(lldb::user_id_t break_id) {
|
|
|
|
Error 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.SetErrorStringWithFormat("invalid breakpoint site ID: %" PRIu64,
|
|
|
|
break_id);
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
return error;
|
|
|
|
}
|
2014-09-16 04:07:33 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error Process::EnableBreakpointSiteByID(lldb::user_id_t break_id) {
|
|
|
|
Error 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.SetErrorStringWithFormat("invalid breakpoint site ID: %" PRIu64,
|
|
|
|
break_id);
|
|
|
|
}
|
|
|
|
return error;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
lldb::break_id_t
|
|
|
|
Process::CreateBreakpointSite(const BreakpointLocationSP &owner,
|
|
|
|
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.
|
|
|
|
owner->SetIsIndirect(false);
|
|
|
|
|
|
|
|
if (owner->ShouldResolveIndirectFunctions()) {
|
|
|
|
Symbol *symbol = owner->GetAddress().CalculateSymbolContextSymbol();
|
|
|
|
if (symbol && symbol->IsIndirect()) {
|
|
|
|
Error error;
|
|
|
|
Address symbol_address = symbol->GetAddress();
|
|
|
|
load_addr = ResolveIndirectFunction(&symbol_address, error);
|
|
|
|
if (!error.Success() && show_error) {
|
|
|
|
GetTarget().GetDebugger().GetErrorFile()->Printf(
|
|
|
|
"warning: failed to resolve indirect function at 0x%" PRIx64
|
|
|
|
" for breakpoint %i.%i: %s\n",
|
|
|
|
symbol->GetLoadAddress(&GetTarget()),
|
|
|
|
owner->GetBreakpoint().GetID(), owner->GetID(),
|
|
|
|
error.AsCString() ? error.AsCString() : "unknown error");
|
|
|
|
return LLDB_INVALID_BREAK_ID;
|
|
|
|
}
|
|
|
|
Address resolved_address(load_addr);
|
|
|
|
load_addr = resolved_address.GetOpcodeLoadAddress(&GetTarget());
|
|
|
|
owner->SetIsIndirect(true);
|
|
|
|
} else
|
|
|
|
load_addr = owner->GetAddress().GetOpcodeLoadAddress(&GetTarget());
|
|
|
|
} else
|
|
|
|
load_addr = owner->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 owner,
|
|
|
|
// 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->AddOwner(owner);
|
|
|
|
owner->SetBreakpointSite(bp_site_sp);
|
|
|
|
return bp_site_sp->GetID();
|
|
|
|
} else {
|
|
|
|
bp_site_sp.reset(new BreakpointSite(&m_breakpoint_site_list, owner,
|
|
|
|
load_addr, use_hardware));
|
|
|
|
if (bp_site_sp) {
|
|
|
|
Error error = EnableBreakpointSite(bp_site_sp.get());
|
|
|
|
if (error.Success()) {
|
|
|
|
owner->SetBreakpointSite(bp_site_sp);
|
|
|
|
return m_breakpoint_site_list.Add(bp_site_sp);
|
|
|
|
} else {
|
|
|
|
if (show_error) {
|
|
|
|
// Report error for setting breakpoint...
|
|
|
|
GetTarget().GetDebugger().GetErrorFile()->Printf(
|
|
|
|
"warning: failed to set breakpoint site at 0x%" PRIx64
|
|
|
|
" for breakpoint %i.%i: %s\n",
|
|
|
|
load_addr, owner->GetBreakpoint().GetID(), owner->GetID(),
|
|
|
|
error.AsCString() ? error.AsCString() : "unknown error");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// We failed to enable the breakpoint
|
|
|
|
return LLDB_INVALID_BREAK_ID;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Process::RemoveOwnerFromBreakpointSite(lldb::user_id_t owner_id,
|
|
|
|
lldb::user_id_t owner_loc_id,
|
|
|
|
BreakpointSiteSP &bp_site_sp) {
|
|
|
|
uint32_t num_owners = bp_site_sp->RemoveOwner(owner_id, owner_loc_id);
|
|
|
|
if (num_owners == 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;
|
|
|
|
BreakpointSiteList 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, &bytes_removed](
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
Error Process::EnableSoftwareBreakpoint(BreakpointSite *bp_site) {
|
|
|
|
Error error;
|
|
|
|
assert(bp_site != nullptr);
|
|
|
|
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
|
|
|
|
const addr_t bp_addr = bp_site->GetLoadAddress();
|
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64,
|
|
|
|
bp_site->GetID(), (uint64_t)bp_addr);
|
|
|
|
if (bp_site->IsEnabled()) {
|
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64
|
|
|
|
" -- already enabled",
|
|
|
|
bp_site->GetID(), (uint64_t)bp_addr);
|
|
|
|
return error;
|
|
|
|
}
|
2014-09-16 04:07:33 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (bp_addr == LLDB_INVALID_ADDRESS) {
|
|
|
|
error.SetErrorString("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.SetErrorStringWithFormat("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.SetErrorString(
|
|
|
|
"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);
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::EnableSoftwareBreakpoint (site_id = %d) "
|
|
|
|
"addr = 0x%" PRIx64 " -- SUCCESS",
|
|
|
|
bp_site->GetID(), (uint64_t)bp_addr);
|
|
|
|
} else
|
|
|
|
error.SetErrorString(
|
|
|
|
"failed to verify the breakpoint trap in memory.");
|
|
|
|
} else
|
|
|
|
error.SetErrorString(
|
|
|
|
"Unable to read memory to verify breakpoint trap.");
|
|
|
|
} else
|
|
|
|
error.SetErrorString("Unable to write breakpoint trap to memory.");
|
|
|
|
} else
|
|
|
|
error.SetErrorString("Unable to read memory at breakpoint address.");
|
|
|
|
}
|
|
|
|
if (log && error.Fail())
|
|
|
|
log->Printf(
|
|
|
|
"Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64
|
|
|
|
" -- FAILED: %s",
|
|
|
|
bp_site->GetID(), (uint64_t)bp_addr, error.AsCString());
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
Error Process::DisableSoftwareBreakpoint(BreakpointSite *bp_site) {
|
|
|
|
Error error;
|
|
|
|
assert(bp_site != nullptr);
|
|
|
|
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
|
|
|
|
addr_t bp_addr = bp_site->GetLoadAddress();
|
|
|
|
lldb::user_id_t breakID = bp_site->GetID();
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::DisableSoftwareBreakpoint (breakID = %" PRIu64
|
|
|
|
") addr = 0x%" PRIx64,
|
|
|
|
breakID, (uint64_t)bp_addr);
|
|
|
|
|
|
|
|
if (bp_site->IsHardware()) {
|
|
|
|
error.SetErrorString("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.SetErrorString(
|
|
|
|
"Memory write failed when restoring original opcode.");
|
|
|
|
} else {
|
|
|
|
error.SetErrorString(
|
|
|
|
"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);
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::DisableSoftwareBreakpoint (site_id = %d) "
|
|
|
|
"addr = 0x%" PRIx64 " -- SUCCESS",
|
|
|
|
bp_site->GetID(), (uint64_t)bp_addr);
|
|
|
|
return error;
|
|
|
|
} else {
|
|
|
|
if (break_op_found)
|
|
|
|
error.SetErrorString("Failed to restore original opcode.");
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
error.SetErrorString("Failed to read memory to verify that "
|
|
|
|
"breakpoint trap was restored.");
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
error.SetErrorString(
|
|
|
|
"Unable to read memory that should contain the breakpoint trap.");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64
|
|
|
|
" -- already disabled",
|
|
|
|
bp_site->GetID(), (uint64_t)bp_addr);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64
|
|
|
|
" -- FAILED: %s",
|
|
|
|
bp_site->GetID(), (uint64_t)bp_addr, error.AsCString());
|
|
|
|
return error;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Uncomment to verify memory caching works after making changes to caching code
|
|
|
|
//#define VERIFY_MEMORY_READS
|
2015-06-02 07:14:09 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
size_t Process::ReadMemory(addr_t addr, void *buf, size_t size, Error &error) {
|
|
|
|
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);
|
|
|
|
Error 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;
|
2011-01-25 10:58:48 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
return 0;
|
|
|
|
#else // !defined(VERIFY_MEMORY_READS)
|
|
|
|
// Memory caching is enabled, without debug verification
|
2014-09-16 04:07:33 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t Process::ReadCStringFromMemory(addr_t addr, std::string &out_str,
|
|
|
|
Error &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;
|
2015-06-02 07:14:09 +08:00
|
|
|
else
|
2016-09-07 04:57:50 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return out_str.size();
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
size_t Process::ReadStringFromMemory(addr_t addr, char *dst, size_t max_bytes,
|
|
|
|
Error &error, size_t type_width) {
|
|
|
|
size_t total_bytes_read = 0;
|
|
|
|
if (dst && max_bytes && type_width && max_bytes >= type_width) {
|
|
|
|
// Ensure a null terminator independent of the number of bytes that is read.
|
|
|
|
memset(dst, 0, max_bytes);
|
|
|
|
size_t bytes_left = max_bytes - type_width;
|
2015-05-28 11:24:30 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const char terminator[4] = {'\0', '\0', '\0', '\0'};
|
|
|
|
assert(sizeof(terminator) >= type_width && "Attempting to validate a "
|
|
|
|
"string with more than 4 bytes "
|
|
|
|
"per character!");
|
2015-05-23 11:54:53 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
addr_t curr_addr = addr;
|
|
|
|
const size_t cache_line_size = m_memory_cache.GetMemoryCacheLineSize();
|
|
|
|
char *curr_dst = dst;
|
2015-05-23 11:54:53 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
error.Clear();
|
|
|
|
while (bytes_left > 0 && error.Success()) {
|
|
|
|
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);
|
|
|
|
size_t bytes_read = ReadMemory(curr_addr, curr_dst, bytes_to_read, error);
|
|
|
|
|
|
|
|
if (bytes_read == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Search for a null terminator of correct size and alignment in
|
|
|
|
// bytes_read
|
|
|
|
size_t aligned_start = total_bytes_read - total_bytes_read % type_width;
|
|
|
|
for (size_t i = aligned_start;
|
|
|
|
i + type_width <= total_bytes_read + bytes_read; i += type_width)
|
|
|
|
if (::memcmp(&dst[i], terminator, type_width) == 0) {
|
|
|
|
error.Clear();
|
|
|
|
return i;
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
total_bytes_read += bytes_read;
|
|
|
|
curr_dst += bytes_read;
|
|
|
|
curr_addr += bytes_read;
|
|
|
|
bytes_left -= bytes_read;
|
2015-11-06 07:03:44 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
} else {
|
|
|
|
if (max_bytes)
|
|
|
|
error.SetErrorString("invalid arguments");
|
|
|
|
}
|
|
|
|
return total_bytes_read;
|
2015-11-06 07:03:44 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// 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, Error &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);
|
|
|
|
Error error;
|
|
|
|
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);
|
|
|
|
size_t bytes_read = ReadMemory(curr_addr, curr_dst, bytes_to_read, error);
|
|
|
|
|
|
|
|
if (bytes_read == 0) {
|
|
|
|
result_error = 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.SetErrorString("invalid arguments");
|
|
|
|
else
|
|
|
|
result_error.Clear();
|
|
|
|
}
|
|
|
|
return total_cstr_len;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
size_t Process::ReadMemoryFromInferior(addr_t addr, void *buf, size_t size,
|
|
|
|
Error &error) {
|
|
|
|
if (buf == nullptr || size == 0)
|
|
|
|
return 0;
|
2011-08-22 10:49:39 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t Process::ReadUnsignedIntegerFromMemory(lldb::addr_t vm_addr,
|
|
|
|
size_t integer_byte_size,
|
|
|
|
uint64_t fail_value,
|
|
|
|
Error &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, Error &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, Error &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,
|
|
|
|
Error &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,
|
|
|
|
Error &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,
|
|
|
|
Error &error) {
|
|
|
|
#if defined(ENABLE_MEMORY_CACHING)
|
|
|
|
m_memory_cache.Flush(addr, size);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (buf == nullptr || size == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
BreakpointSiteList bp_sites_in_range;
|
|
|
|
|
|
|
|
if (m_breakpoint_site_list.FindInRange(addr, addr + size,
|
|
|
|
bp_sites_in_range)) {
|
|
|
|
// No breakpoint sites overlap
|
|
|
|
if (bp_sites_in_range.IsEmpty())
|
|
|
|
return WriteMemoryPrivate(addr, buf, size, error);
|
|
|
|
else {
|
|
|
|
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.Success()) {
|
|
|
|
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.SetErrorToGenericError();
|
2013-12-13 08:29:16 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
// 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;
|
2013-12-13 08:29:16 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
if (bytes_written < size)
|
|
|
|
WriteMemoryPrivate(addr + bytes_written, ubuf + bytes_written,
|
|
|
|
size - bytes_written, error);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return WriteMemoryPrivate(addr, buf, size, error);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write any remaining bytes after the last breakpoint if we have any left
|
|
|
|
return 0; // bytes_written;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t Process::WriteScalarToMemory(addr_t addr, const Scalar &scalar,
|
|
|
|
size_t byte_size, Error &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.SetErrorString("failed to get scalar as memory data");
|
|
|
|
} else {
|
|
|
|
error.SetErrorString("invalid scalar value");
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t Process::ReadScalarIntegerFromMemory(addr_t addr, uint32_t byte_size,
|
|
|
|
bool is_signed, Scalar &scalar,
|
|
|
|
Error &error) {
|
|
|
|
uint64_t uval = 0;
|
|
|
|
if (byte_size == 0) {
|
|
|
|
error.SetErrorString("byte size is zero");
|
|
|
|
} else if (byte_size & (byte_size - 1)) {
|
|
|
|
error.SetErrorStringWithFormat("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.SetErrorStringWithFormat(
|
|
|
|
"byte size of %u is too large for integer scalar type", byte_size);
|
|
|
|
}
|
|
|
|
return 0;
|
2013-12-13 08:29:16 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
#define USE_ALLOCATE_MEMORY_CACHE 1
|
|
|
|
addr_t Process::AllocateMemory(size_t size, uint32_t permissions,
|
|
|
|
Error &error) {
|
|
|
|
if (GetPrivateState() != eStateStopped)
|
|
|
|
return LLDB_INVALID_ADDRESS;
|
<rdar://problem/13010007>
Added the ability for OS plug-ins to lazily populate the thread this. The python OS plug-in classes can now implement the following method:
class OperatingSystemPlugin:
def create_thread(self, tid, context):
# Return a dictionary for a new thread to create it on demand
This will add a new thread to the thread list if it doesn't already exist. The example code in lldb/examples/python/operating_system.py has been updated to show how this call us used.
Cleaned up the code in PythonDataObjects.cpp/h:
- renamed all classes that started with PythonData* to be Python*.
- renamed PythonArray to PythonList. Cleaned up the code to use inheritance where
- Centralized the code that does ref counting in the PythonObject class to a single function.
- Made the "bool PythonObject::Reset(PyObject *)" function be virtual so each subclass can correctly check to ensure a PyObject is of the right type before adopting the object.
- Cleaned up all APIs and added new constructors for the Python* classes to they can all construct form:
- PyObject *
- const PythonObject &
- const lldb::ScriptInterpreterObjectSP &
Cleaned up code in ScriptInterpreterPython:
- Made calling python functions safer by templatizing the production of value formats. Python specifies the value formats based on built in C types (long, long long, etc), and code often uses typedefs for uint32_t, uint64_t, etc when passing arguments down to python. We will now always produce correct value formats as the templatized code will "do the right thing" all the time.
- Fixed issues with the ScriptInterpreterPython::Locker where entering the session and leaving the session had a bunch of issues that could cause the "lldb" module globals lldb.debugger, lldb.target, lldb.process, lldb.thread, and lldb.frame to not be initialized.
llvm-svn: 172873
2013-01-19 07:41:08 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
#if defined(USE_ALLOCATE_MEMORY_CACHE)
|
|
|
|
return m_allocated_memory_cache.AllocateMemory(size, permissions, error);
|
|
|
|
#else
|
|
|
|
addr_t allocated_addr = DoAllocateMemory(size, permissions, error);
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::AllocateMemory(size=%" PRIu64
|
|
|
|
", permissions=%s) => 0x%16.16" PRIx64
|
|
|
|
" (m_stop_id = %u m_memory_id = %u)",
|
|
|
|
(uint64_t)size, GetPermissionsAsCString(permissions),
|
|
|
|
(uint64_t)allocated_addr, m_mod_id.GetStopID(),
|
|
|
|
m_mod_id.GetMemoryID());
|
|
|
|
return allocated_addr;
|
|
|
|
#endif
|
2013-01-09 06:10:01 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
addr_t Process::CallocateMemory(size_t size, uint32_t permissions,
|
|
|
|
Error &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;
|
2013-01-09 06:10:01 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Process::CanJIT() {
|
|
|
|
if (m_can_jit == eCanJITDontKnow) {
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
Error err;
|
2013-01-09 06:10:01 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
uint64_t allocated_memory = AllocateMemory(
|
|
|
|
8, ePermissionsReadable | ePermissionsWritable | ePermissionsExecutable,
|
|
|
|
err);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (err.Success()) {
|
|
|
|
m_can_jit = eCanJITYes;
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s pid %" PRIu64
|
|
|
|
" allocation test passed, CanJIT () is true",
|
|
|
|
__FUNCTION__, GetID());
|
|
|
|
} else {
|
|
|
|
m_can_jit = eCanJITNo;
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s pid %" PRIu64
|
|
|
|
" allocation test failed, CanJIT () is false: %s",
|
|
|
|
__FUNCTION__, GetID(), err.AsCString());
|
2014-10-21 09:00:42 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
|
|
|
|
DeallocateMemory(allocated_memory);
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_can_jit == eCanJITYes;
|
2014-10-21 09:00:42 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::SetCanJIT(bool can_jit) {
|
|
|
|
m_can_jit = (can_jit ? eCanJITYes : eCanJITNo);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::SetCanRunCode(bool can_run_code) {
|
|
|
|
SetCanJIT(can_run_code);
|
|
|
|
m_can_interpret_function_calls = can_run_code;
|
2012-04-19 09:40:33 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error Process::DeallocateMemory(addr_t ptr) {
|
|
|
|
Error error;
|
|
|
|
#if defined(USE_ALLOCATE_MEMORY_CACHE)
|
|
|
|
if (!m_allocated_memory_cache.DeallocateMemory(ptr)) {
|
|
|
|
error.SetErrorStringWithFormat(
|
|
|
|
"deallocation of memory at 0x%" PRIx64 " failed.", (uint64_t)ptr);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
error = DoDeallocateMemory(ptr);
|
|
|
|
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::DeallocateMemory(addr=0x%16.16" PRIx64
|
|
|
|
") => err = %s (m_stop_id = %u, m_memory_id = %u)",
|
|
|
|
ptr, error.AsCString("SUCCESS"), m_mod_id.GetStopID(),
|
|
|
|
m_mod_id.GetMemoryID());
|
|
|
|
#endif
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ModuleSP Process::ReadModuleFromMemory(const FileSpec &file_spec,
|
|
|
|
lldb::addr_t header_addr,
|
|
|
|
size_t size_to_read) {
|
|
|
|
Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
|
|
|
|
if (log) {
|
|
|
|
log->Printf("Process::ReadModuleFromMemory reading %s binary from memory",
|
|
|
|
file_spec.GetPath().c_str());
|
|
|
|
}
|
|
|
|
ModuleSP module_sp(new Module(file_spec, ArchSpec()));
|
|
|
|
if (module_sp) {
|
|
|
|
Error error;
|
|
|
|
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;
|
|
|
|
Error 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;
|
|
|
|
}
|
2014-10-21 09:00:42 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (range_info.GetReadable() == MemoryRegionInfo::eYes)
|
|
|
|
permissions |= lldb::ePermissionsReadable;
|
2014-10-21 09:00:42 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (range_info.GetWritable() == MemoryRegionInfo::eYes)
|
|
|
|
permissions |= lldb::ePermissionsWritable;
|
2014-10-21 09:00:42 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (range_info.GetExecutable() == MemoryRegionInfo::eYes)
|
|
|
|
permissions |= lldb::ePermissionsExecutable;
|
2014-10-21 09:00:42 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
return true;
|
2014-10-21 09:00:42 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error Process::EnableWatchpoint(Watchpoint *watchpoint, bool notify) {
|
|
|
|
Error error;
|
|
|
|
error.SetErrorString("watchpoints are not supported");
|
|
|
|
return error;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error Process::DisableWatchpoint(Watchpoint *watchpoint, bool notify) {
|
|
|
|
Error error;
|
|
|
|
error.SetErrorString("watchpoints are not supported");
|
|
|
|
return error;
|
|
|
|
}
|
2014-07-15 07:09:29 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
StateType
|
|
|
|
Process::WaitForProcessStopPrivate(const std::chrono::microseconds &timeout,
|
|
|
|
EventSP &event_sp) {
|
|
|
|
StateType state;
|
|
|
|
// Now wait for the process to launch and return control to us, and then
|
|
|
|
// call DidLaunch:
|
|
|
|
while (true) {
|
|
|
|
event_sp.reset();
|
|
|
|
state = WaitForStateChangedEventsPrivate(timeout, event_sp);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (StateIsStoppedState(state, false))
|
|
|
|
break;
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// If state is invalid, then we timed out
|
|
|
|
if (state == eStateInvalid)
|
|
|
|
break;
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (event_sp)
|
|
|
|
HandlePrivateEvent(event_sp);
|
|
|
|
}
|
|
|
|
return state;
|
|
|
|
}
|
2013-07-03 00:35:47 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::LoadOperatingSystemPlugin(bool flush) {
|
|
|
|
if (flush)
|
|
|
|
m_thread_list.Clear();
|
|
|
|
m_os_ap.reset(OperatingSystem::FindPlugin(this, nullptr));
|
|
|
|
if (flush)
|
|
|
|
Flush();
|
|
|
|
}
|
2013-05-29 07:04:25 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error Process::Launch(ProcessLaunchInfo &launch_info) {
|
|
|
|
Error error;
|
|
|
|
m_abi_sp.reset();
|
|
|
|
m_dyld_ap.reset();
|
|
|
|
m_jit_loaders_ap.reset();
|
|
|
|
m_system_runtime_ap.reset();
|
|
|
|
m_os_ap.reset();
|
|
|
|
m_process_input_reader.reset();
|
|
|
|
m_stop_info_override_callback = nullptr;
|
|
|
|
|
|
|
|
Module *exe_module = GetTarget().GetExecutableModulePointer();
|
|
|
|
if (exe_module) {
|
|
|
|
char local_exec_file_path[PATH_MAX];
|
|
|
|
char platform_exec_file_path[PATH_MAX];
|
|
|
|
exe_module->GetFileSpec().GetPath(local_exec_file_path,
|
|
|
|
sizeof(local_exec_file_path));
|
|
|
|
exe_module->GetPlatformFileSpec().GetPath(platform_exec_file_path,
|
|
|
|
sizeof(platform_exec_file_path));
|
|
|
|
if (exe_module->GetFileSpec().Exists()) {
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
if (PrivateStateThreadIsValid())
|
|
|
|
PausePrivateStateThread();
|
|
|
|
|
|
|
|
error = WillLaunch(exe_module);
|
|
|
|
if (error.Success()) {
|
|
|
|
const bool restarted = false;
|
|
|
|
SetPublicState(eStateLaunching, restarted);
|
|
|
|
m_should_detach = false;
|
|
|
|
|
|
|
|
if (m_public_run_lock.TrySetRunning()) {
|
|
|
|
// Now launch using these arguments.
|
|
|
|
error = DoLaunch(exe_module, launch_info);
|
|
|
|
} else {
|
|
|
|
// This shouldn't happen
|
|
|
|
error.SetErrorString("failed to acquire process run lock");
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2015-05-20 18:15:47 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
EventSP event_sp;
|
|
|
|
StateType state =
|
|
|
|
WaitForProcessStopPrivate(std::chrono::seconds(10), event_sp);
|
|
|
|
|
|
|
|
if (state == eStateInvalid || !event_sp) {
|
|
|
|
// We were able to launch the process, but we failed to
|
|
|
|
// catch the initial stop.
|
|
|
|
error.SetErrorString("failed to catch stop after launch");
|
|
|
|
SetExitStatus(0, "failed to catch stop after launch");
|
|
|
|
Destroy(false);
|
|
|
|
} else if (state == eStateStopped || state == eStateCrashed) {
|
|
|
|
DidLaunch();
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
DynamicLoader *dyld = GetDynamicLoader();
|
|
|
|
if (dyld)
|
|
|
|
dyld->DidLaunch();
|
2011-11-08 11:00:11 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
GetJITLoaders().DidLaunch();
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
SystemRuntime *system_runtime = GetSystemRuntime();
|
|
|
|
if (system_runtime)
|
|
|
|
system_runtime->DidLaunch();
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
LoadOperatingSystemPlugin(false);
|
|
|
|
|
|
|
|
// 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, false);
|
|
|
|
|
|
|
|
if (PrivateStateThreadIsValid())
|
|
|
|
ResumePrivateStateThread();
|
|
|
|
else
|
|
|
|
StartPrivateStateThread();
|
|
|
|
|
|
|
|
m_stop_info_override_callback =
|
|
|
|
GetTarget().GetArchitecture().GetStopInfoOverrideCallback();
|
|
|
|
|
|
|
|
// Target was stopped at entry as was intended. Need to notify the
|
|
|
|
// listeners
|
|
|
|
// about it.
|
|
|
|
if (state == eStateStopped &&
|
|
|
|
launch_info.GetFlags().Test(eLaunchFlagStopAtEntry))
|
|
|
|
HandlePrivateEvent(event_sp);
|
|
|
|
} else 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
error.SetErrorStringWithFormat("file doesn't exist: '%s'",
|
|
|
|
local_exec_file_path);
|
2010-09-23 10:01:19 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
return error;
|
2010-09-23 10:01:19 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error Process::LoadCore() {
|
|
|
|
Error error = DoLoadCore();
|
|
|
|
if (error.Success()) {
|
|
|
|
ListenerSP listener_sp(
|
|
|
|
Listener::MakeListener("lldb.process.load_core_listener"));
|
|
|
|
HijackProcessEvents(listener_sp);
|
2010-09-23 10:01:19 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (PrivateStateThreadIsValid())
|
|
|
|
ResumePrivateStateThread();
|
|
|
|
else
|
|
|
|
StartPrivateStateThread();
|
2010-09-23 10:01:19 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
DynamicLoader *dyld = GetDynamicLoader();
|
|
|
|
if (dyld)
|
|
|
|
dyld->DidAttach();
|
|
|
|
|
|
|
|
GetJITLoaders().DidAttach();
|
2015-04-03 02:44:58 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
SystemRuntime *system_runtime = GetSystemRuntime();
|
|
|
|
if (system_runtime)
|
|
|
|
system_runtime->DidAttach();
|
2012-05-22 00:51:35 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
m_os_ap.reset(OperatingSystem::FindPlugin(this, nullptr));
|
|
|
|
// 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 indefinitely for a stopped event since we just posted one above...
|
|
|
|
lldb::EventSP event_sp;
|
|
|
|
listener_sp->WaitForEvent(std::chrono::microseconds(0), event_sp);
|
|
|
|
StateType state = ProcessEventData::GetStateFromEvent(event_sp.get());
|
|
|
|
|
|
|
|
if (!StateIsStoppedState(state, false)) {
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::Halt() failed to stop, state is: %s",
|
|
|
|
StateAsCString(state));
|
|
|
|
error.SetErrorString(
|
|
|
|
"Did not get stopped event after loading the core file.");
|
2012-05-22 00:51:35 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
RestoreProcessEvents();
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
2012-05-22 00:51:35 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
DynamicLoader *Process::GetDynamicLoader() {
|
|
|
|
if (!m_dyld_ap)
|
|
|
|
m_dyld_ap.reset(DynamicLoader::FindPlugin(this, nullptr));
|
|
|
|
return m_dyld_ap.get();
|
2012-05-22 00:51:35 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const lldb::DataBufferSP Process::GetAuxvData() { return DataBufferSP(); }
|
|
|
|
|
|
|
|
JITLoaderList &Process::GetJITLoaders() {
|
|
|
|
if (!m_jit_loaders_ap) {
|
|
|
|
m_jit_loaders_ap.reset(new JITLoaderList());
|
|
|
|
JITLoader::LoadPlugins(this, *m_jit_loaders_ap);
|
|
|
|
}
|
|
|
|
return *m_jit_loaders_ap;
|
2015-03-04 05:51:25 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
SystemRuntime *Process::GetSystemRuntime() {
|
|
|
|
if (!m_system_runtime_ap)
|
|
|
|
m_system_runtime_ap.reset(SystemRuntime::FindPlugin(this));
|
|
|
|
return m_system_runtime_ap.get();
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Process::AttachCompletionHandler::AttachCompletionHandler(Process *process,
|
|
|
|
uint32_t exec_count)
|
|
|
|
: NextEventAction(process), m_exec_count(exec_count) {
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"Process::AttachCompletionHandler::%s process=%p, exec_count=%" PRIu32,
|
|
|
|
__FUNCTION__, static_cast<void *>(process), exec_count);
|
2014-08-28 06:58:26 +08:00
|
|
|
}
|
2012-02-09 14:16:32 +08:00
|
|
|
|
2011-01-29 09:49:25 +08:00
|
|
|
Process::NextEventAction::EventActionResult
|
2016-09-07 04:57:50 +08:00
|
|
|
Process::AttachCompletionHandler::PerformAction(lldb::EventSP &event_sp) {
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
2014-08-28 06:58:26 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
StateType state = ProcessEventData::GetStateFromEvent(event_sp.get());
|
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"Process::AttachCompletionHandler::%s called with state %s (%d)",
|
|
|
|
__FUNCTION__, StateAsCString(state), static_cast<int>(state));
|
2014-08-28 06:58:26 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
switch (state) {
|
|
|
|
case eStateAttaching:
|
|
|
|
return eEventActionSuccess;
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
case eStateRunning:
|
|
|
|
case eStateConnected:
|
|
|
|
return eEventActionRetry;
|
2014-11-18 03:39:20 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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);
|
2011-11-17 12:46:02 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (m_exec_count > 0) {
|
|
|
|
--m_exec_count;
|
2012-10-13 00:10:12 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (log)
|
|
|
|
log->Printf("Process::AttachCompletionHandler::%s state %s: reduced "
|
|
|
|
"remaining exec count to %" PRIu32 ", requesting resume",
|
|
|
|
__FUNCTION__, StateAsCString(state), m_exec_count);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
RequestResume();
|
|
|
|
return eEventActionRetry;
|
|
|
|
} else {
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::AttachCompletionHandler::%s state %s: no more "
|
|
|
|
"execs expected to start, continuing with attach",
|
|
|
|
__FUNCTION__, StateAsCString(state));
|
2014-11-20 02:27:45 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
m_process->CompleteAttach();
|
|
|
|
return eEventActionSuccess;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
break;
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
default:
|
|
|
|
case eStateExited:
|
|
|
|
case eStateInvalid:
|
|
|
|
break;
|
|
|
|
}
|
2011-02-16 12:46:07 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
Error Process::Attach(ProcessAttachInfo &attach_info) {
|
|
|
|
m_abi_sp.reset();
|
|
|
|
m_process_input_reader.reset();
|
|
|
|
m_dyld_ap.reset();
|
|
|
|
m_jit_loaders_ap.reset();
|
|
|
|
m_system_runtime_ap.reset();
|
|
|
|
m_os_ap.reset();
|
|
|
|
m_stop_info_override_callback = nullptr;
|
|
|
|
|
|
|
|
lldb::pid_t attach_pid = attach_info.GetProcessID();
|
|
|
|
Error 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()) {
|
|
|
|
if (m_public_run_lock.TrySetRunning()) {
|
|
|
|
m_should_detach = true;
|
|
|
|
const bool restarted = false;
|
|
|
|
SetPublicState(eStateAttaching, restarted);
|
|
|
|
// Now attach using these arguments.
|
|
|
|
error = DoAttachToProcessWithName(process_name, attach_info);
|
|
|
|
} else {
|
|
|
|
// This shouldn't happen
|
|
|
|
error.SetErrorString("failed to acquire process run lock");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error.Fail()) {
|
|
|
|
if (GetID() != LLDB_INVALID_PROCESS_ID) {
|
|
|
|
SetID(LLDB_INVALID_PROCESS_ID);
|
|
|
|
if (error.AsCString() == nullptr)
|
|
|
|
error.SetErrorString("attach failed");
|
|
|
|
|
|
|
|
SetExitStatus(-1, error.AsCString());
|
2012-05-08 09:45:38 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
} else {
|
|
|
|
SetNextEventAction(new Process::AttachCompletionHandler(
|
|
|
|
this, attach_info.GetResumeCount()));
|
|
|
|
StartPrivateStateThread();
|
|
|
|
}
|
|
|
|
return error;
|
2012-05-08 09:45:38 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
} else {
|
|
|
|
ProcessInstanceInfoList process_infos;
|
|
|
|
PlatformSP platform_sp(GetTarget().GetPlatform());
|
|
|
|
|
|
|
|
if (platform_sp) {
|
|
|
|
ProcessInstanceInfoMatch match_info;
|
|
|
|
match_info.GetProcessInfo() = attach_info;
|
|
|
|
match_info.SetNameMatchType(eNameMatchEquals);
|
|
|
|
platform_sp->FindProcesses(match_info, process_infos);
|
|
|
|
const uint32_t num_matches = process_infos.GetSize();
|
|
|
|
if (num_matches == 1) {
|
|
|
|
attach_pid = process_infos.GetProcessIDAtIndex(0);
|
|
|
|
// 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, platform_sp.get(), true,
|
|
|
|
false);
|
|
|
|
for (size_t i = 0; i < num_matches; i++) {
|
|
|
|
process_infos.GetProcessInfoAtIndex(i).DumpAsTableRow(
|
|
|
|
s, platform_sp.get(), true, false);
|
|
|
|
}
|
|
|
|
error.SetErrorStringWithFormat(
|
|
|
|
"more than one process named %s:\n%s", process_name,
|
|
|
|
s.GetData());
|
|
|
|
} else
|
|
|
|
error.SetErrorStringWithFormat(
|
|
|
|
"could not find a process named %s", process_name);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
error.SetErrorString(
|
|
|
|
"invalid platform, can't find processes by name");
|
|
|
|
return error;
|
2012-05-08 09:45:38 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
error.SetErrorString("invalid process name");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attach_pid != LLDB_INVALID_PROCESS_ID) {
|
|
|
|
error = WillAttachToProcessWithID(attach_pid);
|
|
|
|
if (error.Success()) {
|
|
|
|
|
|
|
|
if (m_public_run_lock.TrySetRunning()) {
|
|
|
|
// Now attach using these arguments.
|
|
|
|
m_should_detach = true;
|
|
|
|
const bool restarted = false;
|
|
|
|
SetPublicState(eStateAttaching, restarted);
|
|
|
|
error = DoAttachToProcessWithID(attach_pid, attach_info);
|
|
|
|
} else {
|
|
|
|
// This shouldn't happen
|
|
|
|
error.SetErrorString("failed to acquire process run lock");
|
|
|
|
}
|
|
|
|
|
|
|
|
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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS |
|
|
|
|
LIBLLDB_LOG_TARGET));
|
|
|
|
if (log)
|
|
|
|
log->Printf("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()) {
|
|
|
|
GetTarget().SetArchitecture(process_arch);
|
|
|
|
if (log) {
|
|
|
|
const char *triple_str = process_arch.GetTriple().getTriple().c_str();
|
|
|
|
log->Printf("Process::%s replacing process architecture with DidAttach() "
|
|
|
|
"architecture: %s",
|
|
|
|
__FUNCTION__, triple_str ? triple_str : "<null>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
if (platform_sp) {
|
|
|
|
const ArchSpec &target_arch = GetTarget().GetArchitecture();
|
|
|
|
if (target_arch.IsValid() &&
|
|
|
|
!platform_sp->IsCompatibleArchitecture(target_arch, false, nullptr)) {
|
|
|
|
ArchSpec platform_arch;
|
|
|
|
platform_sp =
|
|
|
|
platform_sp->GetPlatformForArchitecture(target_arch, &platform_arch);
|
|
|
|
if (platform_sp) {
|
|
|
|
GetTarget().SetPlatform(platform_sp);
|
|
|
|
GetTarget().SetArchitecture(platform_arch);
|
2014-08-28 06:58:26 +08:00
|
|
|
if (log)
|
2016-09-07 04:57:50 +08:00
|
|
|
log->Printf("Process::%s switching platform to %s and architecture "
|
|
|
|
"to %s based on info from attach",
|
|
|
|
__FUNCTION__, platform_sp->GetName().AsCString(""),
|
|
|
|
platform_arch.GetTriple().getTriple().c_str());
|
|
|
|
}
|
|
|
|
} else if (!process_arch.IsValid()) {
|
|
|
|
ProcessInstanceInfo process_info;
|
|
|
|
GetProcessInfo(process_info);
|
|
|
|
const ArchSpec &process_arch = process_info.GetArchitecture();
|
|
|
|
if (process_arch.IsValid() &&
|
|
|
|
!GetTarget().GetArchitecture().IsExactMatch(process_arch)) {
|
|
|
|
GetTarget().SetArchitecture(process_arch);
|
2014-08-28 06:58:26 +08:00
|
|
|
if (log)
|
2016-09-07 04:57:50 +08:00
|
|
|
log->Printf("Process::%s switching architecture to %s based on info "
|
|
|
|
"the platform retrieved for pid %" PRIu64,
|
|
|
|
__FUNCTION__,
|
|
|
|
process_arch.GetTriple().getTriple().c_str(), GetID());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
log->Printf("Process::%s after DynamicLoader::DidAttach(), target "
|
|
|
|
"executable is %s (using %s plugin)",
|
|
|
|
__FUNCTION__,
|
|
|
|
exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str()
|
|
|
|
: "<none>",
|
|
|
|
dyld->GetPluginName().AsCString("<unnamed>"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GetJITLoaders().DidAttach();
|
|
|
|
|
|
|
|
SystemRuntime *system_runtime = GetSystemRuntime();
|
|
|
|
if (system_runtime) {
|
|
|
|
system_runtime->DidAttach();
|
|
|
|
if (log) {
|
|
|
|
ModuleSP exe_module_sp = GetTarget().GetExecutableModule();
|
|
|
|
log->Printf("Process::%s after SystemRuntime::DidAttach(), target "
|
|
|
|
"executable is %s (using %s plugin)",
|
|
|
|
__FUNCTION__,
|
|
|
|
exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str()
|
|
|
|
: "<none>",
|
|
|
|
system_runtime->GetPluginName().AsCString("<unnamed>"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_os_ap.reset(OperatingSystem::FindPlugin(this, nullptr));
|
|
|
|
// Figure out which one is the executable, and set that in our target:
|
|
|
|
const ModuleList &target_modules = GetTarget().GetImages();
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex());
|
|
|
|
size_t num_modules = target_modules.GetSize();
|
|
|
|
ModuleSP new_executable_module_sp;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < num_modules; i++) {
|
|
|
|
ModuleSP module_sp(target_modules.GetModuleAtIndexUnlocked(i));
|
|
|
|
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, false);
|
|
|
|
if (log) {
|
|
|
|
ModuleSP exe_module_sp = GetTarget().GetExecutableModule();
|
|
|
|
log->Printf(
|
|
|
|
"Process::%s after looping through modules, target executable is %s",
|
|
|
|
__FUNCTION__,
|
|
|
|
exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str()
|
|
|
|
: "<none>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_stop_info_override_callback = process_arch.GetStopInfoOverrideCallback();
|
|
|
|
}
|
|
|
|
|
|
|
|
Error Process::ConnectRemote(Stream *strm, const char *remote_url) {
|
|
|
|
m_abi_sp.reset();
|
|
|
|
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.
|
|
|
|
|
|
|
|
Error error(DoConnectRemote(strm, remote_url));
|
|
|
|
if (error.Success()) {
|
|
|
|
if (GetID() != LLDB_INVALID_PROCESS_ID) {
|
|
|
|
EventSP event_sp;
|
|
|
|
StateType state =
|
|
|
|
WaitForProcessStopPrivate(std::chrono::microseconds(0), event_sp);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2014-08-28 06:58:26 +08:00
|
|
|
}
|
2013-11-05 11:57:19 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (PrivateStateThreadIsValid())
|
|
|
|
ResumePrivateStateThread();
|
|
|
|
else
|
|
|
|
StartPrivateStateThread();
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
Error Process::PrivateResume() {
|
|
|
|
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS |
|
|
|
|
LIBLLDB_LOG_STEP));
|
|
|
|
if (log)
|
|
|
|
log->Printf("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()));
|
|
|
|
|
|
|
|
Error 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.
|
|
|
|
if (m_thread_list.WillResume()) {
|
|
|
|
// Last thing, do the PreResumeActions.
|
|
|
|
if (!RunPreResumeActions()) {
|
|
|
|
error.SetErrorStringWithFormat(
|
|
|
|
"Process::PrivateResume PreResumeActions failed, not resuming.");
|
|
|
|
} else {
|
|
|
|
m_mod_id.BumpResumeID();
|
|
|
|
error = DoResume();
|
|
|
|
if (error.Success()) {
|
|
|
|
DidResume();
|
|
|
|
m_thread_list.DidResume();
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process thinks the process has resumed.");
|
2014-08-28 06:58:26 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
} 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.
|
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"Process::PrivateResume() asked to simulate a start & stop.");
|
|
|
|
|
|
|
|
SetPrivateState(eStateRunning);
|
|
|
|
SetPrivateState(eStateStopped);
|
|
|
|
}
|
|
|
|
} else if (log)
|
|
|
|
log->Printf("Process::PrivateResume() got an error \"%s\".",
|
|
|
|
error.AsCString("<unknown error>"));
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
Error Process::Halt(bool clear_thread_plans, bool use_run_lock) {
|
|
|
|
if (!StateIsRunningState(m_public_state.GetValue()))
|
|
|
|
return Error("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();
|
|
|
|
SetExitStatus(SIGKILL, "Cancelled async attach.");
|
|
|
|
Destroy(false);
|
2016-11-11 12:29:25 +08:00
|
|
|
return Error();
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2011-02-16 12:46:07 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Wait for 10 second for the process to stop.
|
|
|
|
StateType state =
|
|
|
|
WaitForProcessToStop(std::chrono::seconds(10), &event_sp, true,
|
|
|
|
halt_listener_sp, nullptr, use_run_lock);
|
|
|
|
RestoreProcessEvents();
|
Many improvements to the Platform base class and subclasses. The base Platform
class now implements the Host functionality for a lot of things that make
sense by default so that subclasses can check:
int
PlatformSubclass::Foo ()
{
if (IsHost())
return Platform::Foo (); // Let the platform base class do the host specific stuff
// Platform subclass specific code...
int result = ...
return result;
}
Added new functions to the platform:
virtual const char *Platform::GetUserName (uint32_t uid);
virtual const char *Platform::GetGroupName (uint32_t gid);
The user and group names are cached locally so that remote platforms can avoid
sending packets multiple times to resolve this information.
Added the parent process ID to the ProcessInfo class.
Added a new ProcessInfoMatch class which helps us to match processes up
and changed the Host layer over to using this new class. The new class allows
us to search for processs:
1 - by name (equal to, starts with, ends with, contains, and regex)
2 - by pid
3 - And further check for parent pid == value, uid == value, gid == value,
euid == value, egid == value, arch == value, parent == value.
This is all hookup up to the "platform process list" command which required
adding dumping routines to dump process information. If the Host class
implements the process lookup routines, you can now lists processes on
your local machine:
machine1.foo.com % lldb
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
94727 244 username usergroup username usergroup x86_64-apple-darwin Xcode
92742 92710 username usergroup username usergroup i386-apple-darwin debugserver
This of course also works remotely with the lldb-platform:
machine1.foo.com % lldb-platform --listen 1234
machine2.foo.com % lldb
(lldb) platform create remote-macosx
Platform: remote-macosx
Connected: no
(lldb) platform connect connect://localhost:1444
Platform: remote-macosx
Triple: x86_64-apple-darwin
OS Version: 10.6.7 (10J869)
Kernel: Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386
Hostname: machine1.foo.com
Connected: yes
(lldb) platform process list
PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE NAME
====== ====== ========== ========== ========== ========== ======================== ============================
99556 244 username usergroup username usergroup x86_64-apple-darwin trustevaluation
99548 65539 username usergroup username usergroup x86_64-apple-darwin lldb
99538 1 username usergroup username usergroup x86_64-apple-darwin FileMerge
94943 1 username usergroup username usergroup x86_64-apple-darwin mdworker
94852 244 username usergroup username usergroup x86_64-apple-darwin Safari
The lldb-platform implements everything with the Host:: layer, so this should
"just work" for linux. I will probably be adding more stuff to the Host layer
for launching processes and attaching to processes so that this support should
eventually just work as well.
Modified the target to be able to be created with an architecture that differs
from the main executable. This is needed for iOS debugging since we can have
an "armv6" binary which can run on an "armv7" machine, so we want to be able
to do:
% lldb
(lldb) platform create remote-ios
(lldb) file --arch armv7 a.out
Where "a.out" is an armv6 executable. The platform then can correctly decide
to open all "armv7" images for all dependent shared libraries.
Modified the disassembly to show the current PC value. Example output:
(lldb) disassemble --frame
a.out`main:
0x1eb7: pushl %ebp
0x1eb8: movl %esp, %ebp
0x1eba: pushl %ebx
0x1ebb: subl $20, %esp
0x1ebe: calll 0x1ec3 ; main + 12 at test.c:18
0x1ec3: popl %ebx
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
0x1edb: leal 213(%ebx), %eax
0x1ee1: movl %eax, (%esp)
0x1ee4: calll 0x1f1e ; puts
0x1ee9: calll 0x1f0c ; getchar
0x1eee: movl $20, (%esp)
0x1ef5: calll 0x1e6a ; sleep_loop at test.c:6
0x1efa: movl $12, %eax
0x1eff: addl $20, %esp
0x1f02: popl %ebx
0x1f03: leave
0x1f04: ret
This can be handy when dealing with the new --line options that was recently
added:
(lldb) disassemble --line
a.out`main + 13 at test.c:19
18 {
-> 19 printf("Process: %i\n\n", getpid());
20 puts("Press any key to continue..."); getchar();
-> 0x1ec4: calll 0x1f12 ; getpid
0x1ec9: movl %eax, 4(%esp)
0x1ecd: leal 199(%ebx), %eax
0x1ed3: movl %eax, (%esp)
0x1ed6: calll 0x1f18 ; printf
Modified the ModuleList to have a lookup based solely on a UUID. Since the
UUID is typically the MD5 checksum of a binary image, there is no need
to give the path and architecture when searching for a pre-existing
image in an image list.
Now that we support remote debugging a bit better, our lldb_private::Module
needs to be able to track what the original path for file was as the platform
knows it, as well as where the file is locally. The module has the two
following functions to retrieve both paths:
const FileSpec &Module::GetFileSpec () const;
const FileSpec &Module::GetPlatformFileSpec () const;
llvm-svn: 128563
2011-03-31 02:16:51 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (state == eStateInvalid || !event_sp) {
|
|
|
|
// We timed out and didn't get a stop event...
|
|
|
|
return Error("Halt timed out. State = %s", StateAsCString(GetState()));
|
|
|
|
}
|
2011-02-04 09:58:07 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
BroadcastEvent(event_sp);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-11-11 12:29:25 +08:00
|
|
|
return Error();
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error Process::StopForDestroyOrDetach(lldb::EventSP &exit_event_sp) {
|
|
|
|
Error error;
|
2015-12-07 20:36:52 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// 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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s() About to stop.", __FUNCTION__);
|
|
|
|
|
|
|
|
ListenerSP listener_sp(
|
|
|
|
Listener::MakeListener("lldb.Process.StopForDestroyOrDetach.hijack"));
|
|
|
|
HijackProcessEvents(listener_sp);
|
2011-01-29 09:49:25 +08:00
|
|
|
|
2015-12-07 20:36:52 +08:00
|
|
|
SendAsyncInterrupt();
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Consume the interrupt event.
|
|
|
|
StateType state = WaitForProcessToStop(std::chrono::seconds(10),
|
|
|
|
&exit_event_sp, true, listener_sp);
|
Fix race during process interruption
Summary:
The following situation was occuring in TestAttachResume:
- we did a "continue" from a breakpoint (which involves a private start-stop to step over the
breakpoint)
- after receiving the stop-reply from the step-over, we issue a "detach" (which requires a
process interrupt)
- at this moment, the public state is "running", private state is "about-to-be-stopped" (the
stopped event was broadcast, but it was not received yet)
- StopForDestroyOrDetach (public thread) notes the public state is running, sends an interrupt
request to the private thread
- private thread gets the eBroadcastBitInterrupt (before the eStateStopped message), and asks the
process plugin to stop (via Halt())
- process plugin says it has nothing to do as the process is already stopped
- private thread shrugs and carries on. receives the stop event, restores the breakpoint and
resumes the process.
- after a while, the public thread times out and says it failed to stop the process
This patch does the following:
- splits Halt() into two functions, private and public, their usage depends on the context
- public Halt(): sends eBroadcastBitInterrupt to the private thread and waits for the Stop
event
- HaltPrivate(): asks the plugin to stop and makes a note that the halt was requested. When the
next stop event comes it sets the interrupt flag on it.
- removes HijackPrivateProcessEvents(), as the only user (old Halt()) has gone away
- removes the m_currently_handling_event hack, as the new Halt() does not need it
- adds a use_run_lock parameter to public Halt() and WaitForProcessToStop(). This was needed
because RunThreadPlan uses Halt() while holding the run lock and we don't want Halt() to take
it away from him.
Reviewers: clayborg, jingham
Subscribers: lldb-commits
Differential Revision: http://reviews.llvm.org/D14989
llvm-svn: 254403
2015-12-01 19:28:47 +08:00
|
|
|
|
2015-12-07 20:36:52 +08:00
|
|
|
RestoreProcessEvents();
|
Fix race during process interruption
Summary:
The following situation was occuring in TestAttachResume:
- we did a "continue" from a breakpoint (which involves a private start-stop to step over the
breakpoint)
- after receiving the stop-reply from the step-over, we issue a "detach" (which requires a
process interrupt)
- at this moment, the public state is "running", private state is "about-to-be-stopped" (the
stopped event was broadcast, but it was not received yet)
- StopForDestroyOrDetach (public thread) notes the public state is running, sends an interrupt
request to the private thread
- private thread gets the eBroadcastBitInterrupt (before the eStateStopped message), and asks the
process plugin to stop (via Halt())
- process plugin says it has nothing to do as the process is already stopped
- private thread shrugs and carries on. receives the stop event, restores the breakpoint and
resumes the process.
- after a while, the public thread times out and says it failed to stop the process
This patch does the following:
- splits Halt() into two functions, private and public, their usage depends on the context
- public Halt(): sends eBroadcastBitInterrupt to the private thread and waits for the Stop
event
- HaltPrivate(): asks the plugin to stop and makes a note that the halt was requested. When the
next stop event comes it sets the interrupt flag on it.
- removes HijackPrivateProcessEvents(), as the only user (old Halt()) has gone away
- removes the m_currently_handling_event hack, as the new Halt() does not need it
- adds a use_run_lock parameter to public Halt() and WaitForProcessToStop(). This was needed
because RunThreadPlan uses Halt() while holding the run lock and we don't want Halt() to take
it away from him.
Reviewers: clayborg, jingham
Subscribers: lldb-commits
Differential Revision: http://reviews.llvm.org/D14989
llvm-svn: 254403
2015-12-01 19:28:47 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// 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) {
|
|
|
|
if (log)
|
|
|
|
log->Printf("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) {
|
|
|
|
if (log)
|
|
|
|
log->Printf("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 Error("Attempt to stop the target in order to detach timed out. "
|
|
|
|
"State = %s",
|
|
|
|
StateAsCString(GetState()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
Error Process::Detach(bool keep_stopped) {
|
|
|
|
EventSP exit_event_sp;
|
|
|
|
Error 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;
|
|
|
|
}
|
2015-12-07 20:36:52 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
Error Process::Destroy(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;
|
|
|
|
|
|
|
|
Error error(WillDestroy());
|
|
|
|
if (error.Success()) {
|
|
|
|
EventSP exit_event_sp;
|
|
|
|
if (DestroyRequiresHalt()) {
|
|
|
|
error = StopForDestroyOrDetach(exit_event_sp);
|
2013-03-29 09:18:12 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (m_public_state.GetValue() != eStateRunning) {
|
|
|
|
// 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();
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
error = DoDestroy();
|
|
|
|
if (error.Success()) {
|
|
|
|
DidDestroy();
|
|
|
|
StopPrivateStateThread();
|
|
|
|
}
|
|
|
|
m_stdio_communication.Disconnect();
|
|
|
|
m_stdio_communication.StopReadThread();
|
|
|
|
m_stdin_forward = false;
|
2014-03-26 03:59:36 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (m_process_input_reader) {
|
|
|
|
m_process_input_reader->SetIsDone(true);
|
|
|
|
m_process_input_reader->Cancel();
|
|
|
|
m_process_input_reader.reset();
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
|
2013-03-29 09:18:12 +08:00
|
|
|
// If we exited when we were waiting for a process to stop, then
|
|
|
|
// forward the event here so we don't lose the event
|
2016-09-07 04:57:50 +08:00
|
|
|
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.
|
Use flag instead of rwlock state to track process running state
LLDB requires that the inferior process be stopped before, and remain
stopped during, certain accesses to process state.
Previously this was achieved with a POSIX rwlock which had a write lock
taken for the duration that the process was running, and released when
the process was stopped. Any access to process state was performed with
a read lock held.
However, POSIX requires that pthread_rwlock_unlock() be called from the
same thread as pthread_rwlock_wrlock(), and lldb needs to stop and start
the process from different threads. Violating this constraint is
technically undefined behaviour, although as it happens Linux and Darwin
result in the unlock proceeding in this case. FreeBSD follows POSIX
more strictly, and the unlock would fail, resulting in a hang later upon
the next attempt to take the lock.
All read lock consumers use ReadTryLock() and handle failure to obtain
the lock (typically by logging an error "process is running"). Thus,
instead of using the lock state itself to track the running state, this
change adds an explicit m_running flag. ReadTryLock tests the flag, and
if the process is not running it returns with the read lock held.
WriteLock and WriteTryLock are renamed to SetRunning and TrySetRunning,
and (if successful) they set m_running with the lock held. This way,
read consumers can determine if the process is running and act
appropriately, and write consumers are still held off from starting the
process if read consumers are active.
Note that with this change there are still some curious access patterns,
such as calling WriteUnlock / SetStopped twice in a row, and there's no
protection from multiple threads trying to simultaneously start the
process. In practice this does not seem to be a problem, and was
exposing other undefined POSIX behaviour prior to this change.
llvm-svn: 187377
2013-07-30 04:58:06 +08:00
|
|
|
m_public_run_lock.SetStopped();
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2015-04-17 13:01:58 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
m_destroy_in_process = false;
|
2015-03-26 15:08:47 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
return error;
|
|
|
|
}
|
2013-03-02 04:04:25 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error Process::Signal(int signal) {
|
|
|
|
Error error(WillSignal());
|
|
|
|
if (error.Success()) {
|
|
|
|
error = DoSignal(signal);
|
2010-06-09 00:52:24 +08:00
|
|
|
if (error.Success())
|
2016-09-07 04:57:50 +08:00
|
|
|
DidSignal();
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Process::SetUnixSignals(UnixSignalsSP &&signals_sp) {
|
|
|
|
assert(signals_sp && "null signals_sp");
|
|
|
|
m_unix_signals_sp = 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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EVENTS |
|
|
|
|
LIBLLDB_LOG_PROCESS));
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case eStateDetached:
|
|
|
|
case eStateExited:
|
|
|
|
case eStateUnloaded:
|
|
|
|
m_stdio_communication.SynchronizeWithReadThread();
|
|
|
|
m_stdio_communication.Disconnect();
|
|
|
|
m_stdio_communication.StopReadThread();
|
|
|
|
m_stdin_forward = false;
|
|
|
|
|
|
|
|
LLVM_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;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
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)) {
|
|
|
|
if (log)
|
|
|
|
log->Printf("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 stop_vote = m_thread_list.ShouldReportStop(event_ptr);
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::ShouldBroadcastEvent: should_resume: %i state: "
|
|
|
|
"%s was_restarted: %i stop_vote: %d.",
|
|
|
|
should_resume, StateAsCString(state), was_restarted,
|
|
|
|
stop_vote);
|
|
|
|
|
|
|
|
switch (stop_vote) {
|
|
|
|
case eVoteYes:
|
|
|
|
return_value = true;
|
|
|
|
break;
|
|
|
|
case eVoteNoOpinion:
|
|
|
|
case eVoteNo:
|
|
|
|
return_value = false;
|
|
|
|
break;
|
2014-03-01 02:22:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (!was_restarted) {
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::ShouldBroadcastEvent (%p) Restarting process "
|
|
|
|
"from state: %s",
|
|
|
|
static_cast<void *>(event_ptr), StateAsCString(state));
|
|
|
|
ProcessEventData::SetRestartedInEvent(event_ptr, true);
|
|
|
|
PrivateResume();
|
2012-09-11 10:33:37 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
} 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;
|
|
|
|
|
|
|
|
if (log)
|
|
|
|
log->Printf("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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS));
|
|
|
|
|
|
|
|
bool already_running = PrivateStateThreadIsValid();
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s()%s ", __FUNCTION__,
|
|
|
|
already_running ? " already running"
|
|
|
|
: " starting private state thread");
|
|
|
|
|
|
|
|
if (!is_secondary_thread && already_running)
|
|
|
|
return true;
|
2012-09-11 10:33:37 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// 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];
|
2014-07-17 03:03:16 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (HostInfo::GetMaxThreadNameLength() <= 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");
|
2012-04-07 08:00:41 +08:00
|
|
|
else
|
2016-09-07 04:57:50 +08:00
|
|
|
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());
|
2012-04-10 09:21:57 +08:00
|
|
|
else
|
2016-09-07 04:57:50 +08:00
|
|
|
snprintf(thread_name, sizeof(thread_name),
|
|
|
|
"<lldb.process.internal-state(pid=%" PRIu64 ")>", GetID());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the private state thread, and start it running.
|
|
|
|
PrivateStateThreadArgs *args_ptr =
|
|
|
|
new PrivateStateThreadArgs(this, is_secondary_thread);
|
|
|
|
m_private_state_thread =
|
|
|
|
ThreadLauncher::LaunchThread(thread_name, Process::PrivateStateThread,
|
|
|
|
(void *)args_ptr, nullptr, 8 * 1024 * 1024);
|
|
|
|
if (m_private_state_thread.IsJoinable()) {
|
|
|
|
ResumePrivateStateThread();
|
|
|
|
return true;
|
|
|
|
} else
|
|
|
|
return false;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::PausePrivateStateThread() {
|
|
|
|
ControlPrivateStateThread(eBroadcastInternalStateControlPause);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::ResumePrivateStateThread() {
|
|
|
|
ControlPrivateStateThread(eBroadcastInternalStateControlResume);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::StopPrivateStateThread() {
|
|
|
|
if (m_private_state_thread.IsJoinable())
|
|
|
|
ControlPrivateStateThread(eBroadcastInternalStateControlStop);
|
|
|
|
else {
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"Went to stop the private state thread, but it was already invalid.");
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::ControlPrivateStateThread(uint32_t signal) {
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
assert(signal == eBroadcastInternalStateControlStop ||
|
|
|
|
signal == eBroadcastInternalStateControlPause ||
|
|
|
|
signal == eBroadcastInternalStateControlResume);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s (signal = %d)", __FUNCTION__, signal);
|
2016-06-15 00:22:45 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// 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).
|
|
|
|
if (log)
|
|
|
|
log->Printf("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) {
|
|
|
|
bool timed_out = false;
|
|
|
|
// Check for a receipt for 2 seconds and then check if the private state
|
|
|
|
// thread is still around.
|
|
|
|
receipt_received = event_receipt_sp->WaitForEventReceived(
|
|
|
|
std::chrono::seconds(2), &timed_out);
|
|
|
|
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
|
2016-05-13 06:58:52 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2012-04-13 02:49:31 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
|
|
|
|
if (signal == eBroadcastInternalStateControlStop) {
|
|
|
|
thread_result_t result = NULL;
|
|
|
|
m_private_state_thread.Join(&result);
|
|
|
|
m_private_state_thread.Reset();
|
2016-06-15 00:22:45 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
} else {
|
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"Private state thread already dead, no need to signal it to stop.");
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::SendAsyncInterrupt() {
|
|
|
|
if (PrivateStateThreadIsValid())
|
|
|
|
m_private_state_broadcaster.BroadcastEvent(Process::eBroadcastBitInterrupt,
|
|
|
|
nullptr);
|
|
|
|
else
|
|
|
|
BroadcastEvent(Process::eBroadcastBitInterrupt, nullptr);
|
2012-07-28 07:57:19 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::HandlePrivateEvent(EventSP &event_sp) {
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
m_resume_requested = false;
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const StateType new_state =
|
|
|
|
Process::ProcessEventData::GetStateFromEvent(event_sp.get());
|
Add a first pass at a "stop hook" mechanism. This allows you to add commands that get run every time the debugger stops, whether due to a breakpoint, the end of a step, interrupt, etc. You can also specify in which context you want the stop hook to run, for instance only on a particular thread, or only in a particular shared library, function, file, line range within a file.
Still need to add "in methods of a class" to the specifiers, and the ability to write the stop hooks in the Scripting language as well as in the Command Language.
llvm-svn: 127457
2011-03-11 11:53:59 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// First check to see if anybody wants a shot at this event:
|
|
|
|
if (m_next_event_action_ap) {
|
|
|
|
NextEventAction::EventActionResult action_result =
|
|
|
|
m_next_event_action_ap->PerformAction(event_sp);
|
|
|
|
if (log)
|
|
|
|
log->Printf("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_ap->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) {
|
|
|
|
log->Printf("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);
|
2010-06-09 00:52:24 +08:00
|
|
|
if (log)
|
2016-09-07 04:57:50 +08:00
|
|
|
log->Printf("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) {
|
|
|
|
log->Printf(
|
|
|
|
"Process::%s (pid = %" PRIu64
|
|
|
|
") suppressing state %s (old state %s): should_broadcast == false",
|
|
|
|
__FUNCTION__, GetID(), StateAsCString(new_state),
|
|
|
|
StateAsCString(GetState()));
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2015-12-07 20:36:52 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error Process::HaltPrivate() {
|
|
|
|
EventSP event_sp;
|
|
|
|
Error error(WillHalt());
|
|
|
|
if (error.Fail())
|
|
|
|
return error;
|
2015-12-07 20:36:52 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Ask the process subclass to actually halt our process
|
|
|
|
bool caused_stop;
|
|
|
|
error = DoHalt(caused_stop);
|
2015-12-07 20:36:52 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
DidHalt();
|
|
|
|
return error;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
thread_result_t Process::PrivateStateThread(void *arg) {
|
|
|
|
std::unique_ptr<PrivateStateThreadArgs> args_up(
|
|
|
|
static_cast<PrivateStateThreadArgs *>(arg));
|
|
|
|
thread_result_t result =
|
|
|
|
args_up->process->RunPrivateStateThread(args_up->is_secondary_thread);
|
|
|
|
return result;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
thread_result_t Process::RunPrivateStateThread(bool is_secondary_thread) {
|
|
|
|
bool control_only = true;
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s (arg = %p, pid = %" PRIu64 ") thread starting...",
|
|
|
|
__FUNCTION__, static_cast<void *>(this), GetID());
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool exit_now = false;
|
|
|
|
bool interrupt_requested = false;
|
|
|
|
while (!exit_now) {
|
|
|
|
EventSP event_sp;
|
|
|
|
WaitForEventsPrivate(std::chrono::microseconds(0), event_sp, control_only);
|
|
|
|
if (event_sp->BroadcasterIs(&m_private_state_control_broadcaster)) {
|
|
|
|
if (log)
|
|
|
|
log->Printf("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) {
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s (arg = %p, pid = %" PRIu64
|
|
|
|
") woke up with an interrupt while attaching - "
|
|
|
|
"forwarding interrupt.",
|
|
|
|
__FUNCTION__, static_cast<void *>(this), GetID());
|
|
|
|
BroadcastEvent(eBroadcastBitInterrupt, nullptr);
|
|
|
|
} else if (StateIsRunningState(m_last_broadcast_state)) {
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s (arg = %p, pid = %" PRIu64
|
|
|
|
") woke up with an interrupt - Halting.",
|
|
|
|
__FUNCTION__, static_cast<void *>(this), GetID());
|
|
|
|
Error error = HaltPrivate();
|
|
|
|
if (error.Fail() && log)
|
|
|
|
log->Printf("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.
|
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"Process::%s ignoring interrupt as we have already stopped.",
|
|
|
|
__FUNCTION__);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2015-12-07 20:36:52 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const StateType internal_state =
|
|
|
|
Process::ProcessEventData::GetStateFromEvent(event_sp.get());
|
2015-12-07 20:36:52 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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)) {
|
|
|
|
// 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) {
|
|
|
|
log->Printf("Process::%s interrupt_requested, but a non-stopped "
|
|
|
|
"state '%s' received.",
|
|
|
|
__FUNCTION__, StateAsCString(internal_state));
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
HandlePrivateEvent(event_sp);
|
|
|
|
}
|
2010-11-17 10:32:00 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (internal_state == eStateInvalid || internal_state == eStateExited ||
|
|
|
|
internal_state == eStateDetached) {
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s (arg = %p, pid = %" PRIu64
|
|
|
|
") about to exit with internal state %s...",
|
|
|
|
__FUNCTION__, static_cast<void *>(this), GetID(),
|
|
|
|
StateAsCString(internal_state));
|
|
|
|
|
|
|
|
break;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Verify log is still enabled before attempting to write to it...
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s (arg = %p, pid = %" PRIu64 ") thread exiting...",
|
|
|
|
__FUNCTION__, static_cast<void *>(this), GetID());
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// 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 NULL;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
// Process Event Data
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Process::ProcessEventData::ProcessEventData()
|
|
|
|
: EventData(), m_process_wp(), m_state(eStateInvalid), m_restarted(false),
|
|
|
|
m_update_state(0), m_interrupted(false) {}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Process::ProcessEventData::ProcessEventData(const ProcessSP &process_sp,
|
|
|
|
StateType state)
|
|
|
|
: EventData(), m_process_wp(), m_state(state), m_restarted(false),
|
|
|
|
m_update_state(0), m_interrupted(false) {
|
|
|
|
if (process_sp)
|
|
|
|
m_process_wp = process_sp;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2015-10-24 02:39:37 +08:00
|
|
|
Process::ProcessEventData::~ProcessEventData() = default;
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const ConstString &Process::ProcessEventData::GetFlavorString() {
|
|
|
|
static ConstString g_flavor("Process::ProcessEventData");
|
|
|
|
return g_flavor;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ConstString &Process::ProcessEventData::GetFlavor() const {
|
|
|
|
return ProcessEventData::GetFlavorString();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Process::ProcessEventData::DoOnRemoval(Event *event_ptr) {
|
|
|
|
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 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 stopped and haven't restarted, then do the StopInfo actions here:
|
|
|
|
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();
|
|
|
|
|
|
|
|
ThreadList &curr_thread_list = process_sp->GetThreadList();
|
|
|
|
uint32_t num_threads = curr_thread_list.GetSize();
|
|
|
|
uint32_t idx;
|
|
|
|
|
|
|
|
// 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 make up a vector of the
|
|
|
|
// thread ID's and check what we get back
|
|
|
|
// against this list & bag out if anything differs.
|
|
|
|
std::vector<uint32_t> thread_index_array(num_threads);
|
|
|
|
for (idx = 0; idx < num_threads; ++idx)
|
|
|
|
thread_index_array[idx] =
|
|
|
|
curr_thread_list.GetThreadAtIndex(idx)->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.
|
|
|
|
|
|
|
|
bool does_anybody_have_an_opinion = false;
|
|
|
|
|
|
|
|
for (idx = 0; idx < num_threads; ++idx) {
|
|
|
|
curr_thread_list = process_sp->GetThreadList();
|
|
|
|
if (curr_thread_list.GetSize() != num_threads) {
|
|
|
|
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP |
|
|
|
|
LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"Number of threads changed from %u to %u while processing event.",
|
|
|
|
num_threads, curr_thread_list.GetSize());
|
|
|
|
break;
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
lldb::ThreadSP thread_sp = curr_thread_list.GetThreadAtIndex(idx);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (thread_sp->GetIndexID() != thread_index_array[idx]) {
|
|
|
|
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP |
|
|
|
|
LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("The thread at position %u changed from %u to %u while "
|
|
|
|
"processing event.",
|
|
|
|
idx, thread_index_array[idx], thread_sp->GetIndexID());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
|
|
|
|
if (stop_info_sp && stop_info_sp->IsValid()) {
|
|
|
|
does_anybody_have_an_opinion = 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);
|
Add a first pass at a "stop hook" mechanism. This allows you to add commands that get run every time the debugger stops, whether due to a breakpoint, the end of a step, interrupt, etc. You can also specify in which context you want the stop hook to run, for instance only on a particular thread, or only in a particular shared library, function, file, line range within a file.
Still need to add "in methods of a class" to the specifiers, and the ability to write the stop hooks in the Scripting language as well as in the Command Language.
llvm-svn: 127457
2011-03-11 11:53:59 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
|
|
|
|
if (!still_should_stop)
|
|
|
|
still_should_stop = this_thread_wants_to_stop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!GetRestarted()) {
|
|
|
|
if (!still_should_stop && does_anybody_have_an_opinion) {
|
|
|
|
// We've been asked to continue, so do that here.
|
|
|
|
SetRestarted(true);
|
|
|
|
// Use the public resume method here, since this is just
|
|
|
|
// extending a public resume.
|
|
|
|
process_sp->PrivateResume();
|
|
|
|
} else {
|
|
|
|
// If we didn't restart, run the Stop Hooks here:
|
|
|
|
// They might also restart the target, so watch for that.
|
|
|
|
process_sp->GetTarget().RunStopHooks();
|
|
|
|
if (process_sp->GetPrivateState() == eStateRunning)
|
|
|
|
SetRestarted(true);
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::ProcessEventData::Dump(Stream *s) const {
|
|
|
|
ProcessSP process_sp(m_process_wp.lock());
|
2015-05-29 11:20:37 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (process_sp)
|
|
|
|
s->Printf(" process = %p (pid = %" PRIu64 "), ",
|
|
|
|
static_cast<void *>(process_sp.get()), process_sp->GetID());
|
|
|
|
else
|
|
|
|
s->PutCString(" process = NULL, ");
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
s->Printf("state = %s", StateAsCString(GetState()));
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const Process::ProcessEventData *
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ProcessSP
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
StateType Process::ProcessEventData::GetStateFromEvent(const Event *event_ptr) {
|
|
|
|
const ProcessEventData *data = GetEventDataFromEvent(event_ptr);
|
|
|
|
if (data == nullptr)
|
|
|
|
return eStateInvalid;
|
|
|
|
else
|
|
|
|
return data->GetState();
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Process::ProcessEventData::GetRestartedFromEvent(const Event *event_ptr) {
|
|
|
|
const ProcessEventData *data = GetEventDataFromEvent(event_ptr);
|
|
|
|
if (data == nullptr)
|
|
|
|
return false;
|
|
|
|
else
|
|
|
|
return data->GetRestarted();
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2013-02-09 09:29:05 +08:00
|
|
|
size_t
|
2016-09-07 04:57:50 +08:00
|
|
|
Process::ProcessEventData::GetNumRestartedReasons(const Event *event_ptr) {
|
|
|
|
ProcessEventData *data =
|
|
|
|
const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr));
|
|
|
|
if (data != nullptr)
|
|
|
|
return data->GetNumRestartedReasons();
|
|
|
|
else
|
|
|
|
return 0;
|
2013-02-09 09:29:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
2013-02-09 09:29:05 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::ProcessEventData::AddRestartedReason(Event *event_ptr,
|
|
|
|
const char *reason) {
|
|
|
|
ProcessEventData *data =
|
|
|
|
const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr));
|
|
|
|
if (data != nullptr)
|
|
|
|
data->AddRestartedReason(reason);
|
2013-02-09 09:29:05 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Process::ProcessEventData::GetInterruptedFromEvent(
|
|
|
|
const Event *event_ptr) {
|
|
|
|
const ProcessEventData *data = GetEventDataFromEvent(event_ptr);
|
|
|
|
if (data == nullptr)
|
|
|
|
return false;
|
|
|
|
else
|
|
|
|
return data->GetInterrupted();
|
2010-11-17 10:32:00 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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);
|
2010-11-17 10:32:00 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Process::ProcessEventData::SetUpdateStateOnRemoval(Event *event_ptr) {
|
|
|
|
ProcessEventData *data =
|
|
|
|
const_cast<ProcessEventData *>(GetEventDataFromEvent(event_ptr));
|
|
|
|
if (data) {
|
|
|
|
data->SetUpdateStateOnRemoval();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
lldb::TargetSP Process::CalculateTarget() { return m_target_sp.lock(); }
|
2012-02-18 13:35:26 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::CalculateExecutionContext(ExecutionContext &exe_ctx) {
|
|
|
|
exe_ctx.SetTargetPtr(&GetTarget());
|
|
|
|
exe_ctx.SetProcessPtr(this);
|
|
|
|
exe_ctx.SetThreadPtr(nullptr);
|
|
|
|
exe_ctx.SetFramePtr(nullptr);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// uint32_t
|
|
|
|
// Process::ListProcessesMatchingName (const char *name, StringList &matches,
|
|
|
|
// std::vector<lldb::pid_t> &pids)
|
2011-03-09 06:40:15 +08:00
|
|
|
//{
|
|
|
|
// return 0;
|
|
|
|
//}
|
2016-09-07 04:57:50 +08:00
|
|
|
//
|
|
|
|
// ArchSpec
|
|
|
|
// Process::GetArchSpecForExistingProcess (lldb::pid_t pid)
|
2011-03-09 06:40:15 +08:00
|
|
|
//{
|
|
|
|
// return Host::GetArchSpecForExistingProcess (pid);
|
|
|
|
//}
|
|
|
|
//
|
2016-09-07 04:57:50 +08:00
|
|
|
// ArchSpec
|
|
|
|
// Process::GetArchSpecForExistingProcess (const char *process_name)
|
2011-03-09 06:40:15 +08:00
|
|
|
//{
|
|
|
|
// return Host::GetArchSpecForExistingProcess (process_name);
|
|
|
|
//}
|
2016-03-01 08:55:51 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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);
|
|
|
|
BroadcastEventIfUnique(eBroadcastBitSTDOUT,
|
|
|
|
new ProcessEventData(shared_from_this(), GetState()));
|
2010-11-16 13:07:41 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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);
|
|
|
|
BroadcastEventIfUnique(eBroadcastBitSTDERR,
|
|
|
|
new ProcessEventData(shared_from_this(), GetState()));
|
2011-11-13 12:45:22 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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);
|
|
|
|
BroadcastEventIfUnique(eBroadcastBitProfileData,
|
|
|
|
new ProcessEventData(shared_from_this(), GetState()));
|
2012-11-17 08:21:04 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::BroadcastStructuredData(const StructuredData::ObjectSP &object_sp,
|
|
|
|
const StructuredDataPluginSP &plugin_sp) {
|
|
|
|
BroadcastEvent(
|
|
|
|
eBroadcastBitStructuredData,
|
|
|
|
new EventDataStructuredData(shared_from_this(), object_sp, plugin_sp));
|
2016-08-19 12:21:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
StructuredDataPluginSP
|
2016-09-07 04:57:50 +08:00
|
|
|
Process::GetStructuredDataPlugin(const ConstString &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();
|
2016-08-19 12:21:48 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
size_t Process::GetAsyncProfileData(char *buf, size_t buf_size, Error &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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("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());
|
2012-11-17 08:21:04 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
return bytes_available;
|
2012-11-17 08:21:04 +08:00
|
|
|
}
|
|
|
|
|
2011-11-13 12:45:22 +08:00
|
|
|
//------------------------------------------------------------------
|
|
|
|
// Process STDIO
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
size_t Process::GetSTDOUT(char *buf, size_t buf_size, Error &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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("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, Error &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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("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();
|
2011-11-13 12:45:22 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
return bytes_available;
|
2011-11-13 12:45:22 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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);
|
2010-11-16 13:07:41 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
class IOHandlerProcessSTDIO : public IOHandler {
|
2014-01-28 07:43:24 +08:00
|
|
|
public:
|
2016-09-07 04:57:50 +08:00
|
|
|
IOHandlerProcessSTDIO(Process *process, int write_fd)
|
|
|
|
: IOHandler(process->GetTarget().GetDebugger(),
|
|
|
|
IOHandler::Type::ProcessIO),
|
|
|
|
m_process(process), m_read_file(), m_write_file(write_fd, false),
|
|
|
|
m_pipe() {
|
|
|
|
m_pipe.CreateNew(false);
|
|
|
|
m_read_file.SetDescriptor(GetInputFD(), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
~IOHandlerProcessSTDIO() override = default;
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
TerminalState terminal_state;
|
|
|
|
terminal_state.Save(read_fd, false);
|
|
|
|
Terminal terminal(read_fd);
|
|
|
|
terminal.SetCanonical(false);
|
|
|
|
terminal.SetEcho(false);
|
2014-02-01 02:48:46 +08:00
|
|
|
// FD_ZERO, FD_SET are not supported on windows
|
2014-03-12 18:45:23 +08:00
|
|
|
#ifndef _WIN32
|
2016-09-07 04:57:50 +08:00
|
|
|
const int pipe_read_fd = m_pipe.GetReadFileDescriptor();
|
|
|
|
m_is_running = true;
|
|
|
|
while (!GetIsDone()) {
|
|
|
|
SelectHelper select_helper;
|
|
|
|
select_helper.FDSetRead(read_fd);
|
|
|
|
select_helper.FDSetRead(pipe_read_fd);
|
|
|
|
Error error = select_helper.Select();
|
|
|
|
|
|
|
|
if (error.Fail()) {
|
2016-02-27 01:36:44 +08:00
|
|
|
SetIsDone(true);
|
2016-09-07 04:57:50 +08:00
|
|
|
} else {
|
|
|
|
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)
|
|
|
|
SetIsDone(true);
|
|
|
|
} else
|
|
|
|
SetIsDone(true);
|
2014-07-17 05:05:41 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
if (select_helper.FDIsSetRead(pipe_read_fd)) {
|
|
|
|
size_t bytes_read;
|
|
|
|
// Consume the interrupt byte
|
|
|
|
Error error = m_pipe.Read(&ch, 1, bytes_read);
|
|
|
|
if (error.Success()) {
|
|
|
|
switch (ch) {
|
|
|
|
case 'q':
|
|
|
|
SetIsDone(true);
|
|
|
|
break;
|
|
|
|
case 'i':
|
|
|
|
if (StateIsRunningState(m_process->GetState()))
|
2014-07-17 05:05:41 +08:00
|
|
|
m_process->SendAsyncInterrupt();
|
2016-09-07 04:57:50 +08:00
|
|
|
break;
|
2014-07-17 05:05:41 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2014-07-17 05:05:41 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2014-02-25 06:50:57 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
m_is_running = false;
|
|
|
|
#endif
|
|
|
|
terminal_state.Restore();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Cancel() override {
|
|
|
|
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
|
|
|
|
size_t bytes_written = 0;
|
|
|
|
m_pipe.Write(&ch, 1, bytes_written);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
size_t bytes_written = 0;
|
|
|
|
Error result = m_pipe.Write(&ch, 1, bytes_written);
|
|
|
|
return result.Success();
|
|
|
|
} 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;
|
|
|
|
}
|
2010-11-16 13:07:41 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GotEOF() override {}
|
|
|
|
|
2014-01-28 07:43:24 +08:00
|
|
|
protected:
|
2016-09-07 04:57:50 +08:00
|
|
|
Process *m_process;
|
|
|
|
File m_read_file; // Read from this file (usually actual STDIN for LLDB
|
|
|
|
File m_write_file; // Write to this file (usually the master pty for getting
|
|
|
|
// io to debuggee)
|
|
|
|
Pipe m_pipe;
|
|
|
|
std::atomic<bool> m_is_running;
|
2014-01-28 07:43:24 +08:00
|
|
|
};
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::SetSTDIOFileDescriptor(int fd) {
|
|
|
|
// First set up the Read Thread for reading/handling process I/O
|
|
|
|
|
|
|
|
std::unique_ptr<ConnectionFileDescriptor> conn_ap(
|
|
|
|
new ConnectionFileDescriptor(fd, true));
|
|
|
|
|
|
|
|
if (conn_ap) {
|
|
|
|
m_stdio_communication.SetConnection(conn_ap.release());
|
|
|
|
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.
|
|
|
|
|
|
|
|
if (!m_process_input_reader)
|
|
|
|
m_process_input_reader.reset(new IOHandlerProcessSTDIO(this, fd));
|
2010-11-16 13:07:41 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2010-11-16 13:07:41 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Process::ProcessIOHandlerIsActive() {
|
|
|
|
IOHandlerSP io_handler_sp(m_process_input_reader);
|
|
|
|
if (io_handler_sp)
|
|
|
|
return GetTarget().GetDebugger().IsTopIOHandler(io_handler_sp);
|
|
|
|
return false;
|
2014-03-04 03:15:20 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Process::PushProcessIOHandler() {
|
|
|
|
IOHandlerSP io_handler_sp(m_process_input_reader);
|
|
|
|
if (io_handler_sp) {
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s pushing IO handler", __FUNCTION__);
|
2015-05-27 20:40:32 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
io_handler_sp->SetIsDone(false);
|
|
|
|
GetTarget().GetDebugger().PushIOHandler(io_handler_sp);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2010-11-16 13:07:41 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Process::PopProcessIOHandler() {
|
|
|
|
IOHandlerSP io_handler_sp(m_process_input_reader);
|
|
|
|
if (io_handler_sp)
|
|
|
|
return GetTarget().GetDebugger().PopIOHandler(io_handler_sp);
|
|
|
|
return false;
|
2010-11-16 13:07:41 +08:00
|
|
|
}
|
|
|
|
|
2011-02-18 09:44:25 +08:00
|
|
|
// The process needs to know about installed plug-ins
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::SettingsInitialize() { Thread::SettingsInitialize(); }
|
2010-09-04 08:03:46 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::SettingsTerminate() { Thread::SettingsTerminate(); }
|
2010-09-04 08:03:46 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
namespace {
|
|
|
|
// RestorePlanState is used to record the "is private", "is master" 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.
|
2015-10-24 02:39:37 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
class RestorePlanState {
|
|
|
|
public:
|
|
|
|
RestorePlanState(lldb::ThreadPlanSP thread_plan_sp)
|
|
|
|
: m_thread_plan_sp(thread_plan_sp), m_already_reset(false) {
|
|
|
|
if (m_thread_plan_sp) {
|
|
|
|
m_private = m_thread_plan_sp->GetPrivate();
|
|
|
|
m_is_master = m_thread_plan_sp->IsMasterPlan();
|
|
|
|
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->SetIsMasterPlan(m_is_master);
|
|
|
|
m_thread_plan_sp->SetOkayToDiscard(m_okay_to_discard);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
lldb::ThreadPlanSP m_thread_plan_sp;
|
|
|
|
bool m_already_reset;
|
|
|
|
bool m_private;
|
|
|
|
bool m_is_master;
|
|
|
|
bool m_okay_to_discard;
|
|
|
|
};
|
2015-10-24 02:39:37 +08:00
|
|
|
} // anonymous namespace
|
2015-10-13 03:11:03 +08:00
|
|
|
|
2014-05-05 10:26:40 +08:00
|
|
|
ExpressionResults
|
2016-09-07 04:57:50 +08:00
|
|
|
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.PutCString(
|
|
|
|
eDiagnosticSeverityError,
|
|
|
|
"RunThreadPlan called with empty thread plan.");
|
|
|
|
return eExpressionSetupError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!thread_plan_sp->ValidatePlan(nullptr)) {
|
|
|
|
diagnostic_manager.PutCString(
|
|
|
|
eDiagnosticSeverityError,
|
|
|
|
"RunThreadPlan called with an invalid thread plan.");
|
|
|
|
return eExpressionSetupError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (exe_ctx.GetProcessPtr() != this) {
|
|
|
|
diagnostic_manager.PutCString(eDiagnosticSeverityError,
|
|
|
|
"RunThreadPlan called on wrong process.");
|
|
|
|
return eExpressionSetupError;
|
|
|
|
}
|
|
|
|
|
|
|
|
Thread *thread = exe_ctx.GetThreadPtr();
|
|
|
|
if (thread == nullptr) {
|
|
|
|
diagnostic_manager.PutCString(eDiagnosticSeverityError,
|
|
|
|
"RunThreadPlan called with invalid thread.");
|
|
|
|
return eExpressionSetupError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 master 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->SetIsMasterPlan(true);
|
|
|
|
thread_plan_sp->SetOkayToDiscard(false);
|
|
|
|
|
|
|
|
if (m_private_state.GetValue() != eStateStopped) {
|
|
|
|
diagnostic_manager.PutCString(
|
|
|
|
eDiagnosticSeverityError,
|
|
|
|
"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();
|
|
|
|
if (!selected_frame_sp) {
|
|
|
|
thread->SetSelectedFrame(nullptr);
|
|
|
|
selected_frame_sp = thread->GetSelectedFrame();
|
|
|
|
if (!selected_frame_sp) {
|
|
|
|
diagnostic_manager.Printf(
|
|
|
|
eDiagnosticSeverityError,
|
|
|
|
"RunThreadPlan called without a selected frame on thread %d",
|
|
|
|
thread_idx_id);
|
|
|
|
return eExpressionSetupError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
StackID ctx_frame_id = 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()->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(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP |
|
|
|
|
LIBLLDB_LOG_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.
|
|
|
|
if (log)
|
|
|
|
log->Printf("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);
|
|
|
|
log->Printf("Process::RunThreadPlan(): Resuming thread %u - 0x%4.4" PRIx64
|
|
|
|
" to run thread plan \"%s\".",
|
|
|
|
thread->GetIndexID(), thread->GetID(), 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;
|
|
|
|
const uint64_t default_one_thread_timeout_usec = 250000;
|
|
|
|
|
|
|
|
// This is just for accounting:
|
|
|
|
uint32_t num_resumes = 0;
|
|
|
|
|
|
|
|
uint32_t timeout_usec = options.GetTimeoutUsec();
|
|
|
|
uint32_t one_thread_timeout_usec;
|
|
|
|
uint32_t all_threads_timeout_usec = 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 set the final timeout, and
|
|
|
|
// pretend we are after the
|
|
|
|
// first timeout already.
|
|
|
|
|
|
|
|
if (!options.GetStopOthers() || !options.GetTryAllThreads()) {
|
|
|
|
before_first_timeout = false;
|
|
|
|
one_thread_timeout_usec = 0;
|
|
|
|
all_threads_timeout_usec = timeout_usec;
|
|
|
|
} else {
|
|
|
|
uint32_t option_one_thread_timeout = options.GetOneThreadTimeoutUsec();
|
|
|
|
|
|
|
|
// If the overall wait is forever, then we only need to set the one thread
|
|
|
|
// timeout:
|
|
|
|
if (timeout_usec == 0) {
|
|
|
|
if (option_one_thread_timeout != 0)
|
|
|
|
one_thread_timeout_usec = option_one_thread_timeout;
|
2014-04-16 10:24:48 +08:00
|
|
|
else
|
2016-09-07 04:57:50 +08:00
|
|
|
one_thread_timeout_usec = default_one_thread_timeout_usec;
|
|
|
|
} else {
|
|
|
|
// Otherwise, if the one thread timeout is set, make sure it isn't
|
|
|
|
// longer than the overall timeout,
|
|
|
|
// and use it, otherwise use half the total timeout, bounded by the
|
|
|
|
// default_one_thread_timeout_usec.
|
|
|
|
uint64_t computed_one_thread_timeout;
|
|
|
|
if (option_one_thread_timeout != 0) {
|
|
|
|
if (timeout_usec < option_one_thread_timeout) {
|
2016-03-19 08:03:59 +08:00
|
|
|
diagnostic_manager.PutCString(eDiagnosticSeverityError,
|
2016-09-07 04:57:50 +08:00
|
|
|
"RunThreadPlan called without one "
|
|
|
|
"thread timeout greater than total "
|
|
|
|
"timeout");
|
2014-05-05 10:47:44 +08:00
|
|
|
return eExpressionSetupError;
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
computed_one_thread_timeout = option_one_thread_timeout;
|
|
|
|
} else {
|
|
|
|
computed_one_thread_timeout = timeout_usec / 2;
|
|
|
|
if (computed_one_thread_timeout > default_one_thread_timeout_usec)
|
|
|
|
computed_one_thread_timeout = default_one_thread_timeout_usec;
|
2014-01-11 07:46:59 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
one_thread_timeout_usec = computed_one_thread_timeout;
|
|
|
|
all_threads_timeout_usec = timeout_usec - one_thread_timeout_usec;
|
|
|
|
}
|
|
|
|
}
|
2014-04-04 12:06:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"Stop others: %u, try all: %u, before_first: %u, one thread: %" PRIu32
|
|
|
|
" - all threads: %" PRIu32 ".\n",
|
|
|
|
options.GetStopOthers(), options.GetTryAllThreads(),
|
|
|
|
before_first_timeout, one_thread_timeout_usec,
|
|
|
|
all_threads_timeout_usec);
|
|
|
|
|
|
|
|
// 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.PutCString(
|
|
|
|
eDiagnosticSeverityError,
|
|
|
|
"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.
|
2014-04-04 12:06:10 +08:00
|
|
|
|
2014-04-09 05:33:21 +08:00
|
|
|
#ifdef LLDB_RUN_THREAD_HALT_WITH_EVENT
|
2016-09-07 04:57:50 +08:00
|
|
|
// 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;
|
2014-04-09 05:33:21 +08:00
|
|
|
#endif
|
2016-09-07 04:57:50 +08:00
|
|
|
std::chrono::microseconds timeout = std::chrono::microseconds(0);
|
|
|
|
|
|
|
|
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.
|
|
|
|
if (log)
|
|
|
|
log->Printf("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++;
|
|
|
|
Error resume_error = PrivateResume();
|
|
|
|
if (!resume_error.Success()) {
|
|
|
|
diagnostic_manager.Printf(
|
|
|
|
eDiagnosticSeverityError,
|
|
|
|
"couldn't resume inferior the %d time: \"%s\".", num_resumes,
|
|
|
|
resume_error.AsCString());
|
|
|
|
return_value = eExpressionSetupError;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-04-04 12:06:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
got_event = listener_sp->WaitForEvent(std::chrono::microseconds(500000),
|
|
|
|
event_sp);
|
|
|
|
if (!got_event) {
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::RunThreadPlan(): didn't get any event after "
|
|
|
|
"resume %" PRIu32 ", exiting.",
|
|
|
|
num_resumes);
|
|
|
|
|
|
|
|
diagnostic_manager.Printf(eDiagnosticSeverityError,
|
|
|
|
"didn't get any event after resume %" PRIu32
|
|
|
|
", exiting.",
|
2016-03-19 08:03:59 +08:00
|
|
|
num_resumes);
|
2016-09-07 04:57:50 +08:00
|
|
|
return_value = eExpressionSetupError;
|
|
|
|
break;
|
|
|
|
}
|
2011-02-08 13:20:59 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
stop_state =
|
|
|
|
Process::ProcessEventData::GetStateFromEvent(event_sp.get());
|
2014-04-04 12:06:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (stop_state != eStateRunning) {
|
|
|
|
bool restarted = false;
|
2014-04-04 12:06:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (stop_state == eStateStopped) {
|
|
|
|
restarted = Process::ProcessEventData::GetRestartedFromEvent(
|
|
|
|
event_sp.get());
|
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"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(
|
|
|
|
eDiagnosticSeverityError,
|
|
|
|
"didn't get running event after initial resume, got %s instead.",
|
|
|
|
StateAsCString(stop_state));
|
|
|
|
return_value = eExpressionSetupError;
|
|
|
|
break;
|
|
|
|
}
|
2014-04-04 12:06:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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.");
|
|
|
|
}
|
2014-04-04 12:06:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (before_first_timeout) {
|
|
|
|
if (options.GetTryAllThreads())
|
|
|
|
timeout = std::chrono::microseconds(one_thread_timeout_usec);
|
|
|
|
else
|
|
|
|
timeout = std::chrono::microseconds(timeout_usec);
|
|
|
|
} else {
|
|
|
|
if (timeout_usec == 0)
|
|
|
|
timeout = std::chrono::microseconds(0);
|
|
|
|
else
|
|
|
|
timeout = std::chrono::microseconds(all_threads_timeout_usec);
|
|
|
|
}
|
|
|
|
|
|
|
|
do_resume = true;
|
|
|
|
handle_running_event = true;
|
|
|
|
|
|
|
|
// Now wait for the process to stop again:
|
|
|
|
event_sp.reset();
|
|
|
|
|
|
|
|
if (log) {
|
|
|
|
if (timeout.count()) {
|
|
|
|
log->Printf(
|
|
|
|
"Process::RunThreadPlan(): about to wait - now is %llu - "
|
|
|
|
"endpoint is %llu",
|
|
|
|
static_cast<unsigned long long>(
|
|
|
|
std::chrono::system_clock::now().time_since_epoch().count()),
|
|
|
|
static_cast<unsigned long long>(
|
|
|
|
std::chrono::time_point<std::chrono::system_clock,
|
|
|
|
std::chrono::microseconds>(timeout)
|
|
|
|
.time_since_epoch()
|
|
|
|
.count()));
|
|
|
|
} else {
|
|
|
|
log->Printf("Process::RunThreadPlan(): about to wait forever.");
|
|
|
|
}
|
|
|
|
}
|
2012-04-14 07:09:49 +08:00
|
|
|
|
2014-04-09 05:33:21 +08:00
|
|
|
#ifdef LLDB_RUN_THREAD_HALT_WITH_EVENT
|
2016-09-07 04:57:50 +08:00
|
|
|
// See comment above...
|
|
|
|
if (miss_first_event) {
|
|
|
|
usleep(1000);
|
|
|
|
miss_first_event = false;
|
|
|
|
got_event = false;
|
|
|
|
} else
|
2014-04-09 05:33:21 +08:00
|
|
|
#endif
|
2016-09-07 04:57:50 +08:00
|
|
|
got_event = listener_sp->WaitForEvent(timeout, event_sp);
|
|
|
|
|
|
|
|
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.PutCString(
|
|
|
|
eDiagnosticSeverityRemark,
|
|
|
|
"execution halted by user interrupt.");
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::RunThreadPlan(): Got interrupted by "
|
|
|
|
"eBroadcastBitInterrupted, exiting.");
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
stop_state =
|
|
|
|
Process::ProcessEventData::GetStateFromEvent(event_sp.get());
|
|
|
|
if (log)
|
|
|
|
log->Printf(
|
|
|
|
"Process::RunThreadPlan(): in while loop, got event: %s.",
|
|
|
|
StateAsCString(stop_state));
|
|
|
|
|
|
|
|
switch (stop_state) {
|
|
|
|
case lldb::eStateStopped: {
|
|
|
|
// We stopped, figure out what we are going to do now.
|
|
|
|
ThreadSP thread_sp =
|
|
|
|
GetThreadList().FindThreadByIndexID(thread_idx_id);
|
|
|
|
if (!thread_sp) {
|
|
|
|
// Ooh, our thread has vanished. Unlikely that this was
|
|
|
|
// successful execution...
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::RunThreadPlan(): execution completed "
|
|
|
|
"but our thread (index-id=%u) has vanished.",
|
|
|
|
thread_idx_id);
|
|
|
|
return_value = eExpressionInterrupted;
|
|
|
|
} else {
|
|
|
|
// If we were restarted, we just need to go back up to fetch
|
|
|
|
// another event.
|
|
|
|
if (Process::ProcessEventData::GetRestartedFromEvent(
|
|
|
|
event_sp.get())) {
|
|
|
|
if (log) {
|
|
|
|
log->Printf("Process::RunThreadPlan(): Got a stop and "
|
|
|
|
"restart, so we'll continue waiting.");
|
|
|
|
}
|
|
|
|
keep_going = true;
|
|
|
|
do_resume = false;
|
|
|
|
handle_running_event = true;
|
|
|
|
} else {
|
|
|
|
StopInfoSP stop_info_sp(thread_sp->GetStopInfo());
|
|
|
|
StopReason stop_reason = eStopReasonInvalid;
|
|
|
|
if (stop_info_sp)
|
|
|
|
stop_reason = stop_info_sp->GetStopReason();
|
|
|
|
|
|
|
|
// FIXME: We only check if the stop reason is plan complete,
|
|
|
|
// should we make sure that
|
|
|
|
// it is OUR plan that is complete?
|
|
|
|
if (stop_reason == eStopReasonPlanComplete) {
|
2011-02-08 13:20:59 +08:00
|
|
|
if (log)
|
2016-09-07 04:57:50 +08:00
|
|
|
log->PutCString("Process::RunThreadPlan(): execution "
|
|
|
|
"completed successfully.");
|
|
|
|
|
|
|
|
// Restore the plan state so it will get reported as
|
|
|
|
// intended when we are done.
|
|
|
|
thread_plan_restorer.Clean();
|
|
|
|
|
|
|
|
return_value = eExpressionCompleted;
|
|
|
|
} else {
|
|
|
|
// Something restarted the target, so just wait for it to
|
|
|
|
// stop for real.
|
|
|
|
if (stop_reason == eStopReasonBreakpoint) {
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::RunThreadPlan() stopped for "
|
|
|
|
"breakpoint: %s.",
|
|
|
|
stop_info_sp->GetDescription());
|
|
|
|
return_value = eExpressionHitBreakpoint;
|
|
|
|
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.
|
|
|
|
thread_plan_restorer.Clean();
|
|
|
|
if (thread_plan_sp)
|
|
|
|
thread_plan_sp->SetPrivate(false);
|
|
|
|
event_to_broadcast_sp = event_sp;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (log)
|
|
|
|
log->PutCString("Process::RunThreadPlan(): thread plan "
|
|
|
|
"didn't successfully complete.");
|
|
|
|
if (!options.DoesUnwindOnError())
|
|
|
|
event_to_broadcast_sp = event_sp;
|
|
|
|
return_value = eExpressionInterrupted;
|
2013-02-09 09:29:05 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2011-01-24 05:14:08 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
} 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:
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::RunThreadPlan(): execution stopped with "
|
|
|
|
"unexpected state: %s.",
|
|
|
|
StateAsCString(stop_state));
|
|
|
|
|
|
|
|
if (stop_state == eStateExited)
|
|
|
|
event_to_broadcast_sp = event_sp;
|
|
|
|
|
|
|
|
diagnostic_manager.PutCString(
|
|
|
|
eDiagnosticSeverityError,
|
|
|
|
"execution stopped with unexpected state.");
|
|
|
|
return_value = eExpressionInterrupted;
|
|
|
|
break;
|
2011-02-08 13:20:59 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2015-10-13 03:11:03 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
2015-10-13 03:11:03 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
} 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) {
|
|
|
|
if (timeout_usec != 0) {
|
|
|
|
log->Printf("Process::RunThreadPlan(): Running function with "
|
|
|
|
"one thread timeout timed out, "
|
|
|
|
"running for %" PRIu32
|
|
|
|
" usec with all threads enabled.",
|
|
|
|
all_threads_timeout_usec);
|
|
|
|
} else {
|
|
|
|
log->Printf("Process::RunThreadPlan(): Running function with "
|
|
|
|
"one thread timeout timed out, "
|
|
|
|
"running forever with all threads enabled.");
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
log->Printf("Process::RunThreadPlan(): Restarting function with "
|
|
|
|
"all threads enabled "
|
|
|
|
"and timeout: %u timed out, abandoning execution.",
|
|
|
|
timeout_usec);
|
|
|
|
} else
|
|
|
|
log->Printf("Process::RunThreadPlan(): Running function with "
|
|
|
|
"timeout: %u timed out, "
|
|
|
|
"abandoning execution.",
|
|
|
|
timeout_usec);
|
2012-11-27 07:52:18 +08:00
|
|
|
}
|
2014-04-04 12:06:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// 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) {
|
|
|
|
Error halt_error;
|
|
|
|
if (do_halt) {
|
2012-07-12 05:31:24 +08:00
|
|
|
if (log)
|
2016-09-07 04:57:50 +08:00
|
|
|
log->Printf("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->WaitForEvent(
|
|
|
|
std::chrono::microseconds(500000), event_sp);
|
|
|
|
|
|
|
|
if (got_event) {
|
|
|
|
stop_state =
|
|
|
|
Process::ProcessEventData::GetStateFromEvent(event_sp.get());
|
|
|
|
if (log) {
|
|
|
|
log->Printf("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) {
|
|
|
|
// Between the time we initiated the Halt and the time we
|
|
|
|
// delivered it, the process could have
|
|
|
|
// already finished its job. Check that here:
|
|
|
|
|
|
|
|
if (thread->IsThreadPlanDone(thread_plan_sp.get())) {
|
|
|
|
if (log)
|
|
|
|
log->PutCString("Process::RunThreadPlan(): Even though we "
|
|
|
|
"timed out, the call plan was done. "
|
|
|
|
"Exiting wait loop.");
|
|
|
|
return_value = eExpressionCompleted;
|
|
|
|
back_to_top = false;
|
|
|
|
break;
|
2011-01-22 09:30:53 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
|
|
|
}
|
2014-04-04 12:06:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
|
|
|
}
|
2014-04-04 12:06:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
2012-07-12 05:31:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
} 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;
|
2012-04-14 07:09:49 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
} else {
|
|
|
|
try_halt_again++;
|
|
|
|
continue;
|
|
|
|
}
|
2010-11-30 10:22:11 +08:00
|
|
|
}
|
2014-04-04 12:06:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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();
|
|
|
|
Error 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 (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);
|
|
|
|
log->PutCString(s.GetData());
|
|
|
|
}
|
|
|
|
// 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.");
|
2011-02-08 13:20:59 +08:00
|
|
|
}
|
2014-04-04 12:06:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
StreamString ts;
|
2014-04-04 12:06:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const char *event_explanation = nullptr;
|
2010-11-30 10:22:11 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
do {
|
|
|
|
if (!event_sp) {
|
|
|
|
event_explanation = "<no event>";
|
2014-05-05 10:26:40 +08:00
|
|
|
break;
|
2016-09-07 04:57:50 +08:00
|
|
|
} else if (event_sp->GetType() == eBroadcastBitInterrupt) {
|
|
|
|
event_explanation = "<user interrupt>";
|
2010-11-30 10:22:11 +08:00
|
|
|
break;
|
2016-09-07 04:57:50 +08:00
|
|
|
} else {
|
|
|
|
const Process::ProcessEventData *event_data =
|
|
|
|
Process::ProcessEventData::GetEventDataFromEvent(
|
|
|
|
event_sp.get());
|
|
|
|
|
|
|
|
if (!event_data) {
|
|
|
|
event_explanation = "<no event data>";
|
|
|
|
break;
|
Centralized a lot of the status information for processes,
threads, and stack frame down in the lldb_private::Process,
lldb_private::Thread, lldb_private::StackFrameList and the
lldb_private::StackFrame classes. We had some command line
commands that had duplicate versions of the process status
output ("thread list" and "process status" for example).
Removed the "file" command and placed it where it should
have been: "target create". Made an alias for "file" to
"target create" so we stay compatible with GDB commands.
We can now have multple usable targets in lldb at the
same time. This is nice for comparing two runs of a program
or debugging more than one binary at the same time. The
new command is "target select <target-idx>" and also to see
a list of the current targets you can use the new "target list"
command. The flow in a debug session can be:
(lldb) target create /path/to/exe/a.out
(lldb) breakpoint set --name main
(lldb) run
... hit breakpoint
(lldb) target create /bin/ls
(lldb) run /tmp
Process 36001 exited with status = 0 (0x00000000)
(lldb) target list
Current targets:
target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
* target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) target select 0
Current targets:
* target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) bt
* thread #1: tid = 0x2d03, 0x0000000100000b9a a.out`main + 42 at main.c:16, stop reason = breakpoint 1.1
frame #0: 0x0000000100000b9a a.out`main + 42 at main.c:16
frame #1: 0x0000000100000b64 a.out`start + 52
Above we created a target for "a.out" and ran and hit a
breakpoint at "main". Then we created a new target for /bin/ls
and ran it. Then we listed the targest and selected our original
"a.out" program, so we showed two concurent debug sessions
going on at the same time.
llvm-svn: 129695
2011-04-18 16:33:37 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Process *process = event_data->GetProcessSP().get();
|
2012-02-22 12:37:26 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (!process) {
|
|
|
|
event_explanation = "<no process>";
|
|
|
|
break;
|
|
|
|
}
|
2012-02-22 12:37:26 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
ThreadList &thread_list = process->GetThreadList();
|
2012-04-07 08:00:41 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
uint32_t num_threads = thread_list.GetSize();
|
|
|
|
uint32_t thread_index;
|
2012-04-07 08:00:41 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
ts.Printf("<%u threads> ", num_threads);
|
2012-02-22 12:37:26 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
for (thread_index = 0; thread_index < num_threads; ++thread_index) {
|
|
|
|
Thread *thread = thread_list.GetThreadAtIndex(thread_index).get();
|
2015-03-04 05:51:25 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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(">");
|
|
|
|
}
|
2012-12-05 08:16:59 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
event_explanation = ts.GetData();
|
|
|
|
}
|
|
|
|
} while (0);
|
2013-11-06 07:28:00 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (event_explanation)
|
|
|
|
log->Printf("Process::RunThreadPlan(): execution interrupted: %s %s",
|
|
|
|
s.GetData(), event_explanation);
|
2014-01-11 07:46:59 +08:00
|
|
|
else
|
2016-09-07 04:57:50 +08:00
|
|
|
log->Printf("Process::RunThreadPlan(): execution interrupted: %s",
|
|
|
|
s.GetData());
|
|
|
|
}
|
LLDB AddressSanitizer instrumentation runtime plugin, breakpint on error and report data extraction
Reviewed at http://reviews.llvm.org/D5592
This patch gives LLDB some ability to interact with AddressSanitizer runtime library, on top of what we already have (historical memory stack traces provided by ASan). Namely, that's the ability to stop on an error caught by ASan, and access the report information that are associated with it. The report information is also exposed into SB API.
More precisely this patch...
adds a new plugin type, InstrumentationRuntime, which should serve as a generic superclass for other instrumentation runtime libraries, these plugins get notified when modules are loaded, so they get a chance to "activate" when a specific dynamic library is loaded
an instance of this plugin type, AddressSanitizerRuntime, which activates itself when it sees the ASan dynamic library or founds ASan statically linked in the executable
adds a collection of these plugins into the Process class
AddressSanitizerRuntime sets an internal breakpoint on __asan::AsanDie(), and when this breakpoint gets hit, it retrieves the report information from ASan
this breakpoint is then exposed as a new StopReason, eStopReasonInstrumentation, with a new StopInfo subclass, InstrumentationRuntimeStopInfo
the StopInfo superclass is extended with a m_extended_info field (it's a StructuredData::ObjectSP), that can hold arbitrary JSON-like data, which is the way the new plugin provides the report data
the "thread info" command now accepts a "-s" flag that prints out the JSON data of a stop reason (same way the "-j" flag works now)
SBThread has a new API, GetStopReasonExtendedInfoAsJSON, which dumps the JSON string into a SBStream
adds a test case for all of this
I plan to also get rid of the original ASan plugin (memory history stack traces) and use an instance of AddressSanitizerRuntime for that purpose.
Kuba
llvm-svn: 219546
2014-10-11 07:43:03 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (should_unwind) {
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::RunThreadPlan: ExecutionInterrupted - "
|
|
|
|
"discarding thread plans up to %p.",
|
|
|
|
static_cast<void *>(thread_plan_sp.get()));
|
|
|
|
thread->DiscardThreadPlansUpToPlan(thread_plan_sp);
|
|
|
|
} else {
|
|
|
|
if (log)
|
|
|
|
log->Printf("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);
|
|
|
|
}
|
|
|
|
}
|
2015-04-17 01:13:34 +08:00
|
|
|
}
|
2015-09-17 05:20:44 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// 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));
|
2016-08-19 12:21:48 +08:00
|
|
|
}
|
2014-09-06 09:33:13 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Also restore the current process'es selected frame & thread, since this
|
|
|
|
// function calling may
|
|
|
|
// be done behind the user's back.
|
2015-08-06 11:27:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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());
|
|
|
|
}
|
2015-08-06 11:27:10 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2015-08-06 11:27:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// If the process exited during the run of the thread plan, notify everyone.
|
2015-08-06 11:27:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (event_to_broadcast_sp) {
|
|
|
|
if (log)
|
|
|
|
log->PutCString("Process::RunThreadPlan(): rebroadcasting event.");
|
|
|
|
BroadcastEvent(event_to_broadcast_sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return return_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *Process::ExecutionResultAsCString(ExpressionResults result) {
|
|
|
|
const char *result_name;
|
|
|
|
|
|
|
|
switch (result) {
|
|
|
|
case eExpressionCompleted:
|
|
|
|
result_name = "eExpressionCompleted";
|
|
|
|
break;
|
|
|
|
case eExpressionDiscarded:
|
|
|
|
result_name = "eExpressionDiscarded";
|
|
|
|
break;
|
|
|
|
case eExpressionInterrupted:
|
|
|
|
result_name = "eExpressionInterrupted";
|
|
|
|
break;
|
|
|
|
case eExpressionHitBreakpoint:
|
|
|
|
result_name = "eExpressionHitBreakpoint";
|
|
|
|
break;
|
|
|
|
case eExpressionSetupError:
|
|
|
|
result_name = "eExpressionSetupError";
|
|
|
|
break;
|
|
|
|
case eExpressionParseError:
|
|
|
|
result_name = "eExpressionParseError";
|
|
|
|
break;
|
|
|
|
case eExpressionResultUnavailable:
|
|
|
|
result_name = "eExpressionResultUnavailable";
|
|
|
|
break;
|
|
|
|
case eExpressionTimedOut:
|
|
|
|
result_name = "eExpressionTimedOut";
|
|
|
|
break;
|
|
|
|
case eExpressionStoppedForDebug:
|
|
|
|
result_name = "eExpressionStoppedForDebug";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return result_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2016-11-09 04:36:40 +08:00
|
|
|
uint32_t num_frames_with_source,
|
|
|
|
bool stop_format) {
|
2016-09-07 04:57:50 +08:00
|
|
|
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->IsValid())
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
thread_sp->GetStatus(strm, start_frame, num_frames,
|
2016-11-09 04:36:40 +08:00
|
|
|
num_frames_with_source,
|
|
|
|
stop_format);
|
2016-09-07 04:57:50 +08:00
|
|
|
++num_thread_infos_dumped;
|
|
|
|
} else {
|
|
|
|
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::GetThreadStatus - thread 0x" PRIu64
|
|
|
|
" vanished while running Thread::GetStatus.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return num_thread_infos_dumped;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Process::AddInvalidMemoryRegion(const LoadRange ®ion) {
|
|
|
|
m_memory_cache.AddInvalidRange(region.GetRangeBase(), region.GetByteSize());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Process::RemoveInvalidMemoryRange(const LoadRange ®ion) {
|
|
|
|
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(); }
|
|
|
|
|
2016-10-21 06:50:00 +08:00
|
|
|
void Process::ClearPreResumeAction(PreResumeActionCallback callback, void *baton)
|
|
|
|
{
|
|
|
|
PreResumeCallbackAndBaton element(callback, baton);
|
|
|
|
auto found_iter = std::find(m_pre_resume_actions.begin(), m_pre_resume_actions.end(), element);
|
|
|
|
if (found_iter != m_pre_resume_actions.end())
|
|
|
|
{
|
|
|
|
m_pre_resume_actions.erase(found_iter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
ProcessRunLock &Process::GetRunLock() {
|
|
|
|
if (m_private_state_thread.EqualsThread(Host::GetCurrentThread()))
|
|
|
|
return m_private_run_lock;
|
|
|
|
else
|
|
|
|
return m_public_run_lock;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Process::DidExec() {
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s()", __FUNCTION__);
|
|
|
|
|
|
|
|
Target &target = GetTarget();
|
|
|
|
target.CleanupProcess();
|
|
|
|
target.ClearModules(false);
|
|
|
|
m_dynamic_checkers_ap.reset();
|
|
|
|
m_abi_sp.reset();
|
|
|
|
m_system_runtime_ap.reset();
|
|
|
|
m_os_ap.reset();
|
|
|
|
m_dyld_ap.reset();
|
|
|
|
m_jit_loaders_ap.reset();
|
|
|
|
m_image_tokens.clear();
|
|
|
|
m_allocated_memory_cache.Clear();
|
|
|
|
m_language_runtimes.clear();
|
|
|
|
m_instrumentation_runtimes.clear();
|
|
|
|
m_thread_list.DiscardThreadPlans();
|
|
|
|
m_memory_cache.Clear(true);
|
|
|
|
m_stop_info_override_callback = nullptr;
|
|
|
|
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, Error &error) {
|
|
|
|
if (address == nullptr) {
|
|
|
|
error.SetErrorString("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 (!InferiorCall(this, address, function_addr)) {
|
|
|
|
Symbol *symbol = address->CalculateSymbolContextSymbol();
|
|
|
|
error.SetErrorStringWithFormat(
|
|
|
|
"Unable to call resolver for indirect function %s",
|
|
|
|
symbol ? symbol->GetName().AsCString() : "<UNKNOWN>");
|
|
|
|
function_addr = LLDB_INVALID_ADDRESS;
|
|
|
|
} else {
|
|
|
|
m_resolved_indirect_addresses.insert(
|
|
|
|
std::pair<addr_t, addr_t>(addr, function_addr));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return function_addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Process::ModulesDidLoad(ModuleList &module_list) {
|
|
|
|
SystemRuntime *sys_runtime = GetSystemRuntime();
|
|
|
|
if (sys_runtime) {
|
|
|
|
sys_runtime->ModulesDidLoad(module_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
GetJITLoaders().ModulesDidLoad(module_list);
|
|
|
|
|
|
|
|
// Give runtimes a chance to be created.
|
|
|
|
InstrumentationRuntime::ModulesDidLoad(module_list, this,
|
|
|
|
m_instrumentation_runtimes);
|
|
|
|
|
|
|
|
// Tell runtimes about new modules.
|
|
|
|
for (auto pos = m_instrumentation_runtimes.begin();
|
|
|
|
pos != m_instrumentation_runtimes.end(); ++pos) {
|
|
|
|
InstrumentationRuntimeSP runtime = pos->second;
|
|
|
|
runtime->ModulesDidLoad(module_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Let any language runtimes we have already created know
|
|
|
|
// about the modules that loaded.
|
|
|
|
|
|
|
|
// Iterate over a copy of this language runtime list in case
|
|
|
|
// the language runtime ModulesDidLoad somehow causes the language
|
|
|
|
// riuntime to be unloaded.
|
|
|
|
LanguageRuntimeCollection language_runtimes(m_language_runtimes);
|
|
|
|
for (const auto &pair : language_runtimes) {
|
|
|
|
// We must check language_runtime_sp to make sure it is not
|
|
|
|
// nullptr as we might cache the fact that we didn't have a
|
|
|
|
// language runtime for a language.
|
|
|
|
LanguageRuntimeSP language_runtime_sp = pair.second;
|
|
|
|
if (language_runtime_sp)
|
|
|
|
language_runtime_sp->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_ap)
|
|
|
|
LoadOperatingSystemPlugin(false);
|
|
|
|
|
|
|
|
// Give structured-data plugins a chance to see the modified modules.
|
|
|
|
for (auto pair : m_structured_data_plugin_map) {
|
|
|
|
if (pair.second)
|
|
|
|
pair.second->ModulesDidLoad(*this, module_list);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Process::PrintWarning(uint64_t warning_type, const void *repeat_key,
|
|
|
|
const char *fmt, ...) {
|
|
|
|
bool print_warning = true;
|
|
|
|
|
|
|
|
StreamSP stream_sp = GetTarget().GetDebugger().GetAsyncOutputStream();
|
|
|
|
if (!stream_sp)
|
|
|
|
return;
|
|
|
|
if (warning_type == eWarningsOptimization && !GetWarningsOptimization()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (repeat_key != nullptr) {
|
|
|
|
WarningsCollection::iterator it = m_warnings_issued.find(warning_type);
|
|
|
|
if (it == m_warnings_issued.end()) {
|
|
|
|
m_warnings_issued[warning_type] = WarningsPointerSet();
|
|
|
|
m_warnings_issued[warning_type].insert(repeat_key);
|
|
|
|
} else {
|
|
|
|
if (it->second.find(repeat_key) != it->second.end()) {
|
|
|
|
print_warning = false;
|
|
|
|
} else {
|
|
|
|
it->second.insert(repeat_key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (print_warning) {
|
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
stream_sp->PrintfVarArg(fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Process::PrintWarningOptimization(const SymbolContext &sc) {
|
|
|
|
if (GetWarningsOptimization() && sc.module_sp &&
|
|
|
|
!sc.module_sp->GetFileSpec().GetFilename().IsEmpty() && sc.function &&
|
|
|
|
sc.function->GetIsOptimized()) {
|
|
|
|
PrintWarning(Process::Warnings::eWarningsOptimization, sc.module_sp.get(),
|
|
|
|
"%s was compiled with optimization - stepping may behave "
|
|
|
|
"oddly; variables may not be available.\n",
|
|
|
|
sc.module_sp->GetFileSpec().GetFilename().GetCString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Process::GetProcessInfo(ProcessInstanceInfo &info) {
|
|
|
|
info.Clear();
|
|
|
|
|
|
|
|
PlatformSP platform_sp = GetTarget().GetPlatform();
|
|
|
|
if (!platform_sp)
|
|
|
|
return false;
|
2015-08-06 11:27:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
return platform_sp->GetProcessInfo(GetID(), info);
|
2015-08-10 15:55:25 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
ThreadCollectionSP Process::GetHistoryThreads(lldb::addr_t addr) {
|
|
|
|
ThreadCollectionSP threads;
|
2016-04-05 21:07:16 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const MemoryHistorySP &memory_history =
|
|
|
|
MemoryHistory::FindPlugin(shared_from_this());
|
2016-04-05 21:07:16 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (!memory_history) {
|
|
|
|
return threads;
|
|
|
|
}
|
2016-04-05 21:07:16 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
threads.reset(new ThreadCollection(memory_history->GetHistoryThreads(addr)));
|
2015-04-17 01:13:34 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
return threads;
|
2014-09-06 09:33:13 +08:00
|
|
|
}
|
2014-10-11 09:59:32 +08:00
|
|
|
|
|
|
|
InstrumentationRuntimeSP
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
2014-10-11 09:59:32 +08:00
|
|
|
}
|
2015-03-24 19:15:23 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Process::GetModuleSpec(const FileSpec &module_file_spec,
|
|
|
|
const ArchSpec &arch, ModuleSpec &module_spec) {
|
|
|
|
module_spec.Clear();
|
|
|
|
return false;
|
2015-03-24 19:15:23 +08:00
|
|
|
}
|
2015-12-02 19:58:51 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
size_t Process::AddImageToken(lldb::addr_t image_ptr) {
|
|
|
|
m_image_tokens.push_back(image_ptr);
|
|
|
|
return m_image_tokens.size() - 1;
|
2015-12-02 19:58:51 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
2015-12-02 19:58:51 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::ResetImageToken(size_t token) {
|
|
|
|
if (token < m_image_tokens.size())
|
|
|
|
m_image_tokens[token] = LLDB_INVALID_IMAGE_TOKEN;
|
2015-12-04 07:53:45 +08:00
|
|
|
}
|
2016-01-09 05:40:11 +08:00
|
|
|
|
|
|
|
Address
|
2016-09-07 04:57:50 +08:00
|
|
|
Process::AdvanceAddressToNextBranchInstruction(Address default_stop_addr,
|
|
|
|
AddressRange range_bounds) {
|
|
|
|
Target &target = GetTarget();
|
|
|
|
DisassemblerSP disassembler_sp;
|
|
|
|
InstructionList *insn_list = nullptr;
|
2016-01-09 05:40:11 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Address retval = default_stop_addr;
|
2016-01-09 05:40:11 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (!target.GetUseFastStepping())
|
|
|
|
return retval;
|
|
|
|
if (!default_stop_addr.IsValid())
|
|
|
|
return retval;
|
2016-01-09 05:40:11 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
ExecutionContext exe_ctx(this);
|
|
|
|
const char *plugin_name = nullptr;
|
|
|
|
const char *flavor = nullptr;
|
|
|
|
const bool prefer_file_cache = true;
|
|
|
|
disassembler_sp = Disassembler::DisassembleRange(
|
|
|
|
target.GetArchitecture(), plugin_name, flavor, exe_ctx, range_bounds,
|
|
|
|
prefer_file_cache);
|
|
|
|
if (disassembler_sp)
|
|
|
|
insn_list = &disassembler_sp->GetInstructionList();
|
|
|
|
|
|
|
|
if (insn_list == nullptr) {
|
|
|
|
return retval;
|
|
|
|
}
|
2016-01-09 05:40:11 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
size_t insn_offset =
|
|
|
|
insn_list->GetIndexOfInstructionAtAddress(default_stop_addr);
|
|
|
|
if (insn_offset == UINT32_MAX) {
|
2016-01-09 05:40:11 +08:00
|
|
|
return retval;
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2016-07-07 16:21:28 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
uint32_t branch_index =
|
|
|
|
insn_list->GetIndexOfNextBranchInstruction(insn_offset, target);
|
|
|
|
if (branch_index == UINT32_MAX) {
|
|
|
|
return retval;
|
|
|
|
}
|
2016-07-07 16:21:28 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2016-07-07 16:21:28 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
return retval;
|
|
|
|
}
|
2016-07-07 16:21:28 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error Process::GetMemoryRegions(
|
|
|
|
std::vector<lldb::MemoryRegionInfoSP> ®ion_list) {
|
2016-07-07 16:21:28 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error error;
|
2016-08-19 12:21:48 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
lldb::addr_t range_end = 0;
|
2016-08-19 12:21:48 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
region_list.clear();
|
|
|
|
do {
|
|
|
|
lldb::MemoryRegionInfoSP region_info(new lldb_private::MemoryRegionInfo());
|
|
|
|
error = GetMemoryRegionInfo(range_end, *region_info);
|
|
|
|
// GetMemoryRegionInfo should only return an error if it is unimplemented.
|
|
|
|
if (error.Fail()) {
|
|
|
|
region_list.clear();
|
|
|
|
break;
|
|
|
|
}
|
2016-08-19 12:21:48 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
range_end = region_info->GetRange().GetRangeEnd();
|
|
|
|
if (region_info->GetMapped() == MemoryRegionInfo::eYes) {
|
|
|
|
region_list.push_back(region_info);
|
2016-08-19 12:21:48 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
} while (range_end != LLDB_INVALID_ADDRESS);
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
Error Process::ConfigureStructuredData(
|
|
|
|
const ConstString &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 Error("unimplemented");
|
|
|
|
}
|
2016-08-19 12:21:48 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Process::MapSupportedStructuredDataPlugins(
|
|
|
|
const StructuredData::Array &supported_type_names) {
|
|
|
|
Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
|
2016-08-19 12:21:48 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Bail out early if there are no type names to map.
|
|
|
|
if (supported_type_names.GetSize() == 0) {
|
2016-08-19 12:21:48 +08:00
|
|
|
if (log)
|
2016-09-07 04:57:50 +08:00
|
|
|
log->Printf("Process::%s(): no structured data types supported",
|
|
|
|
__FUNCTION__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert StructuredData type names to ConstString instances.
|
|
|
|
std::set<ConstString> const_type_names;
|
|
|
|
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s(): the process supports the following async "
|
|
|
|
"structured data types:",
|
|
|
|
__FUNCTION__);
|
|
|
|
|
|
|
|
supported_type_names.ForEach(
|
|
|
|
[&const_type_names, &log](StructuredData::Object *object) {
|
|
|
|
if (!object) {
|
|
|
|
// Invalid - shouldn't be null objects in the array.
|
|
|
|
return false;
|
2016-08-19 12:21:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
auto type_name = object->GetAsString();
|
2016-09-07 04:57:50 +08:00
|
|
|
if (!type_name) {
|
|
|
|
// Invalid format - all type names should be strings.
|
|
|
|
return false;
|
2016-08-19 12:21:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const_type_names.insert(ConstString(type_name->GetValue()));
|
|
|
|
if (log)
|
2016-09-07 04:57:50 +08:00
|
|
|
log->Printf("- %s", type_name->GetValue().c_str());
|
2016-08-19 12:21:48 +08:00
|
|
|
return true;
|
2016-09-07 04:57:50 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
// For each StructuredDataPlugin, if the plugin handles any of the
|
|
|
|
// types in the supported_type_names, map that type name to that plugin.
|
|
|
|
uint32_t plugin_index = 0;
|
|
|
|
for (auto create_instance =
|
|
|
|
PluginManager::GetStructuredDataPluginCreateCallbackAtIndex(
|
|
|
|
plugin_index);
|
|
|
|
create_instance && !const_type_names.empty(); ++plugin_index) {
|
|
|
|
// 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<ConstString> names_to_remove;
|
|
|
|
for (auto &type_name : const_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);
|
|
|
|
if (log)
|
|
|
|
log->Printf("Process::%s(): using plugin %s for type name "
|
|
|
|
"%s",
|
|
|
|
__FUNCTION__, plugin_sp->GetPluginName().GetCString(),
|
|
|
|
type_name.GetCString());
|
|
|
|
}
|
|
|
|
}
|
2016-08-19 12:21:48 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Remove the type names that were consumed by this plugin.
|
|
|
|
for (auto &type_name : names_to_remove)
|
|
|
|
const_type_names.erase(type_name);
|
|
|
|
}
|
|
|
|
}
|
2016-08-19 12:21:48 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Process::RouteAsyncStructuredData(
|
|
|
|
const StructuredData::ObjectSP object_sp) {
|
|
|
|
// Nothing to do if there's no data.
|
|
|
|
if (!object_sp)
|
|
|
|
return false;
|
2016-07-07 16:21:28 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// 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;
|
2016-08-19 12:21:48 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Grab the async structured type name (i.e. the feature/plugin name).
|
|
|
|
ConstString type_name;
|
|
|
|
if (!dictionary->GetValueForKeyAsString("type", type_name))
|
|
|
|
return false;
|
2016-08-19 12:21:48 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// 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;
|
2016-07-07 16:21:28 +08:00
|
|
|
}
|