llvm-project/lldb/source/Target/Platform.cpp

1933 lines
66 KiB
C++
Raw Normal View History

LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
//===-- Platform.cpp --------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
//
//===----------------------------------------------------------------------===//
#include <algorithm>
#include <csignal>
#include <fstream>
#include <memory>
#include <vector>
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "lldb/Breakpoint/BreakpointIDList.h"
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.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/ModuleSpec.h"
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Host/FileSystem.h"
Added more platform support. There are now some new commands: platform status -- gets status information for the selected platform platform create <platform-name> -- creates a new instance of a remote platform platform list -- list all available platforms platform select -- select a platform instance as the current platform (not working yet) When using "platform create" it will create a remote platform and make it the selected platform. For instances for iPhone OS debugging on Mac OS X one can do: (lldb) platform create remote-ios --sdk-version=4.0 Remote platform: iOS platform SDK version: 4.0 SDK path: "/Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0" Not connected to a remote device. (lldb) file ~/Documents/a.out Current executable set to '~/Documents/a.out' (armv6). (lldb) image list [ 0] /Volumes/work/gclayton/Documents/devb/attach/a.out [ 1] /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0/Symbols/usr/lib/dyld [ 2] /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0/Symbols/usr/lib/libSystem.B.dylib Note that this is all happening prior to running _or_ connecting to a remote platform. Once connected to a remote platform the OS version might change which means we will need to update our dependecies. Also once we run, we will need to match up the actualy binaries with the actualy UUID's to files in the SDK, or download and cache them locally. This is just the start of the remote platforms, but this modification is the first iteration in getting the platforms really doing something. llvm-svn: 127934
2011-03-19 09:12:21 +08:00
#include "lldb/Host/Host.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Host/OptionParser.h"
#include "lldb/Interpreter/OptionValueProperties.h"
#include "lldb/Interpreter/Property.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Target/ModuleCache.h"
#include "lldb/Target/Platform.h"
Moved the execution context that was in the Debugger into the CommandInterpreter where it was always being used. Make sure that Modules can track their object file offsets correctly to allow opening of sub object files (like the "__commpage" on darwin). Modified the Platforms to be able to launch processes. The first part of this move is the platform soon will become the entity that launches your program and when it does, it uses a new ProcessLaunchInfo class which encapsulates all process launching settings. This simplifies the internal APIs needed for launching. I want to slowly phase out process launching from the process classes, so for now we can still launch just as we used to, but eventually the platform is the object that should do the launching. Modified the Host::LaunchProcess in the MacOSX Host.mm to correctly be able to launch processes with all of the new eLaunchFlag settings. Modified any code that was manually launching processes to use the Host::LaunchProcess functions. Fixed an issue where lldb_private::Args had implicitly defined copy constructors that could do the wrong thing. This has now been fixed by adding an appropriate copy constructor and assignment operator. Make sure we don't add empty ModuleSP entries to a module list. Fixed the commpage module creation on MacOSX, but we still need to train the MacOSX dynamic loader to not get rid of it when it doesn't have an entry in the all image infos. Abstracted many more calls from in ProcessGDBRemote down into the GDBRemoteCommunicationClient subclass to make the classes cleaner and more efficient. Fixed the default iOS ARM register context to be correct and also added support for targets that don't support the qThreadStopInfo packet by selecting the current thread (only if needed) and then sending a stop reply packet. Debugserver can now start up with a --unix-socket (-u for short) and can then bind to port zero and send the port it bound to to a listening process on the other end. This allows the GDB remote platform to spawn new GDB server instances (debugserver) to allow platform debugging. llvm-svn: 129351
2011-04-12 13:54:46 +08:00
#include "lldb/Target/Process.h"
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
#include "lldb/Target/Target.h"
#include "lldb/Target/UnixSignals.h"
#include "lldb/Utility/DataBufferHeap.h"
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/Status.h"
#include "lldb/Utility/StructuredData.h"
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
#include "llvm/Support/FileSystem.h"
// Define these constants from POSIX mman.h rather than include the file so
// that they will be correct even when compiled on Linux.
#define MAP_PRIVATE 2
#define MAP_ANON 0x1000
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
using namespace lldb;
using namespace lldb_private;
static uint32_t g_initialize_count = 0;
// Use a singleton function for g_local_platform_sp to avoid init constructors
// since LLDB is often part of a shared library
static PlatformSP &GetHostPlatformSP() {
static PlatformSP g_platform_sp;
return g_platform_sp;
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
}
const char *Platform::GetHostPlatformName() { return "host"; }
namespace {
static constexpr PropertyDefinition g_properties[] = {
{"use-module-cache", OptionValue::eTypeBoolean, true, true, nullptr,
{}, "Use module cache."},
{"module-cache-directory", OptionValue::eTypeFileSpec, true, 0, nullptr,
{}, "Root directory for cached modules."}};
enum { ePropertyUseModuleCache, ePropertyModuleCacheDirectory };
} // namespace
ConstString PlatformProperties::GetSettingName() {
static ConstString g_setting_name("platform");
return g_setting_name;
}
PlatformProperties::PlatformProperties() {
m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
m_collection_sp->Initialize(g_properties);
auto module_cache_dir = GetModuleCacheDirectory();
if (module_cache_dir)
return;
llvm::SmallString<64> user_home_dir;
if (!llvm::sys::path::home_directory(user_home_dir))
return;
module_cache_dir = FileSpec(user_home_dir.c_str());
module_cache_dir.AppendPathComponent(".lldb");
module_cache_dir.AppendPathComponent("module_cache");
SetModuleCacheDirectory(module_cache_dir);
}
bool PlatformProperties::GetUseModuleCache() const {
const auto idx = ePropertyUseModuleCache;
return m_collection_sp->GetPropertyAtIndexAsBoolean(
nullptr, idx, g_properties[idx].default_uint_value != 0);
}
bool PlatformProperties::SetUseModuleCache(bool use_module_cache) {
return m_collection_sp->SetPropertyAtIndexAsBoolean(
nullptr, ePropertyUseModuleCache, use_module_cache);
}
FileSpec PlatformProperties::GetModuleCacheDirectory() const {
return m_collection_sp->GetPropertyAtIndexAsFileSpec(
nullptr, ePropertyModuleCacheDirectory);
}
bool PlatformProperties::SetModuleCacheDirectory(const FileSpec &dir_spec) {
return m_collection_sp->SetPropertyAtIndexAsFileSpec(
nullptr, ePropertyModuleCacheDirectory, dir_spec);
}
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
//------------------------------------------------------------------
/// Get the native host platform plug-in.
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
///
/// There should only be one of these for each host that LLDB runs
/// upon that should be statically compiled in and registered using
/// preprocessor macros or other similar build mechanisms.
///
/// This platform will be used as the default platform when launching
/// or attaching to processes unless another platform is specified.
//------------------------------------------------------------------
PlatformSP Platform::GetHostPlatform() { return GetHostPlatformSP(); }
static std::vector<PlatformSP> &GetPlatformList() {
static std::vector<PlatformSP> g_platform_list;
return g_platform_list;
}
static std::recursive_mutex &GetPlatformListMutex() {
static std::recursive_mutex g_mutex;
return g_mutex;
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
}
void Platform::Initialize() { g_initialize_count++; }
void Platform::Terminate() {
if (g_initialize_count > 0) {
if (--g_initialize_count == 0) {
std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex());
GetPlatformList().clear();
}
}
}
const PlatformPropertiesSP &Platform::GetGlobalPlatformProperties() {
static const auto g_settings_sp(std::make_shared<PlatformProperties>());
return g_settings_sp;
}
void Platform::SetHostPlatform(const lldb::PlatformSP &platform_sp) {
// The native platform should use its static void Platform::Initialize()
// function to register itself as the native platform.
GetHostPlatformSP() = platform_sp;
if (platform_sp) {
std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex());
GetPlatformList().push_back(platform_sp);
}
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
}
Status Platform::GetFileWithUUID(const FileSpec &platform_file,
const UUID *uuid_ptr, FileSpec &local_file) {
// Default to the local case
local_file = platform_file;
return Status();
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
}
FileSpecList
Platform::LocateExecutableScriptingResources(Target *target, Module &module,
Stream *feedback_stream) {
return FileSpecList();
}
// PlatformSP
// Platform::FindPlugin (Process *process, const ConstString &plugin_name)
//{
// PlatformCreateInstance create_callback = nullptr;
// if (plugin_name)
// {
// create_callback =
// PluginManager::GetPlatformCreateCallbackForPluginName (plugin_name);
// if (create_callback)
// {
// ArchSpec arch;
// if (process)
// {
// arch = process->GetTarget().GetArchitecture();
// }
// PlatformSP platform_sp(create_callback(process, &arch));
// if (platform_sp)
// return platform_sp;
// }
// }
// else
// {
// for (uint32_t idx = 0; (create_callback =
// PluginManager::GetPlatformCreateCallbackAtIndex(idx)) != nullptr;
// ++idx)
// {
// PlatformSP platform_sp(create_callback(process, nullptr));
// if (platform_sp)
// return platform_sp;
// }
// }
// return PlatformSP();
//}
Status Platform::GetSharedModule(const ModuleSpec &module_spec,
Process *process, ModuleSP &module_sp,
const FileSpecList *module_search_paths_ptr,
ModuleSP *old_module_sp_ptr,
bool *did_create_ptr) {
if (IsHost())
return ModuleList::GetSharedModule(
module_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr,
did_create_ptr, false);
// Module resolver lambda.
auto resolver = [&](const ModuleSpec &spec) {
Status error(eErrorTypeGeneric);
ModuleSpec resolved_spec;
// Check if we have sysroot set.
if (m_sdk_sysroot) {
// Prepend sysroot to module spec.
resolved_spec = spec;
resolved_spec.GetFileSpec().PrependPathComponent(
m_sdk_sysroot.GetStringRef());
// Try to get shared module with resolved spec.
error = ModuleList::GetSharedModule(
resolved_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr,
did_create_ptr, false);
}
// If we don't have sysroot or it didn't work then
// try original module spec.
if (!error.Success()) {
resolved_spec = spec;
error = ModuleList::GetSharedModule(
resolved_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr,
did_create_ptr, false);
}
if (error.Success() && module_sp)
module_sp->SetPlatformFileSpec(resolved_spec.GetFileSpec());
return error;
};
return GetRemoteSharedModule(module_spec, process, module_sp, resolver,
did_create_ptr);
}
bool Platform::GetModuleSpec(const FileSpec &module_file_spec,
const ArchSpec &arch, ModuleSpec &module_spec) {
ModuleSpecList module_specs;
if (ObjectFile::GetModuleSpecifications(module_file_spec, 0, 0,
module_specs) == 0)
return false;
ModuleSpec matched_module_spec;
return module_specs.FindMatchingModuleSpec(ModuleSpec(module_file_spec, arch),
module_spec);
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
}
PlatformSP Platform::Find(const ConstString &name) {
if (name) {
static ConstString g_host_platform_name("host");
if (name == g_host_platform_name)
return GetHostPlatform();
std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex());
for (const auto &platform_sp : GetPlatformList()) {
if (platform_sp->GetName() == name)
return platform_sp;
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
}
}
return PlatformSP();
}
PlatformSP Platform::Create(const ConstString &name, Status &error) {
PlatformCreateInstance create_callback = nullptr;
lldb::PlatformSP platform_sp;
if (name) {
static ConstString g_host_platform_name("host");
if (name == g_host_platform_name)
return GetHostPlatform();
create_callback =
PluginManager::GetPlatformCreateCallbackForPluginName(name);
if (create_callback)
platform_sp = create_callback(true, nullptr);
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
else
error.SetErrorStringWithFormat(
"unable to find a plug-in for the platform named \"%s\"",
name.GetCString());
} else
error.SetErrorString("invalid platform name");
if (platform_sp) {
std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex());
GetPlatformList().push_back(platform_sp);
}
return platform_sp;
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
}
PlatformSP Platform::Create(const ArchSpec &arch, ArchSpec *platform_arch_ptr,
Status &error) {
lldb::PlatformSP platform_sp;
if (arch.IsValid()) {
// Scope for locker
{
// First try exact arch matches across all platforms already created
std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex());
for (const auto &platform_sp : GetPlatformList()) {
if (platform_sp->IsCompatibleArchitecture(arch, true,
platform_arch_ptr))
return platform_sp;
}
// Next try compatible arch matches across all platforms already created
for (const auto &platform_sp : GetPlatformList()) {
if (platform_sp->IsCompatibleArchitecture(arch, false,
platform_arch_ptr))
return platform_sp;
}
}
PlatformCreateInstance create_callback;
// First try exact arch matches across all platform plug-ins
uint32_t idx;
for (idx = 0; (create_callback =
PluginManager::GetPlatformCreateCallbackAtIndex(idx));
++idx) {
if (create_callback) {
platform_sp = create_callback(false, &arch);
if (platform_sp &&
platform_sp->IsCompatibleArchitecture(arch, true,
platform_arch_ptr)) {
std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex());
GetPlatformList().push_back(platform_sp);
return platform_sp;
}
}
}
// Next try compatible arch matches across all platform plug-ins
for (idx = 0; (create_callback =
PluginManager::GetPlatformCreateCallbackAtIndex(idx));
++idx) {
if (create_callback) {
platform_sp = create_callback(false, &arch);
if (platform_sp &&
platform_sp->IsCompatibleArchitecture(arch, false,
platform_arch_ptr)) {
std::lock_guard<std::recursive_mutex> guard(GetPlatformListMutex());
GetPlatformList().push_back(platform_sp);
return platform_sp;
}
}
}
} else
error.SetErrorString("invalid platform name");
if (platform_arch_ptr)
platform_arch_ptr->Clear();
platform_sp.reset();
return platform_sp;
}
ArchSpec Platform::GetAugmentedArchSpec(Platform *platform, llvm::StringRef triple) {
if (platform)
return platform->GetAugmentedArchSpec(triple);
return HostInfo::GetAugmentedArchSpec(triple);
}
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
//------------------------------------------------------------------
/// Default Constructor
//------------------------------------------------------------------
Platform::Platform(bool is_host)
: m_is_host(is_host), m_os_version_set_while_connected(false),
m_system_arch_set_while_connected(false), m_sdk_sysroot(), m_sdk_build(),
m_working_dir(), m_remote_url(), m_name(), m_system_arch(), m_mutex(),
m_max_uid_name_len(0), m_max_gid_name_len(0), m_supports_rsync(false),
m_rsync_opts(), m_rsync_prefix(), m_supports_ssh(false), m_ssh_opts(),
m_ignores_remote_hostname(false), m_trap_handlers(),
m_calculated_trap_handlers(false),
m_module_cache(llvm::make_unique<ModuleCache>()) {
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
if (log)
log->Printf("%p Platform::Platform()", static_cast<void *>(this));
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
}
//------------------------------------------------------------------
/// Destructor.
///
/// The destructor is virtual since this class is designed to be
/// inherited from by the plug-in instance.
//------------------------------------------------------------------
Platform::~Platform() {
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT));
if (log)
log->Printf("%p Platform::~Platform()", static_cast<void *>(this));
}
void Platform::GetStatus(Stream &strm) {
std::string s;
strm.Printf(" Platform: %s\n", GetPluginName().GetCString());
ArchSpec arch(GetSystemArchitecture());
if (arch.IsValid()) {
if (!arch.GetTriple().str().empty()) {
strm.Printf(" Triple: ");
arch.DumpTriple(strm);
strm.EOL();
}
}
llvm::VersionTuple os_version = GetOSVersion();
if (!os_version.empty()) {
strm.Format("OS Version: {0}", os_version.getAsString());
if (GetOSBuildString(s))
strm.Printf(" (%s)", s.c_str());
strm.EOL();
}
if (GetOSKernelDescription(s))
strm.Printf(" Kernel: %s\n", s.c_str());
if (IsHost()) {
strm.Printf(" Hostname: %s\n", GetHostname());
} else {
const bool is_connected = IsConnected();
if (is_connected)
strm.Printf(" Hostname: %s\n", GetHostname());
strm.Printf(" Connected: %s\n", is_connected ? "yes" : "no");
}
if (GetWorkingDirectory()) {
strm.Printf("WorkingDir: %s\n", GetWorkingDirectory().GetCString());
}
if (!IsConnected())
return;
std::string specific_info(GetPlatformSpecificConnectionInformation());
if (!specific_info.empty())
strm.Printf("Platform-specific connection: %s\n", specific_info.c_str());
}
llvm::VersionTuple Platform::GetOSVersion(Process *process) {
std::lock_guard<std::mutex> guard(m_mutex);
if (IsHost()) {
if (m_os_version.empty()) {
// We have a local host platform
m_os_version = HostInfo::GetOSVersion();
m_os_version_set_while_connected = !m_os_version.empty();
}
} else {
// We have a remote platform. We can only fetch the remote
// OS version if we are connected, and we don't want to do it
// more than once.
const bool is_connected = IsConnected();
bool fetch = false;
if (!m_os_version.empty()) {
// We have valid OS version info, check to make sure it wasn't manually
// set prior to connecting. If it was manually set prior to connecting,
// then lets fetch the actual OS version info if we are now connected.
if (is_connected && !m_os_version_set_while_connected)
fetch = true;
} else {
// We don't have valid OS version info, fetch it if we are connected
fetch = is_connected;
}
if (fetch)
m_os_version_set_while_connected = GetRemoteOSVersion();
}
Added more platform support. There are now some new commands: platform status -- gets status information for the selected platform platform create <platform-name> -- creates a new instance of a remote platform platform list -- list all available platforms platform select -- select a platform instance as the current platform (not working yet) When using "platform create" it will create a remote platform and make it the selected platform. For instances for iPhone OS debugging on Mac OS X one can do: (lldb) platform create remote-ios --sdk-version=4.0 Remote platform: iOS platform SDK version: 4.0 SDK path: "/Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0" Not connected to a remote device. (lldb) file ~/Documents/a.out Current executable set to '~/Documents/a.out' (armv6). (lldb) image list [ 0] /Volumes/work/gclayton/Documents/devb/attach/a.out [ 1] /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0/Symbols/usr/lib/dyld [ 2] /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0/Symbols/usr/lib/libSystem.B.dylib Note that this is all happening prior to running _or_ connecting to a remote platform. Once connected to a remote platform the OS version might change which means we will need to update our dependecies. Also once we run, we will need to match up the actualy binaries with the actualy UUID's to files in the SDK, or download and cache them locally. This is just the start of the remote platforms, but this modification is the first iteration in getting the platforms really doing something. llvm-svn: 127934
2011-03-19 09:12:21 +08:00
if (!m_os_version.empty())
return m_os_version;
if (process) {
// Check with the process in case it can answer the question if a process
// was provided
return process->GetHostOSVersion();
}
return llvm::VersionTuple();
Added more platform support. There are now some new commands: platform status -- gets status information for the selected platform platform create <platform-name> -- creates a new instance of a remote platform platform list -- list all available platforms platform select -- select a platform instance as the current platform (not working yet) When using "platform create" it will create a remote platform and make it the selected platform. For instances for iPhone OS debugging on Mac OS X one can do: (lldb) platform create remote-ios --sdk-version=4.0 Remote platform: iOS platform SDK version: 4.0 SDK path: "/Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0" Not connected to a remote device. (lldb) file ~/Documents/a.out Current executable set to '~/Documents/a.out' (armv6). (lldb) image list [ 0] /Volumes/work/gclayton/Documents/devb/attach/a.out [ 1] /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0/Symbols/usr/lib/dyld [ 2] /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0/Symbols/usr/lib/libSystem.B.dylib Note that this is all happening prior to running _or_ connecting to a remote platform. Once connected to a remote platform the OS version might change which means we will need to update our dependecies. Also once we run, we will need to match up the actualy binaries with the actualy UUID's to files in the SDK, or download and cache them locally. This is just the start of the remote platforms, but this modification is the first iteration in getting the platforms really doing something. llvm-svn: 127934
2011-03-19 09:12:21 +08:00
}
bool Platform::GetOSBuildString(std::string &s) {
s.clear();
if (IsHost())
#if !defined(__linux__)
return HostInfo::GetOSBuildString(s);
#else
return false;
#endif
else
return GetRemoteOSBuildString(s);
}
bool Platform::GetOSKernelDescription(std::string &s) {
if (IsHost())
#if !defined(__linux__)
return HostInfo::GetOSKernelDescription(s);
#else
return false;
#endif
else
return GetRemoteOSKernelDescription(s);
}
void Platform::AddClangModuleCompilationOptions(
Target *target, std::vector<std::string> &options) {
std::vector<std::string> default_compilation_options = {
"-x", "c++", "-Xclang", "-nostdsysteminc", "-Xclang", "-nostdsysteminc"};
options.insert(options.end(), default_compilation_options.begin(),
default_compilation_options.end());
}
FileSpec Platform::GetWorkingDirectory() {
if (IsHost()) {
llvm::SmallString<64> cwd;
if (llvm::sys::fs::current_path(cwd))
return {};
else {
FileSpec file_spec(cwd);
FileSystem::Instance().Resolve(file_spec);
return file_spec;
}
} else {
if (!m_working_dir)
m_working_dir = GetRemoteWorkingDirectory();
return m_working_dir;
}
}
struct RecurseCopyBaton {
const FileSpec &dst;
Platform *platform_ptr;
Status error;
};
static FileSystem::EnumerateDirectoryResult
RecurseCopy_Callback(void *baton, llvm::sys::fs::file_type ft,
llvm::StringRef path) {
RecurseCopyBaton *rc_baton = (RecurseCopyBaton *)baton;
FileSpec src(path);
namespace fs = llvm::sys::fs;
switch (ft) {
case fs::file_type::fifo_file:
case fs::file_type::socket_file:
// we have no way to copy pipes and sockets - ignore them and continue
return FileSystem::eEnumerateDirectoryResultNext;
break;
case fs::file_type::directory_file: {
// make the new directory and get in there
FileSpec dst_dir = rc_baton->dst;
if (!dst_dir.GetFilename())
dst_dir.GetFilename() = src.GetLastPathComponent();
Status error = rc_baton->platform_ptr->MakeDirectory(
dst_dir, lldb::eFilePermissionsDirectoryDefault);
if (error.Fail()) {
rc_baton->error.SetErrorStringWithFormat(
"unable to setup directory %s on remote end", dst_dir.GetCString());
return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out
}
// now recurse
std::string src_dir_path(src.GetPath());
// Make a filespec that only fills in the directory of a FileSpec so when
// we enumerate we can quickly fill in the filename for dst copies
FileSpec recurse_dst;
recurse_dst.GetDirectory().SetCString(dst_dir.GetPath().c_str());
RecurseCopyBaton rc_baton2 = {recurse_dst, rc_baton->platform_ptr,
Status()};
FileSystem::Instance().EnumerateDirectory(src_dir_path, true, true, true,
RecurseCopy_Callback, &rc_baton2);
if (rc_baton2.error.Fail()) {
rc_baton->error.SetErrorString(rc_baton2.error.AsCString());
return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out
}
return FileSystem::eEnumerateDirectoryResultNext;
} break;
case fs::file_type::symlink_file: {
// copy the file and keep going
FileSpec dst_file = rc_baton->dst;
if (!dst_file.GetFilename())
dst_file.GetFilename() = src.GetFilename();
FileSpec src_resolved;
rc_baton->error = FileSystem::Instance().Readlink(src, src_resolved);
if (rc_baton->error.Fail())
return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out
rc_baton->error =
rc_baton->platform_ptr->CreateSymlink(dst_file, src_resolved);
if (rc_baton->error.Fail())
return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out
return FileSystem::eEnumerateDirectoryResultNext;
} break;
case fs::file_type::regular_file: {
// copy the file and keep going
FileSpec dst_file = rc_baton->dst;
if (!dst_file.GetFilename())
dst_file.GetFilename() = src.GetFilename();
Status err = rc_baton->platform_ptr->PutFile(src, dst_file);
if (err.Fail()) {
rc_baton->error.SetErrorString(err.AsCString());
return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out
}
return FileSystem::eEnumerateDirectoryResultNext;
} break;
default:
rc_baton->error.SetErrorStringWithFormat(
"invalid file detected during copy: %s", src.GetPath().c_str());
return FileSystem::eEnumerateDirectoryResultQuit; // got an error, bail out
break;
}
llvm_unreachable("Unhandled file_type!");
}
Status Platform::Install(const FileSpec &src, const FileSpec &dst) {
Status error;
Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
if (log)
log->Printf("Platform::Install (src='%s', dst='%s')", src.GetPath().c_str(),
dst.GetPath().c_str());
FileSpec fixed_dst(dst);
if (!fixed_dst.GetFilename())
fixed_dst.GetFilename() = src.GetFilename();
FileSpec working_dir = GetWorkingDirectory();
if (dst) {
if (dst.GetDirectory()) {
const char first_dst_dir_char = dst.GetDirectory().GetCString()[0];
if (first_dst_dir_char == '/' || first_dst_dir_char == '\\') {
fixed_dst.GetDirectory() = dst.GetDirectory();
}
// If the fixed destination file doesn't have a directory yet, then we
// must have a relative path. We will resolve this relative path against
// the platform's working directory
if (!fixed_dst.GetDirectory()) {
FileSpec relative_spec;
std::string path;
if (working_dir) {
relative_spec = working_dir;
relative_spec.AppendPathComponent(dst.GetPath());
fixed_dst.GetDirectory() = relative_spec.GetDirectory();
} else {
error.SetErrorStringWithFormat(
"platform working directory must be valid for relative path '%s'",
dst.GetPath().c_str());
return error;
}
}
} else {
if (working_dir) {
fixed_dst.GetDirectory().SetCString(working_dir.GetCString());
} else {
error.SetErrorStringWithFormat(
"platform working directory must be valid for relative path '%s'",
dst.GetPath().c_str());
return error;
}
}
} else {
if (working_dir) {
fixed_dst.GetDirectory().SetCString(working_dir.GetCString());
} else {
error.SetErrorStringWithFormat("platform working directory must be valid "
"when destination directory is empty");
return error;
}
}
if (log)
log->Printf("Platform::Install (src='%s', dst='%s') fixed_dst='%s'",
src.GetPath().c_str(), dst.GetPath().c_str(),
fixed_dst.GetPath().c_str());
if (GetSupportsRSync()) {
error = PutFile(src, dst);
} else {
namespace fs = llvm::sys::fs;
switch (fs::get_file_type(src.GetPath(), false)) {
case fs::file_type::directory_file: {
llvm::sys::fs::remove(fixed_dst.GetPath());
uint32_t permissions = FileSystem::Instance().GetPermissions(src);
if (permissions == 0)
permissions = eFilePermissionsDirectoryDefault;
error = MakeDirectory(fixed_dst, permissions);
if (error.Success()) {
// Make a filespec that only fills in the directory of a FileSpec so
// when we enumerate we can quickly fill in the filename for dst copies
FileSpec recurse_dst;
recurse_dst.GetDirectory().SetCString(fixed_dst.GetCString());
std::string src_dir_path(src.GetPath());
RecurseCopyBaton baton = {recurse_dst, this, Status()};
FileSystem::Instance().EnumerateDirectory(
src_dir_path, true, true, true, RecurseCopy_Callback, &baton);
return baton.error;
}
} break;
case fs::file_type::regular_file:
llvm::sys::fs::remove(fixed_dst.GetPath());
error = PutFile(src, fixed_dst);
break;
case fs::file_type::symlink_file: {
llvm::sys::fs::remove(fixed_dst.GetPath());
FileSpec src_resolved;
error = FileSystem::Instance().Readlink(src, src_resolved);
if (error.Success())
error = CreateSymlink(dst, src_resolved);
} break;
case fs::file_type::fifo_file:
error.SetErrorString("platform install doesn't handle pipes");
break;
case fs::file_type::socket_file:
error.SetErrorString("platform install doesn't handle sockets");
break;
default:
error.SetErrorString(
"platform install doesn't handle non file or directory items");
break;
}
}
return error;
}
bool Platform::SetWorkingDirectory(const FileSpec &file_spec) {
if (IsHost()) {
Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
LLDB_LOG(log, "{0}", file_spec);
if (std::error_code ec = llvm::sys::fs::set_current_path(file_spec.GetPath())) {
LLDB_LOG(log, "error: {0}", ec.message());
return false;
}
return true;
} else {
m_working_dir.Clear();
return SetRemoteWorkingDirectory(file_spec);
}
}
Status Platform::MakeDirectory(const FileSpec &file_spec,
uint32_t permissions) {
if (IsHost())
return llvm::sys::fs::create_directory(file_spec.GetPath(), permissions);
else {
Status error;
error.SetErrorStringWithFormat("remote platform %s doesn't support %s",
GetPluginName().GetCString(),
LLVM_PRETTY_FUNCTION);
return error;
}
}
Status Platform::GetFilePermissions(const FileSpec &file_spec,
uint32_t &file_permissions) {
if (IsHost()) {
auto Value = llvm::sys::fs::getPermissions(file_spec.GetPath());
if (Value)
file_permissions = Value.get();
return Status(Value.getError());
} else {
Status error;
error.SetErrorStringWithFormat("remote platform %s doesn't support %s",
GetPluginName().GetCString(),
LLVM_PRETTY_FUNCTION);
return error;
}
}
Status Platform::SetFilePermissions(const FileSpec &file_spec,
uint32_t file_permissions) {
if (IsHost()) {
auto Perms = static_cast<llvm::sys::fs::perms>(file_permissions);
return llvm::sys::fs::setPermissions(file_spec.GetPath(), Perms);
} else {
Status error;
error.SetErrorStringWithFormat("remote platform %s doesn't support %s",
GetPluginName().GetCString(),
LLVM_PRETTY_FUNCTION);
return error;
}
Moved the execution context that was in the Debugger into the CommandInterpreter where it was always being used. Make sure that Modules can track their object file offsets correctly to allow opening of sub object files (like the "__commpage" on darwin). Modified the Platforms to be able to launch processes. The first part of this move is the platform soon will become the entity that launches your program and when it does, it uses a new ProcessLaunchInfo class which encapsulates all process launching settings. This simplifies the internal APIs needed for launching. I want to slowly phase out process launching from the process classes, so for now we can still launch just as we used to, but eventually the platform is the object that should do the launching. Modified the Host::LaunchProcess in the MacOSX Host.mm to correctly be able to launch processes with all of the new eLaunchFlag settings. Modified any code that was manually launching processes to use the Host::LaunchProcess functions. Fixed an issue where lldb_private::Args had implicitly defined copy constructors that could do the wrong thing. This has now been fixed by adding an appropriate copy constructor and assignment operator. Make sure we don't add empty ModuleSP entries to a module list. Fixed the commpage module creation on MacOSX, but we still need to train the MacOSX dynamic loader to not get rid of it when it doesn't have an entry in the all image infos. Abstracted many more calls from in ProcessGDBRemote down into the GDBRemoteCommunicationClient subclass to make the classes cleaner and more efficient. Fixed the default iOS ARM register context to be correct and also added support for targets that don't support the qThreadStopInfo packet by selecting the current thread (only if needed) and then sending a stop reply packet. Debugserver can now start up with a --unix-socket (-u for short) and can then bind to port zero and send the port it bound to to a listening process on the other end. This allows the GDB remote platform to spawn new GDB server instances (debugserver) to allow platform debugging. llvm-svn: 129351
2011-04-12 13:54:46 +08:00
}
ConstString Platform::GetName() { return GetPluginName(); }
const char *Platform::GetHostname() {
if (IsHost())
return "127.0.0.1";
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
if (m_name.empty())
return nullptr;
return m_name.c_str();
}
ConstString Platform::GetFullNameForDylib(ConstString basename) {
return basename;
}
bool Platform::SetRemoteWorkingDirectory(const FileSpec &working_dir) {
Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
if (log)
log->Printf("Platform::SetRemoteWorkingDirectory('%s')",
working_dir.GetCString());
m_working_dir = working_dir;
return true;
}
bool Platform::SetOSVersion(llvm::VersionTuple version) {
if (IsHost()) {
// We don't need anyone setting the OS version for the host platform, we
// should be able to figure it out by calling HostInfo::GetOSVersion(...).
Added more platform support. There are now some new commands: platform status -- gets status information for the selected platform platform create <platform-name> -- creates a new instance of a remote platform platform list -- list all available platforms platform select -- select a platform instance as the current platform (not working yet) When using "platform create" it will create a remote platform and make it the selected platform. For instances for iPhone OS debugging on Mac OS X one can do: (lldb) platform create remote-ios --sdk-version=4.0 Remote platform: iOS platform SDK version: 4.0 SDK path: "/Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0" Not connected to a remote device. (lldb) file ~/Documents/a.out Current executable set to '~/Documents/a.out' (armv6). (lldb) image list [ 0] /Volumes/work/gclayton/Documents/devb/attach/a.out [ 1] /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0/Symbols/usr/lib/dyld [ 2] /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0/Symbols/usr/lib/libSystem.B.dylib Note that this is all happening prior to running _or_ connecting to a remote platform. Once connected to a remote platform the OS version might change which means we will need to update our dependecies. Also once we run, we will need to match up the actualy binaries with the actualy UUID's to files in the SDK, or download and cache them locally. This is just the start of the remote platforms, but this modification is the first iteration in getting the platforms really doing something. llvm-svn: 127934
2011-03-19 09:12:21 +08:00
return false;
} else {
// We have a remote platform, allow setting the target OS version if we
// aren't connected, since if we are connected, we should be able to
// request the remote OS version from the connected platform.
if (IsConnected())
return false;
else {
// We aren't connected and we might want to set the OS version ahead of
// time before we connect so we can peruse files and use a local SDK or
// PDK cache of support files to disassemble or do other things.
m_os_version = version;
return true;
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
}
}
return false;
}
Status
Platform::ResolveExecutable(const ModuleSpec &module_spec,
lldb::ModuleSP &exe_module_sp,
const FileSpecList *module_search_paths_ptr) {
Status error;
if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {
if (module_spec.GetArchitecture().IsValid()) {
error = ModuleList::GetSharedModule(module_spec, exe_module_sp,
module_search_paths_ptr, nullptr,
nullptr);
} else {
// No valid architecture was specified, ask the platform for the
// architectures that we should be using (in the correct order) and see
// if we can find a match that way
ModuleSpec arch_module_spec(module_spec);
for (uint32_t idx = 0; GetSupportedArchitectureAtIndex(
idx, arch_module_spec.GetArchitecture());
++idx) {
error = ModuleList::GetSharedModule(arch_module_spec, exe_module_sp,
module_search_paths_ptr, nullptr,
nullptr);
// Did we find an executable using one of the
if (error.Success() && exe_module_sp)
break;
}
LLDB now has "Platform" plug-ins. Platform plug-ins are plug-ins that provide an interface to a local or remote debugging platform. By default each host OS that supports LLDB should be registering a "default" platform that will be used unless a new platform is selected. Platforms are responsible for things such as: - getting process information by name or by processs ID - finding platform files. This is useful for remote debugging where there is an SDK with files that might already or need to be cached for debug access. - getting a list of platform supported architectures in the exact order they should be selected. This helps the native x86 platform on MacOSX select the correct x86_64/i386 slice from universal binaries. - Connect to remote platforms for remote debugging - Resolving an executable including finding an executable inside platform specific bundles (macosx uses .app bundles that contain files) and also selecting the appropriate slice of universal files for a given platform. So by default there is always a local platform, but remote platforms can be connected to. I will soon be adding a new "platform" command that will support the following commands: (lldb) platform connect --name machine1 macosx connect://host:port Connected to "machine1" platform. (lldb) platform disconnect macosx This allows LLDB to be well setup to do remote debugging and also once connected process listing and finding for things like: (lldb) process attach --name x<TAB> The currently selected platform plug-in can now auto complete any available processes that start with "x". The responsibilities for the platform plug-in will soon grow and expand. llvm-svn: 127286
2011-03-09 06:40:15 +08:00
}
} else {
error.SetErrorStringWithFormat("'%s' does not exist",
module_spec.GetFileSpec().GetPath().c_str());
}
return error;
}
Status Platform::ResolveSymbolFile(Target &target, const ModuleSpec &sym_spec,
FileSpec &sym_file) {
Status error;
if (FileSystem::Instance().Exists(sym_spec.GetSymbolFileSpec()))
sym_file = sym_spec.GetSymbolFileSpec();
else
error.SetErrorString("unable to resolve symbol file");
return error;
}
bool Platform::ResolveRemotePath(const FileSpec &platform_path,
FileSpec &resolved_platform_path) {
resolved_platform_path = platform_path;
FileSystem::Instance().Resolve(resolved_platform_path);
return true;
}
const ArchSpec &Platform::GetSystemArchitecture() {
if (IsHost()) {
if (!m_system_arch.IsValid()) {
// We have a local host platform
m_system_arch = HostInfo::GetArchitecture();
m_system_arch_set_while_connected = m_system_arch.IsValid();
Added more platform support. There are now some new commands: platform status -- gets status information for the selected platform platform create <platform-name> -- creates a new instance of a remote platform platform list -- list all available platforms platform select -- select a platform instance as the current platform (not working yet) When using "platform create" it will create a remote platform and make it the selected platform. For instances for iPhone OS debugging on Mac OS X one can do: (lldb) platform create remote-ios --sdk-version=4.0 Remote platform: iOS platform SDK version: 4.0 SDK path: "/Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0" Not connected to a remote device. (lldb) file ~/Documents/a.out Current executable set to '~/Documents/a.out' (armv6). (lldb) image list [ 0] /Volumes/work/gclayton/Documents/devb/attach/a.out [ 1] /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0/Symbols/usr/lib/dyld [ 2] /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0/Symbols/usr/lib/libSystem.B.dylib Note that this is all happening prior to running _or_ connecting to a remote platform. Once connected to a remote platform the OS version might change which means we will need to update our dependecies. Also once we run, we will need to match up the actualy binaries with the actualy UUID's to files in the SDK, or download and cache them locally. This is just the start of the remote platforms, but this modification is the first iteration in getting the platforms really doing something. llvm-svn: 127934
2011-03-19 09:12:21 +08:00
}
} else {
// We have a remote platform. We can only fetch the remote system
// architecture if we are connected, and we don't want to do it more than
// once.
const bool is_connected = IsConnected();
bool fetch = false;
if (m_system_arch.IsValid()) {
// We have valid OS version info, check to make sure it wasn't manually
// set prior to connecting. If it was manually set prior to connecting,
// then lets fetch the actual OS version info if we are now connected.
if (is_connected && !m_system_arch_set_while_connected)
fetch = true;
} else {
// We don't have valid OS version info, fetch it if we are connected
fetch = is_connected;
Added more platform support. There are now some new commands: platform status -- gets status information for the selected platform platform create <platform-name> -- creates a new instance of a remote platform platform list -- list all available platforms platform select -- select a platform instance as the current platform (not working yet) When using "platform create" it will create a remote platform and make it the selected platform. For instances for iPhone OS debugging on Mac OS X one can do: (lldb) platform create remote-ios --sdk-version=4.0 Remote platform: iOS platform SDK version: 4.0 SDK path: "/Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0" Not connected to a remote device. (lldb) file ~/Documents/a.out Current executable set to '~/Documents/a.out' (armv6). (lldb) image list [ 0] /Volumes/work/gclayton/Documents/devb/attach/a.out [ 1] /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0/Symbols/usr/lib/dyld [ 2] /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.0/Symbols/usr/lib/libSystem.B.dylib Note that this is all happening prior to running _or_ connecting to a remote platform. Once connected to a remote platform the OS version might change which means we will need to update our dependecies. Also once we run, we will need to match up the actualy binaries with the actualy UUID's to files in the SDK, or download and cache them locally. This is just the start of the remote platforms, but this modification is the first iteration in getting the platforms really doing something. llvm-svn: 127934
2011-03-19 09:12:21 +08:00
}
if (fetch) {
m_system_arch = GetRemoteSystemArchitecture();
m_system_arch_set_while_connected = m_system_arch.IsValid();
}
}
return m_system_arch;
}
ArchSpec Platform::GetAugmentedArchSpec(llvm::StringRef triple) {
if (triple.empty())
return ArchSpec();
llvm::Triple normalized_triple(llvm::Triple::normalize(triple));
if (!ArchSpec::ContainsOnlyArch(normalized_triple))
return ArchSpec(triple);
if (auto kind = HostInfo::ParseArchitectureKind(triple))
return HostInfo::GetArchitecture(*kind);
ArchSpec compatible_arch;
ArchSpec raw_arch(triple);
if (!IsCompatibleArchitecture(raw_arch, false, &compatible_arch))
return raw_arch;
if (!compatible_arch.IsValid())
return ArchSpec(normalized_triple);
const llvm::Triple &compatible_triple = compatible_arch.GetTriple();
if (normalized_triple.getVendorName().empty())
normalized_triple.setVendor(compatible_triple.getVendor());
if (normalized_triple.getOSName().empty())
normalized_triple.setOS(compatible_triple.getOS());
if (normalized_triple.getEnvironmentName().empty())
normalized_triple.setEnvironment(compatible_triple.getEnvironment());
return ArchSpec(normalized_triple);
}
Status Platform::ConnectRemote(Args &args) {
Status error;
if (IsHost())
error.SetErrorStringWithFormat("The currently selected platform (%s) is "
"the host platform and is always connected.",
GetPluginName().GetCString());
else
error.SetErrorStringWithFormat(
"Platform::ConnectRemote() is not supported by %s",
GetPluginName().GetCString());
return error;
}
Status Platform::DisconnectRemote() {
Status error;
if (IsHost())
error.SetErrorStringWithFormat("The currently selected platform (%s) is "
"the host platform and is always connected.",
GetPluginName().GetCString());
else
error.SetErrorStringWithFormat(
"Platform::DisconnectRemote() is not supported by %s",
GetPluginName().GetCString());
return error;
}
bool Platform::GetProcessInfo(lldb::pid_t pid,
ProcessInstanceInfo &process_info) {
// Take care of the host case so that each subclass can just call this
// function to get the host functionality.
if (IsHost())
return Host::GetProcessInfo(pid, process_info);
return false;
}
uint32_t Platform::FindProcesses(const ProcessInstanceInfoMatch &match_info,
ProcessInstanceInfoList &process_infos) {
// Take care of the host case so that each subclass can just call this
// function to get the host functionality.
uint32_t match_count = 0;
if (IsHost())
match_count = Host::FindProcesses(match_info, process_infos);
return match_count;
}
Status Platform::LaunchProcess(ProcessLaunchInfo &launch_info) {
Status error;
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
if (log)
log->Printf("Platform::%s()", __FUNCTION__);
// Take care of the host case so that each subclass can just call this
// function to get the host functionality.
if (IsHost()) {
if (::getenv("LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY"))
launch_info.GetFlags().Set(eLaunchFlagLaunchInTTY);
if (launch_info.GetFlags().Test(eLaunchFlagLaunchInShell)) {
const bool is_localhost = true;
const bool will_debug = launch_info.GetFlags().Test(eLaunchFlagDebug);
const bool first_arg_is_full_shell_command = false;
uint32_t num_resumes = GetResumeCountForLaunchInfo(launch_info);
if (log) {
const FileSpec &shell = launch_info.GetShell();
std::string shell_str = (shell) ? shell.GetPath() : "<null>";
log->Printf(
"Platform::%s GetResumeCountForLaunchInfo() returned %" PRIu32
", shell is '%s'",
__FUNCTION__, num_resumes, shell_str.c_str());
}
if (!launch_info.ConvertArgumentsForLaunchingInShell(
error, is_localhost, will_debug, first_arg_is_full_shell_command,
num_resumes))
return error;
} else if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) {
error = ShellExpandArguments(launch_info);
if (error.Fail()) {
error.SetErrorStringWithFormat("shell expansion failed (reason: %s). "
"consider launching with 'process "
"launch'.",
error.AsCString("unknown"));
return error;
}
}
if (log)
log->Printf("Platform::%s final launch_info resume count: %" PRIu32,
__FUNCTION__, launch_info.GetResumeCount());
error = Host::LaunchProcess(launch_info);
} else
error.SetErrorString(
"base lldb_private::Platform class can't launch remote processes");
return error;
}
Status Platform::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
if (IsHost())
return Host::ShellExpandArguments(launch_info);
return Status("base lldb_private::Platform class can't expand arguments");
}
Status Platform::KillProcess(const lldb::pid_t pid) {
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
if (log)
log->Printf("Platform::%s, pid %" PRIu64, __FUNCTION__, pid);
// Try to find a process plugin to handle this Kill request. If we can't,
// fall back to the default OS implementation.
size_t num_debuggers = Debugger::GetNumDebuggers();
for (size_t didx = 0; didx < num_debuggers; ++didx) {
DebuggerSP debugger = Debugger::GetDebuggerAtIndex(didx);
lldb_private::TargetList &targets = debugger->GetTargetList();
for (int tidx = 0; tidx < targets.GetNumTargets(); ++tidx) {
ProcessSP process = targets.GetTargetAtIndex(tidx)->GetProcessSP();
if (process->GetID() == pid)
return process->Destroy(true);
}
}
if (!IsHost()) {
return Status(
"base lldb_private::Platform class can't kill remote processes unless "
"they are controlled by a process plugin");
}
Host::Kill(pid, SIGTERM);
return Status();
}
Moved the execution context that was in the Debugger into the CommandInterpreter where it was always being used. Make sure that Modules can track their object file offsets correctly to allow opening of sub object files (like the "__commpage" on darwin). Modified the Platforms to be able to launch processes. The first part of this move is the platform soon will become the entity that launches your program and when it does, it uses a new ProcessLaunchInfo class which encapsulates all process launching settings. This simplifies the internal APIs needed for launching. I want to slowly phase out process launching from the process classes, so for now we can still launch just as we used to, but eventually the platform is the object that should do the launching. Modified the Host::LaunchProcess in the MacOSX Host.mm to correctly be able to launch processes with all of the new eLaunchFlag settings. Modified any code that was manually launching processes to use the Host::LaunchProcess functions. Fixed an issue where lldb_private::Args had implicitly defined copy constructors that could do the wrong thing. This has now been fixed by adding an appropriate copy constructor and assignment operator. Make sure we don't add empty ModuleSP entries to a module list. Fixed the commpage module creation on MacOSX, but we still need to train the MacOSX dynamic loader to not get rid of it when it doesn't have an entry in the all image infos. Abstracted many more calls from in ProcessGDBRemote down into the GDBRemoteCommunicationClient subclass to make the classes cleaner and more efficient. Fixed the default iOS ARM register context to be correct and also added support for targets that don't support the qThreadStopInfo packet by selecting the current thread (only if needed) and then sending a stop reply packet. Debugserver can now start up with a --unix-socket (-u for short) and can then bind to port zero and send the port it bound to to a listening process on the other end. This allows the GDB remote platform to spawn new GDB server instances (debugserver) to allow platform debugging. llvm-svn: 129351
2011-04-12 13:54:46 +08:00
lldb::ProcessSP
Platform::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger,
Target *target, // Can be nullptr, if nullptr create a
// new target, else use existing one
Status &error) {
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
if (log)
log->Printf("Platform::%s entered (target %p)", __FUNCTION__,
static_cast<void *>(target));
ProcessSP process_sp;
// Make sure we stop at the entry point
launch_info.GetFlags().Set(eLaunchFlagDebug);
// We always launch the process we are going to debug in a separate process
// group, since then we can handle ^C interrupts ourselves w/o having to
// worry about the target getting them as well.
launch_info.SetLaunchInSeparateProcessGroup(true);
// Allow any StructuredData process-bound plugins to adjust the launch info
// if needed
size_t i = 0;
bool iteration_complete = false;
// Note iteration can't simply go until a nullptr callback is returned, as it
// is valid for a plugin to not supply a filter.
auto get_filter_func = PluginManager::GetStructuredDataFilterCallbackAtIndex;
for (auto filter_callback = get_filter_func(i, iteration_complete);
!iteration_complete;
filter_callback = get_filter_func(++i, iteration_complete)) {
if (filter_callback) {
// Give this ProcessLaunchInfo filter a chance to adjust the launch info.
error = (*filter_callback)(launch_info, target);
if (!error.Success()) {
if (log)
log->Printf("Platform::%s() StructuredDataPlugin launch "
"filter failed.",
__FUNCTION__);
return process_sp;
}
}
}
error = LaunchProcess(launch_info);
if (error.Success()) {
if (log)
log->Printf("Platform::%s LaunchProcess() call succeeded (pid=%" PRIu64
")",
__FUNCTION__, launch_info.GetProcessID());
if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) {
ProcessAttachInfo attach_info(launch_info);
process_sp = Attach(attach_info, debugger, target, error);
if (process_sp) {
if (log)
log->Printf("Platform::%s Attach() succeeded, Process plugin: %s",
__FUNCTION__, process_sp->GetPluginName().AsCString());
launch_info.SetHijackListener(attach_info.GetHijackListener());
// Since we attached to the process, it will think it needs to detach
// if the process object just goes away without an explicit call to
// Process::Kill() or Process::Detach(), so let it know to kill the
// process if this happens.
process_sp->SetShouldDetach(false);
// If we didn't have any file actions, the pseudo terminal might have
// been used where the slave side was given as the file to open for
// stdin/out/err after we have already opened the master so we can
// read/write stdin/out/err.
int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
if (pty_fd != PseudoTerminal::invalid_fd) {
process_sp->SetSTDIOFileDescriptor(pty_fd);
}
} else {
if (log)
log->Printf("Platform::%s Attach() failed: %s", __FUNCTION__,
error.AsCString());
}
} else {
if (log)
log->Printf("Platform::%s LaunchProcess() returned launch_info with "
"invalid process id",
__FUNCTION__);
}
} else {
if (log)
log->Printf("Platform::%s LaunchProcess() failed: %s", __FUNCTION__,
error.AsCString());
}
return process_sp;
Moved the execution context that was in the Debugger into the CommandInterpreter where it was always being used. Make sure that Modules can track their object file offsets correctly to allow opening of sub object files (like the "__commpage" on darwin). Modified the Platforms to be able to launch processes. The first part of this move is the platform soon will become the entity that launches your program and when it does, it uses a new ProcessLaunchInfo class which encapsulates all process launching settings. This simplifies the internal APIs needed for launching. I want to slowly phase out process launching from the process classes, so for now we can still launch just as we used to, but eventually the platform is the object that should do the launching. Modified the Host::LaunchProcess in the MacOSX Host.mm to correctly be able to launch processes with all of the new eLaunchFlag settings. Modified any code that was manually launching processes to use the Host::LaunchProcess functions. Fixed an issue where lldb_private::Args had implicitly defined copy constructors that could do the wrong thing. This has now been fixed by adding an appropriate copy constructor and assignment operator. Make sure we don't add empty ModuleSP entries to a module list. Fixed the commpage module creation on MacOSX, but we still need to train the MacOSX dynamic loader to not get rid of it when it doesn't have an entry in the all image infos. Abstracted many more calls from in ProcessGDBRemote down into the GDBRemoteCommunicationClient subclass to make the classes cleaner and more efficient. Fixed the default iOS ARM register context to be correct and also added support for targets that don't support the qThreadStopInfo packet by selecting the current thread (only if needed) and then sending a stop reply packet. Debugserver can now start up with a --unix-socket (-u for short) and can then bind to port zero and send the port it bound to to a listening process on the other end. This allows the GDB remote platform to spawn new GDB server instances (debugserver) to allow platform debugging. llvm-svn: 129351
2011-04-12 13:54:46 +08:00
}
lldb::PlatformSP
Platform::GetPlatformForArchitecture(const ArchSpec &arch,
ArchSpec *platform_arch_ptr) {
lldb::PlatformSP platform_sp;
Status error;
if (arch.IsValid())
platform_sp = Platform::Create(arch, platform_arch_ptr, error);
return platform_sp;
}
//------------------------------------------------------------------
/// Lets a platform answer if it is compatible with a given
/// architecture and the target triple contained within.
//------------------------------------------------------------------
bool Platform::IsCompatibleArchitecture(const ArchSpec &arch,
bool exact_arch_match,
ArchSpec *compatible_arch_ptr) {
// If the architecture is invalid, we must answer true...
if (arch.IsValid()) {
ArchSpec platform_arch;
// Try for an exact architecture match first.
if (exact_arch_match) {
for (uint32_t arch_idx = 0;
GetSupportedArchitectureAtIndex(arch_idx, platform_arch);
++arch_idx) {
if (arch.IsExactMatch(platform_arch)) {
if (compatible_arch_ptr)
*compatible_arch_ptr = platform_arch;
return true;
}
}
} else {
for (uint32_t arch_idx = 0;
GetSupportedArchitectureAtIndex(arch_idx, platform_arch);
++arch_idx) {
if (arch.IsCompatibleMatch(platform_arch)) {
if (compatible_arch_ptr)
*compatible_arch_ptr = platform_arch;
return true;
}
}
}
}
if (compatible_arch_ptr)
compatible_arch_ptr->Clear();
return false;
}
Status Platform::PutFile(const FileSpec &source, const FileSpec &destination,
uint32_t uid, uint32_t gid) {
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
if (log)
log->Printf("[PutFile] Using block by block transfer....\n");
uint32_t source_open_options =
File::eOpenOptionRead | File::eOpenOptionCloseOnExec;
namespace fs = llvm::sys::fs;
if (fs::is_symlink_file(source.GetPath()))
source_open_options |= File::eOpenOptionDontFollowSymlinks;
File source_file;
Status error = FileSystem::Instance().Open(
source_file, source, source_open_options, lldb::eFilePermissionsUserRW);
uint32_t permissions = source_file.GetPermissions(error);
if (permissions == 0)
permissions = lldb::eFilePermissionsFileDefault;
if (!source_file.IsValid())
return Status("PutFile: unable to open source file");
lldb::user_id_t dest_file = OpenFile(
destination, File::eOpenOptionCanCreate | File::eOpenOptionWrite |
File::eOpenOptionTruncate | File::eOpenOptionCloseOnExec,
permissions, error);
if (log)
log->Printf("dest_file = %" PRIu64 "\n", dest_file);
if (error.Fail())
return error;
if (dest_file == UINT64_MAX)
return Status("unable to open target file");
lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024 * 16, 0));
uint64_t offset = 0;
for (;;) {
size_t bytes_read = buffer_sp->GetByteSize();
error = source_file.Read(buffer_sp->GetBytes(), bytes_read);
if (error.Fail() || bytes_read == 0)
break;
const uint64_t bytes_written =
WriteFile(dest_file, offset, buffer_sp->GetBytes(), bytes_read, error);
if (error.Fail())
break;
offset += bytes_written;
if (bytes_written != bytes_read) {
// We didn't write the correct number of bytes, so adjust the file
// position in the source file we are reading from...
source_file.SeekFromStart(offset);
}
}
CloseFile(dest_file, error);
if (uid == UINT32_MAX && gid == UINT32_MAX)
return error;
// TODO: ChownFile?
return error;
}
Status Platform::GetFile(const FileSpec &source, const FileSpec &destination) {
Status error("unimplemented");
return error;
}
Status
Platform::CreateSymlink(const FileSpec &src, // The name of the link is in src
const FileSpec &dst) // The symlink points to dst
{
Status error("unimplemented");
return error;
}
bool Platform::GetFileExists(const lldb_private::FileSpec &file_spec) {
return false;
}
Status Platform::Unlink(const FileSpec &path) {
Status error("unimplemented");
return error;
}
MmapArgList Platform::GetMmapArgumentList(const ArchSpec &arch, addr_t addr,
addr_t length, unsigned prot,
unsigned flags, addr_t fd,
addr_t offset) {
uint64_t flags_platform = 0;
if (flags & eMmapFlagsPrivate)
flags_platform |= MAP_PRIVATE;
if (flags & eMmapFlagsAnon)
flags_platform |= MAP_ANON;
MmapArgList args({addr, length, prot, flags_platform, fd, offset});
return args;
}
lldb_private::Status Platform::RunShellCommand(
const char *command, // Shouldn't be nullptr
const FileSpec &
working_dir, // Pass empty FileSpec to use the current working directory
int *status_ptr, // Pass nullptr if you don't want the process exit status
int *signo_ptr, // Pass nullptr if you don't want the signal that caused the
// process to exit
std::string
*command_output, // Pass nullptr if you don't want the command output
const Timeout<std::micro> &timeout) {
if (IsHost())
return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr,
command_output, timeout);
else
return Status("unimplemented");
}
bool Platform::CalculateMD5(const FileSpec &file_spec, uint64_t &low,
uint64_t &high) {
if (!IsHost())
return false;
auto Result = llvm::sys::fs::md5_contents(file_spec.GetPath());
if (!Result)
return false;
std::tie(high, low) = Result->words();
return true;
}
void Platform::SetLocalCacheDirectory(const char *local) {
m_local_cache_directory.assign(local);
}
const char *Platform::GetLocalCacheDirectory() {
return m_local_cache_directory.c_str();
}
static constexpr OptionDefinition g_rsync_option_table[] = {
{LLDB_OPT_SET_ALL, false, "rsync", 'r', OptionParser::eNoArgument, nullptr,
{}, 0, eArgTypeNone, "Enable rsync."},
{LLDB_OPT_SET_ALL, false, "rsync-opts", 'R',
OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCommandName,
"Platform-specific options required for rsync to work."},
{LLDB_OPT_SET_ALL, false, "rsync-prefix", 'P',
OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCommandName,
"Platform-specific rsync prefix put before the remote path."},
{LLDB_OPT_SET_ALL, false, "ignore-remote-hostname", 'i',
OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone,
"Do not automatically fill in the remote hostname when composing the "
"rsync command."},
};
static constexpr OptionDefinition g_ssh_option_table[] = {
{LLDB_OPT_SET_ALL, false, "ssh", 's', OptionParser::eNoArgument, nullptr,
{}, 0, eArgTypeNone, "Enable SSH."},
{LLDB_OPT_SET_ALL, false, "ssh-opts", 'S', OptionParser::eRequiredArgument,
nullptr, {}, 0, eArgTypeCommandName,
"Platform-specific options required for SSH to work."},
};
static constexpr OptionDefinition g_caching_option_table[] = {
{LLDB_OPT_SET_ALL, false, "local-cache-dir", 'c',
OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePath,
"Path in which to store local copies of files."},
};
llvm::ArrayRef<OptionDefinition> OptionGroupPlatformRSync::GetDefinitions() {
return llvm::makeArrayRef(g_rsync_option_table);
}
void OptionGroupPlatformRSync::OptionParsingStarting(
ExecutionContext *execution_context) {
m_rsync = false;
m_rsync_opts.clear();
m_rsync_prefix.clear();
m_ignores_remote_hostname = false;
}
lldb_private::Status
OptionGroupPlatformRSync::SetOptionValue(uint32_t option_idx,
llvm::StringRef option_arg,
ExecutionContext *execution_context) {
Status error;
char short_option = (char)GetDefinitions()[option_idx].short_option;
switch (short_option) {
case 'r':
m_rsync = true;
break;
case 'R':
m_rsync_opts.assign(option_arg);
break;
case 'P':
m_rsync_prefix.assign(option_arg);
break;
case 'i':
m_ignores_remote_hostname = true;
break;
default:
error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
break;
}
return error;
}
lldb::BreakpointSP
Platform::SetThreadCreationBreakpoint(lldb_private::Target &target) {
return lldb::BreakpointSP();
}
llvm::ArrayRef<OptionDefinition> OptionGroupPlatformSSH::GetDefinitions() {
return llvm::makeArrayRef(g_ssh_option_table);
}
void OptionGroupPlatformSSH::OptionParsingStarting(
ExecutionContext *execution_context) {
m_ssh = false;
m_ssh_opts.clear();
}
lldb_private::Status
OptionGroupPlatformSSH::SetOptionValue(uint32_t option_idx,
llvm::StringRef option_arg,
ExecutionContext *execution_context) {
Status error;
char short_option = (char)GetDefinitions()[option_idx].short_option;
switch (short_option) {
case 's':
m_ssh = true;
break;
case 'S':
m_ssh_opts.assign(option_arg);
break;
default:
error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
break;
}
return error;
}
llvm::ArrayRef<OptionDefinition> OptionGroupPlatformCaching::GetDefinitions() {
return llvm::makeArrayRef(g_caching_option_table);
}
void OptionGroupPlatformCaching::OptionParsingStarting(
ExecutionContext *execution_context) {
m_cache_dir.clear();
}
lldb_private::Status OptionGroupPlatformCaching::SetOptionValue(
uint32_t option_idx, llvm::StringRef option_arg,
ExecutionContext *execution_context) {
Status error;
char short_option = (char)GetDefinitions()[option_idx].short_option;
switch (short_option) {
case 'c':
m_cache_dir.assign(option_arg);
break;
default:
error.SetErrorStringWithFormat("unrecognized option '%c'", short_option);
break;
}
return error;
}
Add Utility/Environment class for handling... environments Summary: There was some confusion in the code about how to represent process environment. Most of the code (ab)used the Args class for this purpose, but some of it used a more basic StringList class instead. In either case, the fact that the underlying abstraction did not provide primitive operations for the typical environment operations meant that even a simple operation like checking for an environment variable value was several lines of code. This patch adds a separate Environment class, which is essentialy a llvm::StringMap<std::string> in disguise. To standard StringMap functionality, it adds a couple of new functions, which are specific to the environment use case: - (most important) envp conversion for passing into execve() and likes. Instead of trying to maintain a constantly up-to-date envp view, it provides a function which creates a envp view on demand, with the expectation that this will be called as the very last thing before handing the value to the system function. - insert(StringRef KeyEqValue) - splits KeyEqValue into (key, value) pair and inserts it into the environment map. - compose(value_type KeyValue) - takes a map entry and converts in back into "KEY=VALUE" representation. With this interface most of the environment-manipulating code becomes one-liners. The only tricky part was maintaining compatibility in SBLaunchInfo, which expects that the environment entries are accessible by index and that the returned const char* is backed by the launch info object (random access into maps is hard and the map stores the entry in a deconstructed form, so we cannot just return a .c_str() value). To solve this, I have the SBLaunchInfo convert the environment into the "envp" form, and use it to answer the environment queries. Extra code is added to make sure the envp version is always in sync. (This also improves the layering situation as Args was in the Interpreter module whereas Environment is in Utility.) Reviewers: zturner, davide, jingham, clayborg Subscribers: emaste, lldb-commits, mgorny Differential Revision: https://reviews.llvm.org/D41359 llvm-svn: 322174
2018-01-10 19:57:31 +08:00
Environment Platform::GetEnvironment() { return Environment(); }
const std::vector<ConstString> &Platform::GetTrapHandlerSymbolNames() {
if (!m_calculated_trap_handlers) {
std::lock_guard<std::mutex> guard(m_mutex);
if (!m_calculated_trap_handlers) {
CalculateTrapHandlerSymbolNames();
m_calculated_trap_handlers = true;
}
}
return m_trap_handlers;
}
Status Platform::GetCachedExecutable(
ModuleSpec &module_spec, lldb::ModuleSP &module_sp,
const FileSpecList *module_search_paths_ptr, Platform &remote_platform) {
const auto platform_spec = module_spec.GetFileSpec();
const auto error = LoadCachedExecutable(
module_spec, module_sp, module_search_paths_ptr, remote_platform);
if (error.Success()) {
module_spec.GetFileSpec() = module_sp->GetFileSpec();
module_spec.GetPlatformFileSpec() = platform_spec;
}
return error;
}
Status Platform::LoadCachedExecutable(
const ModuleSpec &module_spec, lldb::ModuleSP &module_sp,
const FileSpecList *module_search_paths_ptr, Platform &remote_platform) {
return GetRemoteSharedModule(module_spec, nullptr, module_sp,
[&](const ModuleSpec &spec) {
return remote_platform.ResolveExecutable(
spec, module_sp, module_search_paths_ptr);
},
nullptr);
}
Status Platform::GetRemoteSharedModule(const ModuleSpec &module_spec,
Process *process,
lldb::ModuleSP &module_sp,
const ModuleResolver &module_resolver,
bool *did_create_ptr) {
// Get module information from a target.
ModuleSpec resolved_module_spec;
bool got_module_spec = false;
if (process) {
// Try to get module information from the process
if (process->GetModuleSpec(module_spec.GetFileSpec(),
module_spec.GetArchitecture(),
resolved_module_spec)) {
if (!module_spec.GetUUID().IsValid() ||
module_spec.GetUUID() == resolved_module_spec.GetUUID()) {
got_module_spec = true;
}
}
}
if (!module_spec.GetArchitecture().IsValid()) {
Status error;
// No valid architecture was specified, ask the platform for the
// architectures that we should be using (in the correct order) and see if
// we can find a match that way
ModuleSpec arch_module_spec(module_spec);
for (uint32_t idx = 0; GetSupportedArchitectureAtIndex(
idx, arch_module_spec.GetArchitecture());
++idx) {
error = ModuleList::GetSharedModule(arch_module_spec, module_sp, nullptr,
nullptr, nullptr);
// Did we find an executable using one of the
if (error.Success() && module_sp)
break;
}
if (module_sp)
got_module_spec = true;
}
if (!got_module_spec) {
// Get module information from a target.
if (!GetModuleSpec(module_spec.GetFileSpec(), module_spec.GetArchitecture(),
resolved_module_spec)) {
if (!module_spec.GetUUID().IsValid() ||
module_spec.GetUUID() == resolved_module_spec.GetUUID()) {
return module_resolver(module_spec);
}
}
}
// If we are looking for a specific UUID, make sure resolved_module_spec has
// the same one before we search.
if (module_spec.GetUUID().IsValid()) {
resolved_module_spec.GetUUID() = module_spec.GetUUID();
}
// Trying to find a module by UUID on local file system.
const auto error = module_resolver(resolved_module_spec);
if (error.Fail()) {
if (GetCachedSharedModule(resolved_module_spec, module_sp, did_create_ptr))
return Status();
}
return error;
}
bool Platform::GetCachedSharedModule(const ModuleSpec &module_spec,
lldb::ModuleSP &module_sp,
bool *did_create_ptr) {
if (IsHost() || !GetGlobalPlatformProperties()->GetUseModuleCache() ||
!GetGlobalPlatformProperties()->GetModuleCacheDirectory())
return false;
Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM);
// Check local cache for a module.
auto error = m_module_cache->GetAndPut(
GetModuleCacheRoot(), GetCacheHostname(), module_spec,
[this](const ModuleSpec &module_spec,
const FileSpec &tmp_download_file_spec) {
return DownloadModuleSlice(
module_spec.GetFileSpec(), module_spec.GetObjectOffset(),
module_spec.GetObjectSize(), tmp_download_file_spec);
},
[this](const ModuleSP &module_sp,
const FileSpec &tmp_download_file_spec) {
return DownloadSymbolFile(module_sp, tmp_download_file_spec);
},
module_sp, did_create_ptr);
if (error.Success())
return true;
if (log)
log->Printf("Platform::%s - module %s not found in local cache: %s",
__FUNCTION__, module_spec.GetUUID().GetAsString().c_str(),
error.AsCString());
return false;
}
Status Platform::DownloadModuleSlice(const FileSpec &src_file_spec,
const uint64_t src_offset,
const uint64_t src_size,
const FileSpec &dst_file_spec) {
Status error;
std::error_code EC;
llvm::raw_fd_ostream dst(dst_file_spec.GetPath(), EC, llvm::sys::fs::F_None);
if (EC) {
error.SetErrorStringWithFormat("unable to open destination file: %s",
dst_file_spec.GetPath().c_str());
return error;
}
auto src_fd = OpenFile(src_file_spec, File::eOpenOptionRead,
lldb::eFilePermissionsFileDefault, error);
if (error.Fail()) {
error.SetErrorStringWithFormat("unable to open source file: %s",
error.AsCString());
return error;
}
std::vector<char> buffer(1024);
auto offset = src_offset;
uint64_t total_bytes_read = 0;
while (total_bytes_read < src_size) {
const auto to_read = std::min(static_cast<uint64_t>(buffer.size()),
src_size - total_bytes_read);
const uint64_t n_read =
ReadFile(src_fd, offset, &buffer[0], to_read, error);
if (error.Fail())
break;
if (n_read == 0) {
error.SetErrorString("read 0 bytes");
break;
}
offset += n_read;
total_bytes_read += n_read;
dst.write(&buffer[0], n_read);
}
Status close_error;
CloseFile(src_fd, close_error); // Ignoring close error.
return error;
}
Status Platform::DownloadSymbolFile(const lldb::ModuleSP &module_sp,
const FileSpec &dst_file_spec) {
return Status(
"Symbol file downloading not supported by the default platform.");
}
FileSpec Platform::GetModuleCacheRoot() {
auto dir_spec = GetGlobalPlatformProperties()->GetModuleCacheDirectory();
dir_spec.AppendPathComponent(GetName().AsCString());
return dir_spec;
}
const char *Platform::GetCacheHostname() { return GetHostname(); }
const UnixSignalsSP &Platform::GetRemoteUnixSignals() {
static const auto s_default_unix_signals_sp = std::make_shared<UnixSignals>();
return s_default_unix_signals_sp;
}
UnixSignalsSP Platform::GetUnixSignals() {
if (IsHost())
return UnixSignals::CreateForHost();
return GetRemoteUnixSignals();
}
uint32_t Platform::LoadImage(lldb_private::Process *process,
const lldb_private::FileSpec &local_file,
const lldb_private::FileSpec &remote_file,
lldb_private::Status &error) {
if (local_file && remote_file) {
// Both local and remote file was specified. Install the local file to the
// given location.
if (IsRemote() || local_file != remote_file) {
error = Install(local_file, remote_file);
if (error.Fail())
return LLDB_INVALID_IMAGE_TOKEN;
}
return DoLoadImage(process, remote_file, nullptr, error);
}
if (local_file) {
// Only local file was specified. Install it to the current working
// directory.
FileSpec target_file = GetWorkingDirectory();
target_file.AppendPathComponent(local_file.GetFilename().AsCString());
if (IsRemote() || local_file != target_file) {
error = Install(local_file, target_file);
if (error.Fail())
return LLDB_INVALID_IMAGE_TOKEN;
}
return DoLoadImage(process, target_file, nullptr, error);
}
if (remote_file) {
// Only remote file was specified so we don't have to do any copying
return DoLoadImage(process, remote_file, nullptr, error);
}
error.SetErrorString("Neither local nor remote file was specified");
return LLDB_INVALID_IMAGE_TOKEN;
}
uint32_t Platform::DoLoadImage(lldb_private::Process *process,
const lldb_private::FileSpec &remote_file,
const std::vector<std::string> *paths,
lldb_private::Status &error,
lldb_private::FileSpec *loaded_image) {
error.SetErrorString("LoadImage is not supported on the current platform");
return LLDB_INVALID_IMAGE_TOKEN;
}
uint32_t Platform::LoadImageUsingPaths(lldb_private::Process *process,
const lldb_private::FileSpec &remote_filename,
const std::vector<std::string> &paths,
lldb_private::Status &error,
lldb_private::FileSpec *loaded_path)
{
FileSpec file_to_use;
if (remote_filename.IsAbsolute())
file_to_use = FileSpec(remote_filename.GetFilename().GetStringRef(),
remote_filename.GetPathStyle());
else
file_to_use = remote_filename;
return DoLoadImage(process, file_to_use, &paths, error, loaded_path);
}
Status Platform::UnloadImage(lldb_private::Process *process,
uint32_t image_token) {
return Status("UnloadImage is not supported on the current platform");
}
lldb::ProcessSP Platform::ConnectProcess(llvm::StringRef connect_url,
llvm::StringRef plugin_name,
lldb_private::Debugger &debugger,
lldb_private::Target *target,
lldb_private::Status &error) {
error.Clear();
if (!target) {
ArchSpec arch;
if (target && target->GetArchitecture().IsValid())
arch = target->GetArchitecture();
else
arch = Target::GetDefaultArchitecture();
const char *triple = "";
if (arch.IsValid())
triple = arch.GetTriple().getTriple().c_str();
TargetSP new_target_sp;
error = debugger.GetTargetList().CreateTarget(
debugger, "", triple, eLoadDependentsNo, nullptr, new_target_sp);
target = new_target_sp.get();
}
if (!target || error.Fail())
return nullptr;
debugger.GetTargetList().SetSelectedTarget(target);
lldb::ProcessSP process_sp =
target->CreateProcess(debugger.GetListener(), plugin_name, nullptr);
if (!process_sp)
return nullptr;
error =
process_sp->ConnectRemote(debugger.GetOutputFile().get(), connect_url);
if (error.Fail())
return nullptr;
return process_sp;
}
size_t Platform::ConnectToWaitingProcesses(lldb_private::Debugger &debugger,
lldb_private::Status &error) {
error.Clear();
return 0;
}
size_t Platform::GetSoftwareBreakpointTrapOpcode(Target &target,
BreakpointSite *bp_site) {
ArchSpec arch = target.GetArchitecture();
const uint8_t *trap_opcode = nullptr;
size_t trap_opcode_size = 0;
switch (arch.GetMachine()) {
case llvm::Triple::aarch64: {
static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x20, 0xd4};
trap_opcode = g_aarch64_opcode;
trap_opcode_size = sizeof(g_aarch64_opcode);
} break;
Support Linux on SystemZ as platform This patch adds support for Linux on SystemZ: - A new ArchSpec value of eCore_s390x_generic - A new directory Plugins/ABI/SysV-s390x providing an ABI implementation - Register context support - Native Linux support including watchpoint support - ELF core file support - Misc. support throughout the code base (e.g. breakpoint opcodes) - Test case updates to support the platform This should provide complete support for debugging the SystemZ platform. Not yet supported are optional features like transaction support (zEC12) or SIMD vector support (z13). There is no instruction emulation, since our ABI requires that all code provide correct DWARF CFI at all PC locations in .eh_frame to support unwinding (i.e. -fasynchronous-unwind-tables is on by default). The implementation follows existing platforms in a mostly straightforward manner. A couple of things that are different: - We do not use PTRACE_PEEKUSER / PTRACE_POKEUSER to access single registers, since some registers (access register) reside at offsets in the user area that are multiples of 4, but the PTRACE_PEEKUSER interface only allows accessing aligned 8-byte blocks in the user area. Instead, we use a s390 specific ptrace interface PTRACE_PEEKUSR_AREA / PTRACE_POKEUSR_AREA that allows accessing a whole block of the user area in one go, so in effect allowing to treat parts of the user area as register sets. - SystemZ hardware does not provide any means to implement read watchpoints, only write watchpoints. In fact, we can only support a *single* write watchpoint (but this can span a range of arbitrary size). In LLDB this means we support only a single watchpoint. I've set all test cases that require read watchpoints (or multiple watchpoints) to expected failure on the platform. [ Note that there were two test cases that install a read/write watchpoint even though they nowhere rely on the "read" property. I've changed those to simply use plain write watchpoints. ] Differential Revision: http://reviews.llvm.org/D18978 llvm-svn: 266308
2016-04-14 22:28:34 +08:00
// TODO: support big-endian arm and thumb trap codes.
case llvm::Triple::arm: {
// The ARM reference recommends the use of 0xe7fddefe and 0xdefe but the
// linux kernel does otherwise.
static const uint8_t g_arm_breakpoint_opcode[] = {0xf0, 0x01, 0xf0, 0xe7};
static const uint8_t g_thumb_breakpoint_opcode[] = {0x01, 0xde};
lldb::BreakpointLocationSP bp_loc_sp(bp_site->GetOwnerAtIndex(0));
AddressClass addr_class = AddressClass::eUnknown;
if (bp_loc_sp) {
addr_class = bp_loc_sp->GetAddress().GetAddressClass();
if (addr_class == AddressClass::eUnknown &&
(bp_loc_sp->GetAddress().GetFileAddress() & 1))
addr_class = AddressClass::eCodeAlternateISA;
}
if (addr_class == AddressClass::eCodeAlternateISA) {
trap_opcode = g_thumb_breakpoint_opcode;
trap_opcode_size = sizeof(g_thumb_breakpoint_opcode);
} else {
trap_opcode = g_arm_breakpoint_opcode;
trap_opcode_size = sizeof(g_arm_breakpoint_opcode);
}
} break;
case llvm::Triple::mips:
case llvm::Triple::mips64: {
static const uint8_t g_hex_opcode[] = {0x00, 0x00, 0x00, 0x0d};
trap_opcode = g_hex_opcode;
trap_opcode_size = sizeof(g_hex_opcode);
} break;
case llvm::Triple::mipsel:
case llvm::Triple::mips64el: {
static const uint8_t g_hex_opcode[] = {0x0d, 0x00, 0x00, 0x00};
trap_opcode = g_hex_opcode;
trap_opcode_size = sizeof(g_hex_opcode);
} break;
case llvm::Triple::systemz: {
static const uint8_t g_hex_opcode[] = {0x00, 0x01};
trap_opcode = g_hex_opcode;
trap_opcode_size = sizeof(g_hex_opcode);
} break;
case llvm::Triple::hexagon: {
static const uint8_t g_hex_opcode[] = {0x0c, 0xdb, 0x00, 0x54};
trap_opcode = g_hex_opcode;
trap_opcode_size = sizeof(g_hex_opcode);
} break;
case llvm::Triple::ppc:
case llvm::Triple::ppc64: {
static const uint8_t g_ppc_opcode[] = {0x7f, 0xe0, 0x00, 0x08};
trap_opcode = g_ppc_opcode;
trap_opcode_size = sizeof(g_ppc_opcode);
} break;
case llvm::Triple::ppc64le: {
static const uint8_t g_ppc64le_opcode[] = {0x08, 0x00, 0xe0, 0x7f}; // trap
trap_opcode = g_ppc64le_opcode;
trap_opcode_size = sizeof(g_ppc64le_opcode);
} break;
case llvm::Triple::x86:
case llvm::Triple::x86_64: {
static const uint8_t g_i386_opcode[] = {0xCC};
trap_opcode = g_i386_opcode;
trap_opcode_size = sizeof(g_i386_opcode);
} break;
default:
llvm_unreachable(
"Unhandled architecture in Platform::GetSoftwareBreakpointTrapOpcode");
}
assert(bp_site);
if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size))
return trap_opcode_size;
return 0;
}