diff options
85 files changed, 5862 insertions, 330 deletions
diff --git a/lldb/docs/lldb-gdb-remote.txt b/lldb/docs/lldb-gdb-remote.txt index 1ceda81a174..c857a3306d2 100644 --- a/lldb/docs/lldb-gdb-remote.txt +++ b/lldb/docs/lldb-gdb-remote.txt @@ -421,6 +421,20 @@ invalidate-regs change depending on if the mode has changed. //---------------------------------------------------------------------- +// "qPlatform_RunCommand" +// +// BRIEF +// Run a command in a shell on the connected remote machine. +// +// PRIORITY TO IMPLEMENT +// TODO +//---------------------------------------------------------------------- + +send packet: TODO (see GDBRemoteCommunicationClient::RunShellCommand) +read packet: TODO (see GDBRemoteCommunicationServer::Handle_qPlatform_RunCommand) + + +//---------------------------------------------------------------------- // "qHostInfo" // // BRIEF @@ -848,3 +862,123 @@ for this region. // your debug session more reliable and informative. //---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// PLATFORM EXTENSION - for use as a GDB remote platform +//---------------------------------------------------------------------- +// "qfProcessInfo" +// "qsProcessInfo" +// +// BRIEF +// Get the first process info (qfProcessInfo) or subsequent processs +// info (qsProcessInfo) for one or more processes on the remote +// platform. The first call gets the first match and subsequent calls +// to qsProcessInfo gets the subsequent matches. Return an error EXX, +// where XX are two hex digits, when no more matches are available. +// +// PRIORITY TO IMPLEMENT +// Required. The qfProcessInfo packet can be followed by a ':' and +// some key value pairs. The key value pairs in the command are: +// +// KEY VALUE DESCRIPTION +// =========== ======== ================================================ +// "name" ascii-hex An ASCII hex string that contains the name of +// the process that will be matched. +// "name_match" enum One of: "equals", "starts_with", "ends_with", +// "contains" or "regex" +// "pid" integer A string value containing the decimal process ID +// "parent_pid" integer A string value containing the decimal parent +// process ID +// "uid" integer A string value containing the decimal user ID +// "gid" integer A string value containing the decimal group ID +// "euid" integer A string value containing the decimal effective user ID +// "egid" integer A string value containing the decimal effective group ID +// "all_users" bool A boolean value that specifies if processes should +// be listed for all users, not just the user that the +// platform is running as +// "triple" ascii-hex An ASCII hex target triple string ("x86_64", +// "x86_64-apple-macosx", "armv7-apple-ios") +// +// The response consists of key/value pairs where the key is separated from the +// values with colons and each pair is terminated with a semi colon. For a list +// of the key/value pairs in the response see the "qProcessInfoPID" packet +// documentation. +// +// Sample packet/response: +// send packet: $qfProcessInfo#00 +// read packet: $pid:60001;ppid:59948;uid:7746;gid:11;euid:7746;egid:11;name:6c6c6462;triple:7838365f36342d6170706c652d6d61636f7378;#00 +// send packet: $qsProcessInfo#00 +// read packet: $pid:59992;ppid:192;uid:7746;gid:11;euid:7746;egid:11;name:6d64776f726b6572;triple:7838365f36342d6170706c652d6d61636f7378;#00 +// send packet: $qsProcessInfo#00 +// read packet: $E04#00 +//---------------------------------------------------------------------- + + +//---------------------------------------------------------------------- +// PLATFORM EXTENSION - for use as a GDB remote platform +//---------------------------------------------------------------------- +// "qLaunchGDBServer" +// +// BRIEF +// Have the remote platform launch a GDB server. +// +// PRIORITY TO IMPLEMENT +// Required. The qLaunchGDBServer packet must be followed by a ':' and +// some key value pairs. The key value pairs in the command are: +// +// KEY VALUE DESCRIPTION +// =========== ======== ================================================ +// "port" integer A string value containing the decimal port ID or +// zero if the port should be bound and returned +// +// "host" integer The host that connections should be limited to +// when the GDB server is connected to. +// +// The response consists of key/value pairs where the key is separated from the +// values with colons and each pair is terminated with a semi colon. +// +// Sample packet/response: +// send packet: $qLaunchGDBServer:port:0;host:lldb.apple.com;#00 +// read packet: $pid:60025;port:50776;#00 +// +// The "pid" key/value pair is only specified if the remote platform launched +// a separate process for the GDB remote server and can be omitted if no +// process was separately launched. +// +// The "port" key/value pair in the response lets clients know what port number +// to attach to in case zero was specified as the "port" in the sent command. +//---------------------------------------------------------------------- + + +//---------------------------------------------------------------------- +// PLATFORM EXTENSION - for use as a GDB remote platform +//---------------------------------------------------------------------- +// "qProcessInfoPID:PID" +// +// BRIEF +// Have the remote platform get detailed information on a process by +// ID. PID is specified as a decimal integer. +// +// PRIORITY TO IMPLEMENT +// Optional. +// +// The response consists of key/value pairs where the key is separated from the +// values with colons and each pair is terminated with a semi colon. +// +// The key value pairs in the response are: +// +// KEY VALUE DESCRIPTION +// =========== ======== ================================================ +// "pid" integer Process ID as a decimal integer string +// "ppid" integer Parent process ID as a decimal integer string +// "uid" integer A string value containing the decimal user ID +// "gid" integer A string value containing the decimal group ID +// "euid" integer A string value containing the decimal effective user ID +// "egid" integer A string value containing the decimal effective group ID +// "name" ascii-hex An ASCII hex string that contains the name of the process +// "triple" ascii-hex A target triple ("x86_64-apple-macosx", "armv7-apple-ios") +// +// Sample packet/response: +// send packet: $qProcessInfoPID:60050#00 +// read packet: $pid:60050;ppid:59948;uid:7746;gid:11;euid:7746;egid:11;name:6c6c6462;triple:7838365f36342d6170706c652d6d61636f7378;#00 +//---------------------------------------------------------------------- diff --git a/lldb/include/lldb/Core/StreamGDBRemote.h b/lldb/include/lldb/Core/StreamGDBRemote.h new file mode 100644 index 00000000000..3fdb6f6e701 --- /dev/null +++ b/lldb/include/lldb/Core/StreamGDBRemote.h @@ -0,0 +1,54 @@ +//===-- StreamGDBRemote.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StreamGDBRemote_h_ +#define liblldb_StreamGDBRemote_h_ + +// C Includes +// C++ Includes + +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/StreamString.h" + +namespace lldb_private { + + class StreamGDBRemote : public StreamString + { + public: + StreamGDBRemote (); + + StreamGDBRemote (uint32_t flags, + uint32_t addr_size, + lldb::ByteOrder byte_order); + + virtual + ~StreamGDBRemote (); + + //------------------------------------------------------------------ + /// Output a block of data to the stream performing GDB-remote escaping. + /// + /// @param[in] s + /// A block of data. + /// + /// @param[in] src_len + /// The amount of data to write. + /// + /// @return + /// Number of bytes written. + //------------------------------------------------------------------ + int + PutEscapedBytes (const void* s, + size_t src_len); + }; + +} // namespace lldb_private + +#endif // liblldb_StreamGDBRemote_h_ diff --git a/lldb/include/lldb/Host/File.h b/lldb/include/lldb/Host/File.h index df7fe92cccb..bb04784a2fe 100644 --- a/lldb/include/lldb/Host/File.h +++ b/lldb/include/lldb/Host/File.h @@ -41,17 +41,20 @@ public: eOpenOptionCanCreateNewOnly = (1u << 6) // Can create file only if it doesn't already exist }; + static mode_t + ConvertOpenOptionsForPOSIXOpen (uint32_t open_options); + enum Permissions { - ePermissionsUserRead = (1u << 0), - ePermissionsUserWrite = (1u << 1), - ePermissionsUserExecute = (1u << 2), - ePermissionsGroupRead = (1u << 3), + ePermissionsUserRead = (1u << 8), + ePermissionsUserWrite = (1u << 7), + ePermissionsUserExecute = (1u << 6), + ePermissionsGroupRead = (1u << 5), ePermissionsGroupWrite = (1u << 4), - ePermissionsGroupExecute = (1u << 5), - ePermissionsWorldRead = (1u << 6), - ePermissionsWorldWrite = (1u << 7), - ePermissionsWorldExecute = (1u << 8), + ePermissionsGroupExecute = (1u << 3), + ePermissionsWorldRead = (1u << 2), + ePermissionsWorldWrite = (1u << 1), + ePermissionsWorldExecute = (1u << 0), ePermissionsUserRW = (ePermissionsUserRead | ePermissionsUserWrite | 0 ), ePermissionsUserRX = (ePermissionsUserRead | 0 | ePermissionsUserExecute ), @@ -117,6 +120,27 @@ public: uint32_t options, uint32_t permissions = ePermissionsDefault); + //------------------------------------------------------------------ + /// Constructor with FileSpec. + /// + /// Takes a FileSpec pointing to a file which can be just a filename, or a full + /// path. If \a path is not NULL or empty, this function will call + /// File::Open (const char *path, uint32_t options, uint32_t permissions). + /// + /// @param[in] path + /// The FileSpec for this file. + /// + /// @param[in] options + /// Options to use when opening (see File::OpenOptions) + /// + /// @param[in] permissions + /// Options to use when opening (see File::Permissions) + /// + /// @see File::Open (const char *path, uint32_t options, uint32_t permissions) + //------------------------------------------------------------------ + File (const FileSpec& filespec, + uint32_t options, + uint32_t permissions = ePermissionsDefault); File (int fd, bool tranfer_ownership) : m_descriptor (fd), @@ -451,6 +475,19 @@ public: //------------------------------------------------------------------ Error Sync (); + + //------------------------------------------------------------------ + /// Get the permissions for a this file. + /// + /// @return + /// Bits logical OR'ed together from the permission bits defined + /// in lldb_private::File::Permissions. + //------------------------------------------------------------------ + uint32_t + GetPermissions(Error &error) const; + + static uint32_t + GetPermissions (const char *path, Error &error); //------------------------------------------------------------------ /// Output printf formatted output to the stream. diff --git a/lldb/include/lldb/Host/FileSpec.h b/lldb/include/lldb/Host/FileSpec.h index c58be9ec09d..f4618c14c4c 100644 --- a/lldb/include/lldb/Host/FileSpec.h +++ b/lldb/include/lldb/Host/FileSpec.h @@ -624,6 +624,21 @@ public: static size_t Resolve (const char *src_path, char *dst_path, size_t dst_len); + FileSpec + CopyByAppendingPathComponent (const char *new_path) const; + + FileSpec + CopyByRemovingLastPathComponent () const; + + void + AppendPathComponent (const char *new_path); + + void + RemoveLastPathComponent (); + + const char* + GetLastPathComponent () const; + //------------------------------------------------------------------ /// Resolves the user name at the beginning of \a src_path, and writes the output /// to \a dst_path. Note, \a src_path can contain other path components after the diff --git a/lldb/include/lldb/Host/Host.h b/lldb/include/lldb/Host/Host.h index bb3bc4a495e..5f459e7a07c 100644 --- a/lldb/include/lldb/Host/Host.h +++ b/lldb/include/lldb/Host/Host.h @@ -18,6 +18,7 @@ #include "lldb/lldb-private.h" #include "lldb/Core/StringList.h" +#include "lldb/Host/File.h" namespace lldb_private { @@ -508,6 +509,45 @@ public: DynamicLibraryGetSymbol (void *dynamic_library_handle, const char *symbol_name, Error &error); + + static uint32_t + MakeDirectory (const char* path, mode_t mode); + + static lldb::user_id_t + OpenFile (const FileSpec& file_spec, + uint32_t flags, + mode_t mode, + Error &error); + + static bool + CloseFile (lldb::user_id_t fd, + Error &error); + + static uint64_t + WriteFile (lldb::user_id_t fd, + uint64_t offset, + const void* src, + uint64_t src_len, + Error &error); + + static uint64_t + ReadFile (lldb::user_id_t fd, + uint64_t offset, + void* dst, + uint64_t dst_len, + Error &error); + + static lldb::user_id_t + GetFileSize (const FileSpec& file_spec); + + static bool + GetFileExists (const FileSpec& file_spec); + + static bool + CalculateMD5 (const FileSpec& file_spec, + uint64_t &low, + uint64_t &high); + }; } // namespace lldb_private diff --git a/lldb/include/lldb/Interpreter/Options.h b/lldb/include/lldb/Interpreter/Options.h index ac4daa8f579..ad4e283ba7f 100644 --- a/lldb/include/lldb/Interpreter/Options.h +++ b/lldb/include/lldb/Interpreter/Options.h @@ -448,6 +448,12 @@ protected: void Finalize (); + bool + DidFinalize () + { + return m_did_finalize; + } + virtual Error SetOptionValue (uint32_t option_idx, const char *option_arg); @@ -464,6 +470,10 @@ protected: assert (m_did_finalize); return &m_option_defs[0]; } + + const OptionGroup* + GetGroupWithOption (char short_opt); + struct OptionInfo { OptionInfo (OptionGroup* g, uint32_t i) : diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index 9a66c775d47..6f003daf8c4 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -112,6 +112,7 @@ public: typedef void* (*SWIGPythonGetChildAtIndex) (void *implementor, uint32_t idx); typedef int (*SWIGPythonGetIndexOfChildWithName) (void *implementor, const char* child_name); typedef void* (*SWIGPythonCastPyObjectToSBValue) (void* data); + typedef lldb::ValueObjectSP (*SWIGPythonGetValueObjectSPFromSBValue) (void* data); typedef bool (*SWIGPythonUpdateSynthProviderInstance) (void* data); typedef bool (*SWIGPythonMightHaveChildrenSynthProviderInstance) (void* data); diff --git a/lldb/include/lldb/Target/Platform.h b/lldb/include/lldb/Target/Platform.h index b0a07946e74..115f09c34d3 100644 --- a/lldb/include/lldb/Target/Platform.h +++ b/lldb/include/lldb/Target/Platform.h @@ -22,6 +22,7 @@ #include "lldb/Core/ArchSpec.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/PluginInterface.h" +#include "lldb/Interpreter/Options.h" #include "lldb/Host/Mutex.h" namespace lldb_private { @@ -542,9 +543,190 @@ namespace lldb_private { return false; } + virtual uint32_t + MakeDirectory (const std::string &path, + mode_t mode) + { + return UINT32_MAX; + } + + // this need not be virtual: the core behavior is in + // MakeDirectory(std::string,mode_t) + uint32_t + MakeDirectory (const FileSpec &spec, + mode_t mode); + + virtual lldb::user_id_t + OpenFile (const FileSpec& file_spec, + uint32_t flags, + mode_t mode, + Error &error) + { + return UINT64_MAX; + } + + virtual bool + CloseFile (lldb::user_id_t fd, + Error &error) + { + return false; + } + + virtual lldb::user_id_t + GetFileSize (const FileSpec& file_spec) + { + return UINT64_MAX; + } + + virtual uint64_t + ReadFile (lldb::user_id_t fd, + uint64_t offset, + void *dst, + uint64_t dst_len, + Error &error) + { + error.SetErrorStringWithFormat ("Platform::ReadFile() is not supported in the %s platform", GetName().GetCString()); + return -1; + } + + virtual uint64_t + WriteFile (lldb::user_id_t fd, + uint64_t offset, + const void* src, + uint64_t src_len, + Error &error) + { + error.SetErrorStringWithFormat ("Platform::ReadFile() is not supported in the %s platform", GetName().GetCString()); + return -1; + } + + virtual Error + PutFile (const FileSpec& source, + const FileSpec& destination, + uint32_t uid = UINT32_MAX, + uint32_t gid = UINT32_MAX); + virtual size_t GetEnvironment (StringList &environment); + virtual Error + GetFile (const FileSpec& source, + const FileSpec& destination); + + virtual bool + GetFileExists (const lldb_private::FileSpec& file_spec); + + virtual uint32_t + GetFilePermissions (const lldb_private::FileSpec &file_spec, + Error &error) + { + error.SetErrorStringWithFormat ("Platform::GetFilePermissions() is not supported in the %s platform", GetName().GetCString()); + return 0; + } + + virtual bool + GetSupportsRSync () + { + return m_supports_rsync; + } + + virtual void + SetSupportsRSync(bool flag) + { + m_supports_rsync = flag; + } + + virtual const char* + GetRSyncOpts () + { + return m_rsync_opts.c_str(); + } + + virtual void + SetRSyncOpts (const char* opts) + { + m_rsync_opts.assign(opts); + } + + virtual const char* + GetRSyncPrefix () + { + return m_rsync_prefix.c_str(); + } + + virtual void + SetRSyncPrefix (const char* prefix) + { + m_rsync_prefix.assign(prefix); + } + + virtual bool + GetSupportsSSH () + { + return m_supports_ssh; + } + + virtual void + SetSupportsSSH(bool flag) + { + m_supports_ssh = flag; + } + + virtual const char* + GetSSHOpts () + { + return m_ssh_opts.c_str(); + } + + virtual void + SetSSHOpts (const char* opts) + { + m_ssh_opts.assign(opts); + } + + virtual bool + GetIgnoresRemoteHostname () + { + return m_ignores_remote_hostname; + } + + virtual void + SetIgnoresRemoteHostname(bool flag) + { + m_ignores_remote_hostname = flag; + } + + virtual lldb_private::OptionGroupOptions * + GetConnectionOptions (CommandInterpreter& interpreter) + { + return NULL; + } + + virtual lldb_private::Error + RunShellCommand (const char *command, // Shouldn't be NULL + const char *working_dir, // Pass NULL to use the current working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit + std::string *command_output, // Pass NULL if you don't want the command output + uint32_t timeout_sec); // Timeout in seconds to wait for shell program to finish + + virtual void + SetLocalCacheDirectory (const char* local); + + virtual const char* + GetLocalCacheDirectory (); + + virtual std::string + GetPlatformSpecificConnectionInformation() + { + return ""; + } + + virtual bool + CalculateMD5 (const FileSpec& file_spec, + uint64_t &low, + uint64_t &high); + protected: bool m_is_host; // Set to true when we are able to actually set the OS version while @@ -569,7 +751,14 @@ namespace lldb_private { IDToNameMap m_gid_map; size_t m_max_uid_name_len; size_t m_max_gid_name_len; - + bool m_supports_rsync; + std::string m_rsync_opts; + std::string m_rsync_prefix; + bool m_supports_ssh; + std::string m_ssh_opts; + bool m_ignores_remote_hostname; + std::string m_local_cache_directory; + const char * GetCachedUserName (uint32_t uid) { @@ -750,6 +939,110 @@ namespace lldb_private { private: DISALLOW_COPY_AND_ASSIGN (PlatformList); }; + + class OptionGroupPlatformRSync : public lldb_private::OptionGroup + { + public: + OptionGroupPlatformRSync (); + + virtual + ~OptionGroupPlatformRSync (); + + virtual lldb_private::Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + void + OptionParsingStarting (CommandInterpreter &interpreter); + + const lldb_private::OptionDefinition* + GetDefinitions (); + + virtual uint32_t + GetNumDefinitions (); + + // Options table: Required for subclasses of Options. + + static lldb_private::OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_rsync; + std::string m_rsync_opts; + std::string m_rsync_prefix; + bool m_ignores_remote_hostname; + private: + DISALLOW_COPY_AND_ASSIGN(OptionGroupPlatformRSync); + }; + + class OptionGroupPlatformSSH : public lldb_private::OptionGroup + { + public: + OptionGroupPlatformSSH (); + + virtual + ~OptionGroupPlatformSSH (); + + virtual lldb_private::Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + void + OptionParsingStarting (CommandInterpreter &interpreter); + + virtual uint32_t + GetNumDefinitions (); + + const lldb_private::OptionDefinition* + GetDefinitions (); + + // Options table: Required for subclasses of Options. + + static lldb_private::OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_ssh; + std::string m_ssh_opts; + private: + DISALLOW_COPY_AND_ASSIGN(OptionGroupPlatformSSH); + }; + + class OptionGroupPlatformCaching : public lldb_private::OptionGroup + { + public: + OptionGroupPlatformCaching (); + + virtual + ~OptionGroupPlatformCaching (); + + virtual lldb_private::Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + void + OptionParsingStarting (CommandInterpreter &interpreter); + + virtual uint32_t + GetNumDefinitions (); + + const lldb_private::OptionDefinition* + GetDefinitions (); + + // Options table: Required for subclasses of Options. + + static lldb_private::OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string m_cache_dir; + private: + DISALLOW_COPY_AND_ASSIGN(OptionGroupPlatformCaching); + }; + } // namespace lldb_private #endif // liblldb_Platform_h_ diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index a76228c1e17..169eca60e9a 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -243,6 +243,12 @@ public: return m_arch; } + void + SetArchitecture (ArchSpec arch) + { + m_arch = arch; + } + lldb::pid_t GetProcessID () const { diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 6ec5ed6a35e..9ef342e3e98 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -406,6 +406,9 @@ namespace lldb { eArgTypeOffset, eArgTypeOldPathPrefix, eArgTypeOneLiner, + eArgTypePath, + eArgTypePermissionsNumber, + eArgTypePermissionsString, eArgTypePid, eArgTypePlugin, eArgTypeProcessName, diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index 84af8b64690..d8bc42c9212 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -90,6 +90,7 @@ class ExecutionContext; class ExecutionContextRef; class ExecutionContextRefLocker; class ExecutionContextScope; +class File; class FileSpec; class FileSpecList; class Flags; @@ -119,6 +120,7 @@ struct NameSearchContext; class ObjCLanguageRuntime; class ObjectContainer; class OptionGroup; +class OptionGroupOptions; class OptionGroupPlatform; class ObjectFile; class OperatingSystem; @@ -285,6 +287,7 @@ namespace lldb { typedef std::shared_ptr<lldb_private::DynamicLoader> DynamicLoaderSP; typedef std::shared_ptr<lldb_private::Event> EventSP; typedef std::shared_ptr<lldb_private::ExecutionContextRef> ExecutionContextRefSP; + typedef std::shared_ptr<lldb_private::File> FileSP; typedef std::shared_ptr<lldb_private::Function> FunctionSP; typedef std::shared_ptr<lldb_private::FuncUnwinders> FuncUnwindersSP; typedef std::shared_ptr<lldb_private::InlineFunctionInfo> InlineFunctionInfoSP; diff --git a/lldb/include/lldb/lldb-private-log.h b/lldb/include/lldb/lldb-private-log.h index 31a1c23c5e6..48cce69d36c 100644 --- a/lldb/include/lldb/lldb-private-log.h +++ b/lldb/include/lldb/lldb-private-log.h @@ -44,6 +44,7 @@ #define LIBLLDB_LOG_TARGET (1u << 22) #define LIBLLDB_LOG_MMAP (1u << 23) #define LIBLLDB_LOG_OS (1u << 24) +#define LIBLLDB_LOG_PLATFORM (1u << 25) #define LIBLLDB_LOG_ALL (UINT32_MAX) #define LIBLLDB_LOG_DEFAULT (LIBLLDB_LOG_PROCESS |\ LIBLLDB_LOG_THREAD |\ diff --git a/lldb/lib/Makefile b/lldb/lib/Makefile index 30ed3340b6b..85850e607e2 100644 --- a/lldb/lib/Makefile +++ b/lldb/lib/Makefile @@ -69,7 +69,8 @@ USEDLIBS = lldbAPI.a \ LLVMMCDisassembler.a \ lldbPluginPlatformMacOSX.a \ lldbPluginPlatformLinux.a \ - lldbPluginPlatformFreeBSD.a + lldbPluginPlatformFreeBSD.a \ + lldbPluginPlatformPOSIX.a # Because GCC requires RTTI enabled for lldbCore (see source/Core/Makefile) it is # necessary to also link the clang rewriter libraries so vtable references can diff --git a/lldb/lldb.xcodeproj/project.pbxproj b/lldb/lldb.xcodeproj/project.pbxproj index b2e034306c7..2bf84fd28e6 100644 --- a/lldb/lldb.xcodeproj/project.pbxproj +++ b/lldb/lldb.xcodeproj/project.pbxproj @@ -531,6 +531,9 @@ 944372DD171F6B4300E57C32 /* RegisterContextDummy.h in Headers */ = {isa = PBXBuildFile; fileRef = 944372DB171F6B4300E57C32 /* RegisterContextDummy.h */; }; 9443B122140C18C40013457C /* SBData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9443B121140C18C10013457C /* SBData.cpp */; }; 9443B123140C26AB0013457C /* SBData.h in Headers */ = {isa = PBXBuildFile; fileRef = 9443B120140C18A90013457C /* SBData.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 945759671534941F005A9070 /* PlatformPOSIX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945759651534941F005A9070 /* PlatformPOSIX.cpp */; }; + 945759681534941F005A9070 /* PlatformPOSIX.h in Headers */ = {isa = PBXBuildFile; fileRef = 945759661534941F005A9070 /* PlatformPOSIX.h */; }; + 945E8D80152F6AB40019BCCD /* StreamGDBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 945E8D7F152F6AB40019BCCD /* StreamGDBRemote.cpp */; }; 9452573A16262D0200325455 /* SBDeclaration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9452573916262D0200325455 /* SBDeclaration.cpp */; }; 9456F2241616671900656F91 /* DynamicLibrary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9456F2211616644B00656F91 /* DynamicLibrary.cpp */; }; 94611EB213CCA4A4003A22AF /* RefCounter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94611EB113CCA4A4003A22AF /* RefCounter.cpp */; }; @@ -547,6 +550,7 @@ 947A1D651616476B0017C8D1 /* CommandObjectPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 947A1D631616476A0017C8D1 /* CommandObjectPlugin.h */; }; 949ADF031406F648004833E1 /* ValueObjectConstResultImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 949ADF021406F648004833E1 /* ValueObjectConstResultImpl.cpp */; }; 94B6E76213D88365005F417F /* ValueObjectSyntheticFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94B6E76113D88362005F417F /* ValueObjectSyntheticFilter.cpp */; }; + 94E829CA152D33C1006F96A3 /* lldb-platform in Resources */ = {isa = PBXBuildFile; fileRef = 26DC6A101337FE6900FF7998 /* lldb-platform */; }; 94BA8B6D176F8C9B005A91B5 /* Range.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94BA8B6C176F8C9B005A91B5 /* Range.cpp */; }; 94BA8B70176F97CE005A91B5 /* CommandHistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94BA8B6F176F97CE005A91B5 /* CommandHistory.cpp */; }; 94CB255B16B069770059775D /* CXXFormatterFunctions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94CB255716B069770059775D /* CXXFormatterFunctions.cpp */; }; @@ -712,6 +716,13 @@ remoteGlobalIDString = 2689FFC913353D7A00698AC0; remoteInfo = "lldb-core"; }; + 94E829C8152D33B4006F96A3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 26DC6A0F1337FE6900FF7998; + remoteInfo = "lldb-platform"; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -1562,6 +1573,10 @@ 944372DB171F6B4300E57C32 /* RegisterContextDummy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextDummy.h; path = Utility/RegisterContextDummy.h; sourceTree = "<group>"; }; 9443B120140C18A90013457C /* SBData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBData.h; path = include/lldb/API/SBData.h; sourceTree = "<group>"; }; 9443B121140C18C10013457C /* SBData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SBData.cpp; path = source/API/SBData.cpp; sourceTree = "<group>"; }; + 945759651534941F005A9070 /* PlatformPOSIX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PlatformPOSIX.cpp; path = POSIX/PlatformPOSIX.cpp; sourceTree = "<group>"; }; + 945759661534941F005A9070 /* PlatformPOSIX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlatformPOSIX.h; path = POSIX/PlatformPOSIX.h; sourceTree = "<group>"; }; + 945E8D7D152F6AA80019BCCD /* StreamGDBRemote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StreamGDBRemote.h; path = include/lldb/Core/StreamGDBRemote.h; sourceTree = "<group>"; }; + 945E8D7F152F6AB40019BCCD /* StreamGDBRemote.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StreamGDBRemote.cpp; path = source/Core/StreamGDBRemote.cpp; sourceTree = "<group>"; }; 944DC3481774C99000D7D884 /* python-swigsafecast.swig */ = {isa = PBXFileReference; lastKnownFileType = text; path = "python-swigsafecast.swig"; sourceTree = "<group>"; }; 9452573616262CD000325455 /* SBDeclaration.i */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c.preprocessed; path = SBDeclaration.i; sourceTree = "<group>"; }; 9452573816262CEF00325455 /* SBDeclaration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBDeclaration.h; path = include/lldb/API/SBDeclaration.h; sourceTree = "<group>"; }; @@ -2656,6 +2671,8 @@ 4C6649A214EEE81000B0316F /* StreamCallback.cpp */, 26BC7D7A10F1B77400F91463 /* StreamFile.h */, 26BC7E9210F1B85900F91463 /* StreamFile.cpp */, + 945E8D7D152F6AA80019BCCD /* StreamGDBRemote.h */, + 945E8D7F152F6AB40019BCCD /* StreamGDBRemote.cpp */, 26BC7D7B10F1B77400F91463 /* StreamString.h */, 26BC7E9310F1B85900F91463 /* StreamString.cpp */, 4C626533130F1B0A00C889F6 /* StreamTee.h */, @@ -3154,6 +3171,7 @@ 26C5577E132575B6008FD8FE /* Platform */ = { isa = PBXGroup; children = ( + 9457596415349416005A9070 /* POSIX */, 2694E99814FC0BB30076DE67 /* FreeBSD */, 264A97BC133918A30017F0BE /* GDB Server */, 2694E99F14FC0BBD0076DE67 /* Linux */, @@ -3402,6 +3420,69 @@ path = source/Host/common; sourceTree = "<group>"; }; + 9457596415349416005A9070 /* POSIX */ = { + isa = PBXGroup; + children = ( + 945759651534941F005A9070 /* PlatformPOSIX.cpp */, + 945759661534941F005A9070 /* PlatformPOSIX.h */, + ); + name = POSIX; + sourceTree = "<group>"; + }; + 940DB8C416EA64D400D3C2F1 /* lldb-perf */ = { + isa = PBXGroup; + children = ( + 940DB8C616EA654E00D3C2F1 /* darwin */, + 940DB8C516EA654900D3C2F1 /* lib */, + ); + name = "lldb-perf"; + path = "tools/lldb-perf"; + sourceTree = "<group>"; + }; + 940DB8C516EA654900D3C2F1 /* lib */ = { + isa = PBXGroup; + children = ( + 940DB8CA16EA66FB00D3C2F1 /* Gauge.h */, + 940DB8CE16EA670C00D3C2F1 /* Measurement.h */, + 940DB8D116EA671800D3C2F1 /* MemoryGauge.cpp */, + 940DB8D216EA671800D3C2F1 /* MemoryGauge.h */, + 940DB8D516EA672200D3C2F1 /* Metric.cpp */, + 940DB8D616EA672200D3C2F1 /* Metric.h */, + 940DB8D916EA672D00D3C2F1 /* TestCase.cpp */, + 940DB8DA16EA672D00D3C2F1 /* TestCase.h */, + 940DB8DD16EA673800D3C2F1 /* Timer.cpp */, + 940DB8DE16EA673800D3C2F1 /* Timer.h */, + 940DB8E116EA674000D3C2F1 /* Xcode.cpp */, + 940DB8E216EA674000D3C2F1 /* Xcode.h */, + ); + path = lib; + sourceTree = "<group>"; + }; + 940DB8C616EA654E00D3C2F1 /* darwin */ = { + isa = PBXGroup; + children = ( + 940DB8FB16EA84BF00D3C2F1 /* formatters */, + 940DB8C716EA655400D3C2F1 /* sketch */, + ); + path = darwin; + sourceTree = "<group>"; + }; + 940DB8C716EA655400D3C2F1 /* sketch */ = { + isa = PBXGroup; + children = ( + 940DB8E616EA709400D3C2F1 /* main.cpp */, + ); + path = sketch; + sourceTree = "<group>"; + }; + 940DB8FB16EA84BF00D3C2F1 /* formatters */ = { + isa = PBXGroup; + children = ( + 94DB60F016EA888A00459D9E /* main.cpp */, + ); + path = formatters; + sourceTree = "<group>"; + }; 94CB255616B0683B0059775D /* DataFormatters */ = { isa = PBXGroup; children = ( @@ -3536,6 +3617,7 @@ 2694E99E14FC0BB30076DE67 /* PlatformFreeBSD.h in Headers */, 2694E9A514FC0BBD0076DE67 /* PlatformLinux.h in Headers */, 2663E379152BD1890091EC22 /* ReadWriteLock.h in Headers */, + 945759681534941F005A9070 /* PlatformPOSIX.h in Headers */, 26B1EFAF154638AF00E2DAC7 /* DWARFDeclContext.h in Headers */, 260CC62E15D04377002BF2E0 /* OptionValueArgs.h in Headers */, 260CC62F15D04377002BF2E0 /* OptionValueArray.h in Headers */, @@ -3616,6 +3698,7 @@ buildRules = ( ); dependencies = ( + 94E829C9152D33B4006F96A3 /* PBXTargetDependency */, 2689011513353E9B00698AC0 /* PBXTargetDependency */, 262CFC7211A450CB00946C6C /* PBXTargetDependency */, 26368AF6126B95FA00E8659F /* PBXTargetDependency */, @@ -3766,6 +3849,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 94E829CA152D33C1006F96A3 /* lldb-platform in Resources */, 262CFC7711A4510000946C6C /* debugserver in Resources */, 26368AF7126B960500E8659F /* darwin-debug in Resources */, ); @@ -4269,6 +4353,8 @@ 26FFC19D14FC072100087D58 /* DynamicLoaderPOSIXDYLD.cpp in Sources */, 2694E99D14FC0BB30076DE67 /* PlatformFreeBSD.cpp in Sources */, 2694E9A414FC0BBD0076DE67 /* PlatformLinux.cpp in Sources */, + 945E8D80152F6AB40019BCCD /* StreamGDBRemote.cpp in Sources */, + 945759671534941F005A9070 /* PlatformPOSIX.cpp in Sources */, 26B1EFAE154638AF00E2DAC7 /* DWARFDeclContext.cpp in Sources */, 260CC64815D0440D002BF2E0 /* OptionValueArgs.cpp in Sources */, 260CC64915D0440D002BF2E0 /* OptionValueArray.cpp in Sources */, @@ -4416,6 +4502,11 @@ target = 2689FFC913353D7A00698AC0 /* lldb-core */; targetProxy = 26DC6A151337FE7300FF7998 /* PBXContainerItemProxy */; }; + 94E829C9152D33B4006F96A3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 26DC6A0F1337FE6900FF7998 /* lldb-platform */; + targetProxy = 94E829C8152D33B4006F96A3 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -5127,6 +5218,7 @@ OTHER_LDFLAGS = ( "-lllvmclang", "-lpython", + "-lxml2", "-framework", DebugSymbols, "-framework", @@ -5190,6 +5282,7 @@ OTHER_LDFLAGS = ( "-lllvmclang", "-lpython", + "-lxml2", "-framework", DebugSymbols, "-framework", @@ -5252,6 +5345,7 @@ OTHER_LDFLAGS = ( "-lllvmclang", "-lpython", + "-lxml2", "-framework", DebugSymbols, "-framework", diff --git a/lldb/scripts/Python/python-wrapper.swig b/lldb/scripts/Python/python-wrapper.swig index f3cbf5f1c83..444b9c3e395 100644 --- a/lldb/scripts/Python/python-wrapper.swig +++ b/lldb/scripts/Python/python-wrapper.swig @@ -912,6 +912,19 @@ LLDBSwigPythonCallModuleInit // typemaps and in the extensions (SBInputReader.__del__()).
#include "lldb/API/SBInputReader.h"
#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBValue.h"
+
+SWIGEXPORT lldb::ValueObjectSP
+LLDBSWIGPython_GetValueObjectSPFromSBValue (void* data)
+{
+ lldb::ValueObjectSP valobj_sp;
+ if (data)
+ {
+ lldb::SBValue* sb_ptr = (lldb::SBValue *)data;
+ valobj_sp = sb_ptr->GetSP();
+ }
+ return valobj_sp;
+}
#ifdef __cplusplus
extern "C" {
diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp index aa9b23ac7c6..f13b6722d99 100644 --- a/lldb/source/API/SBValue.cpp +++ b/lldb/source/API/SBValue.cpp @@ -63,20 +63,20 @@ public: lldb::DynamicValueType use_dynamic, bool use_synthetic, const char *name = NULL) : - m_valobj_sp(in_valobj_sp), - m_use_dynamic(use_dynamic), - m_use_synthetic(use_synthetic), - m_name (name) + m_valobj_sp(in_valobj_sp), + m_use_dynamic(use_dynamic), + m_use_synthetic(use_synthetic), + m_name (name) { if (!m_name.IsEmpty() && m_valobj_sp) m_valobj_sp->SetName(m_name); } ValueImpl (const ValueImpl& rhs) : - m_valobj_sp(rhs.m_valobj_sp), - m_use_dynamic(rhs.m_use_dynamic), - m_use_synthetic(rhs.m_use_synthetic), - m_name (rhs.m_name) + m_valobj_sp(rhs.m_valobj_sp), + m_use_dynamic(rhs.m_use_dynamic), + m_use_synthetic(rhs.m_use_synthetic), + m_name (rhs.m_name) { } @@ -120,7 +120,7 @@ public: Target *target = value_sp->GetTargetSP().get(); if (target) api_locker.Lock(target->GetAPIMutex()); - + ProcessSP process_sp(value_sp->GetProcessSP()); if (process_sp && !stop_locker.TryLock (&process_sp->GetRunLock())) { @@ -131,7 +131,7 @@ public: error.SetErrorString ("process must be stopped."); return ValueObjectSP(); } - + if (value_sp->GetDynamicValue(m_use_dynamic)) value_sp = value_sp->GetDynamicValue(m_use_dynamic); if (value_sp->GetSyntheticValue(m_use_synthetic)) @@ -167,7 +167,7 @@ public: { return m_use_synthetic; } - + // All the derived values that we would make from the m_valobj_sp will share // the ExecutionContext with m_valobj_sp, so we don't need to do the calculations // in GetSP to return the Target, Process, Thread or Frame. It is convenient to @@ -207,7 +207,7 @@ public: else return StackFrameSP(); } - + private: lldb::ValueObjectSP m_valobj_sp; lldb::DynamicValueType m_use_dynamic; @@ -227,7 +227,7 @@ public: { return in_value.GetSP(m_stop_locker, m_api_locker, m_lock_error); } - + Error & GetError() { @@ -238,11 +238,11 @@ private: Process::StopLocker m_stop_locker; Mutex::Locker m_api_locker; Error m_lock_error; - + }; SBValue::SBValue () : - m_opaque_sp () +m_opaque_sp () { } @@ -318,7 +318,7 @@ SBValue::GetName() lldb::ValueObjectSP value_sp(GetSP(locker)); if (value_sp) name = value_sp->GetName().GetCString(); - + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) { @@ -327,7 +327,7 @@ SBValue::GetName() else log->Printf ("SBValue(%p)::GetName () => NULL", value_sp.get()); } - + return name; } @@ -350,7 +350,7 @@ SBValue::GetTypeName () else log->Printf ("SBValue(%p)::GetTypeName () => NULL", value_sp.get()); } - + return name; } @@ -359,17 +359,17 @@ SBValue::GetByteSize () { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); size_t result = 0; - + ValueLocker locker; lldb::ValueObjectSP value_sp(GetSP(locker)); if (value_sp) { result = value_sp->GetByteSize(); } - + if (log) log->Printf ("SBValue(%p)::GetByteSize () => %" PRIu64, value_sp.get(), (uint64_t)result); - + return result; } @@ -377,18 +377,18 @@ bool SBValue::IsInScope () { bool result = false; - + ValueLocker locker; lldb::ValueObjectSP value_sp(GetSP(locker)); if (value_sp) { result = value_sp->IsInScope (); } - + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBValue(%p)::IsInScope () => %i", value_sp.get(), result); - + return result; } @@ -396,7 +396,7 @@ const char * SBValue::GetValue () { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - + const char *cstr = NULL; ValueLocker locker; lldb::ValueObjectSP value_sp(GetSP(locker)); @@ -411,7 +411,7 @@ SBValue::GetValue () else log->Printf ("SBValue(%p)::GetValue() => NULL", value_sp.get()); } - + return cstr; } @@ -429,14 +429,14 @@ SBValue::GetValueType () { switch (result) { - case eValueTypeInvalid: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeInvalid", value_sp.get()); break; - case eValueTypeVariableGlobal: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeVariableGlobal", value_sp.get()); break; - case eValueTypeVariableStatic: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeVariableStatic", value_sp.get()); break; - case eValueTypeVariableArgument:log->Printf ("SBValue(%p)::GetValueType () => eValueTypeVariableArgument", value_sp.get()); break; - case eValueTypeVariableLocal: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeVariableLocal", value_sp.get()); break; - case eValueTypeRegister: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeRegister", value_sp.get()); break; - case eValueTypeRegisterSet: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeRegisterSet", value_sp.get()); break; - case eValueTypeConstResult: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeConstResult", value_sp.get()); break; + case eValueTypeInvalid: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeInvalid", value_sp.get()); break; + case eValueTypeVariableGlobal: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeVariableGlobal", value_sp.get()); break; + case eValueTypeVariableStatic: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeVariableStatic", value_sp.get()); break; + case eValueTypeVariableArgument:log->Printf ("SBValue(%p)::GetValueType () => eValueTypeVariableArgument", value_sp.get()); break; + case eValueTypeVariableLocal: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeVariableLocal", value_sp.get()); break; + case eValueTypeRegister: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeRegister", value_sp.get()); break; + case eValueTypeRegisterSet: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeRegisterSet", value_sp.get()); break; + case eValueTypeConstResult: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeConstResult", value_sp.get()); break; } } return result; @@ -499,7 +499,7 @@ SBValue::GetValueDidChange () } if (log) log->Printf ("SBValue(%p)::GetValueDidChange() => %i", value_sp.get(), result); - + return result; } @@ -571,7 +571,7 @@ SBValue::SetValueFromCString (const char *value_str, lldb::SBError& error) if (log) log->Printf ("SBValue(%p)::SetValueFromCString(\"%s\") => %i", value_sp.get(), value_str, success); - + return success; } @@ -765,15 +765,15 @@ SBValue::CreateValueFromAddress(const char* name, lldb::addr_t address, SBType s if (pointee_ast_type) { lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(&address,sizeof(lldb::addr_t))); - + ExecutionContext exe_ctx (value_sp->GetExecutionContextRef()); ValueObjectSP ptr_result_valobj_sp(ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), pointee_ast_type, ConstString(name), buffer, - lldb::endian::InlHostByteOrder(), + lldb::endian::InlHostByteOrder(), exe_ctx.GetAddressByteSize())); - + if (ptr_result_valobj_sp) { ptr_result_valobj_sp->GetValue().SetValueType(Value::eValueTypeLoadAddress); @@ -806,7 +806,7 @@ SBValue::CreateValueFromData (const char* name, SBData data, SBType type) if (value_sp) { ExecutionContext exe_ctx (value_sp->GetExecutionContextRef()); - + new_value_sp = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), type.m_opaque_sp->GetClangASTType(), ConstString(name), @@ -837,7 +837,7 @@ SBValue::GetChildAtIndex (uint32_t idx) if (target_sp) use_dynamic = target_sp->GetPreferDynamicValue(); - + return GetChildAtIndex (idx, use_dynamic, can_create_synthetic); } @@ -846,7 +846,7 @@ SBValue::GetChildAtIndex (uint32_t idx, lldb::DynamicValueType use_dynamic, bool { lldb::ValueObjectSP child_sp; Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - + ValueLocker locker; lldb::ValueObjectSP value_sp(GetSP(locker)); if (value_sp) @@ -870,7 +870,7 @@ SBValue::GetChildAtIndex (uint32_t idx, lldb::DynamicValueType use_dynamic, bool sb_value.SetSP (child_sp, use_dynamic, GetPreferSyntheticValue()); if (log) log->Printf ("SBValue(%p)::GetChildAtIndex (%u) => SBValue(%p)", value_sp.get(), idx, value_sp.get()); - + return sb_value; } @@ -913,9 +913,9 @@ SBValue::GetChildMemberWithName (const char *name, lldb::DynamicValueType use_dy { lldb::ValueObjectSP child_sp; const ConstString str_name (name); - + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - + ValueLocker locker; lldb::ValueObjectSP value_sp(GetSP(locker)); if (value_sp) @@ -925,10 +925,10 @@ SBValue::GetChildMemberWithName (const char *name, lldb::DynamicValueType use_dy SBValue sb_value; sb_value.SetSP(child_sp, use_dynamic_value, GetPreferSyntheticValue()); - + if (log) log->Printf ("SBValue(%p)::GetChildMemberWithName (name=\"%s\") => SBValue(%p)", value_sp.get(), name, value_sp.get()); - + return sb_value; } @@ -1117,7 +1117,7 @@ SBValue::MightHaveChildren () lldb::ValueObjectSP value_sp(GetSP(locker)); if (value_sp) has_children = value_sp->MightHaveChildren(); - + if (log) log->Printf ("SBValue(%p)::MightHaveChildren() => %i", value_sp.get(), has_children); return has_children; @@ -1127,16 +1127,16 @@ uint32_t SBValue::GetNumChildren () { uint32_t num_children = 0; - + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); ValueLocker locker; lldb::ValueObjectSP value_sp(GetSP(locker)); if (value_sp) num_children = value_sp->GetNumChildren(); - + if (log) log->Printf ("SBValue(%p)::GetNumChildren () => %u", value_sp.get(), num_children); - + return num_children; } @@ -1149,13 +1149,13 @@ SBValue::Dereference () lldb::ValueObjectSP value_sp(GetSP(locker)); if (value_sp) { - Error error; - sb_value = value_sp->Dereference (error); + Error error; + sb_value = value_sp->Dereference (error); } Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBValue(%p)::Dereference () => SBValue(%p)", value_sp.get(), value_sp.get()); - + return sb_value; } @@ -1163,17 +1163,17 @@ bool SBValue::TypeIsPointerType () { bool is_ptr_type = false; - + ValueLocker locker; lldb::ValueObjectSP value_sp(GetSP(locker)); if (value_sp) is_ptr_type = value_sp->IsPointerType(); - + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) log->Printf ("SBValue(%p)::TypeIsPointerType () => %i", value_sp.get(), is_ptr_type); - - + + return is_ptr_type; } @@ -1390,7 +1390,7 @@ bool SBValue::GetDescription (SBStream &description) { Stream &strm = description.ref(); - + ValueLocker locker; lldb::ValueObjectSP value_sp(GetSP(locker)); if (value_sp) @@ -1399,7 +1399,7 @@ SBValue::GetDescription (SBStream &description) } else strm.PutCString ("No value"); - + return true; } @@ -1653,7 +1653,7 @@ SBValue::Watch (bool resolve_location, bool read, bool write, SBError &error) size_t byte_size = GetByteSize(); if (byte_size == 0) return sb_watchpoint; - + uint32_t watch_type = 0; if (read) watch_type |= LLDB_WATCH_TYPE_READ; @@ -1664,14 +1664,14 @@ SBValue::Watch (bool resolve_location, bool read, bool write, SBError &error) ClangASTType type (value_sp->GetClangType()); WatchpointSP watchpoint_sp = target_sp->CreateWatchpoint(addr, byte_size, &type, watch_type, rc); error.SetError(rc); - - if (watchpoint_sp) + + if (watchpoint_sp) { sb_watchpoint.SetSP (watchpoint_sp); Declaration decl; if (value_sp->GetDeclaration (decl)) { - if (decl.GetFile()) + if (decl.GetFile()) { StreamString ss; // True to show fullpath for declaration file. diff --git a/lldb/source/CMakeLists.txt b/lldb/source/CMakeLists.txt index eddd8cb095a..2a1292a80d1 100644 --- a/lldb/source/CMakeLists.txt +++ b/lldb/source/CMakeLists.txt @@ -58,6 +58,7 @@ set( LLDB_USED_LIBS lldbPluginPlatformGDB
lldbPluginPlatformFreeBSD
lldbPluginPlatformLinux
+ lldbPluginPlatformPOSIX
lldbPluginObjectFileMachO
lldbPluginObjectContainerMachOArchive
lldbPluginObjectContainerBSDArchive
diff --git a/lldb/source/Commands/CommandObjectPlatform.cpp b/lldb/source/Commands/CommandObjectPlatform.cpp index c2185e598ad..68a1dec1cb9 100644 --- a/lldb/source/Commands/CommandObjectPlatform.cpp +++ b/lldb/source/Commands/CommandObjectPlatform.cpp @@ -26,10 +26,158 @@ #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" +#include "lldb/Utility/Utils.h" using namespace lldb; using namespace lldb_private; +static mode_t +ParsePermissionString(const char* permissions) +{ + if (strlen(permissions) != 9) + return (mode_t)(-1); + bool user_r,user_w,user_x, + group_r,group_w,group_x, + world_r,world_w,world_x; + + user_r = (permissions[0] == 'r'); + user_w = (permissions[1] == 'w'); + user_x = (permissions[2] == 'x'); + + group_r = (permissions[3] == 'r'); + group_w = (permissions[4] == 'w'); + group_x = (permissions[5] == 'x'); + + world_r = (permissions[6] == 'r'); + world_w = (permissions[7] == 'w'); + world_x = (permissions[8] == 'x'); + + mode_t user,group,world; + user = (user_r ? 4 : 0) | (user_w ? 2 : 0) | (user_x ? 1 : 0); + group = (group_r ? 4 : 0) | (group_w ? 2 : 0) | (group_x ? 1 : 0); + world = (world_r ? 4 : 0) | (world_w ? 2 : 0) | (world_x ? 1 : 0); + + return user | group | world; +} + +static OptionDefinition +g_permissions_options[] = +{ + { LLDB_OPT_SET_ALL, false, "permissions-value", 'v', required_argument, NULL, 0, eArgTypePermissionsNumber , "Give out the numeric value for permissions (e.g. 757)" }, + { LLDB_OPT_SET_ALL, false, "permissions-string",'s', required_argument, NULL, 0, eArgTypePermissionsString , "Give out the string value for permissions (e.g. rwxr-xr--)." }, + { LLDB_OPT_SET_ALL, false, "user-read", 'r', no_argument, NULL, 0, eArgTypeNone , "Allow user to read." }, + { LLDB_OPT_SET_ALL, false, "user-write", 'w', no_argument, NULL, 0, eArgTypeNone , "Allow user to write." }, + { LLDB_OPT_SET_ALL, false, "user-exec", 'x', no_argument, NULL, 0, eArgTypeNone , "Allow user to execute." }, + + { LLDB_OPT_SET_ALL, false, "group-read", 'R', no_argument, NULL, 0, eArgTypeNone , "Allow group to read." }, + { LLDB_OPT_SET_ALL, false, "group-write", 'W', no_argument, NULL, 0, eArgTypeNone , "Allow group to write." }, + { LLDB_OPT_SET_ALL, false, "group-exec", 'X', no_argument, NULL, 0, eArgTypeNone , "Allow group to execute." }, + + { LLDB_OPT_SET_ALL, false, "world-read", 'd', no_argument, NULL, 0, eArgTypeNone , "Allow world to read." }, + { LLDB_OPT_SET_ALL, false, "world-write", 't', no_argument, NULL, 0, eArgTypeNone , "Allow world to write." }, + { LLDB_OPT_SET_ALL, false, "world-exec", 'e', no_argument, NULL, 0, eArgTypeNone , "Allow world to execute." }, + +}; + +class OptionPermissions : public lldb_private::OptionGroup +{ +public: + OptionPermissions () + { + } + + virtual + ~OptionPermissions () + { + } + + virtual lldb_private::Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) + { + Error error; + char short_option = (char) GetDefinitions()[option_idx].short_option; + switch (short_option) + { + case 'v': + { + bool ok; + uint32_t perms = Args::StringToUInt32(option_arg, 777, 8, &ok); + if (!ok) + error.SetErrorStringWithFormat("invalid value for permissions: %s", option_arg); + else + m_permissions = perms; + } + break; + case 's': + { + mode_t perms = ParsePermissionString(option_arg); + if (perms == (mode_t)-1) + error.SetErrorStringWithFormat("invalid value for permissions: %s", option_arg); + else + m_permissions = perms; + } + case 'r': + m_permissions |= File::ePermissionsUserRead; + break; + case 'w': + m_permissions |= File::ePermissionsUserWrite; + break; + case 'x': + m_permissions |= File::ePermissionsUserExecute; + break; + case 'R': + m_permissions |= File::ePermissionsGroupRead; + break; + case 'W': + m_permissions |= File::ePermissionsGroupWrite; + break; + case 'X': + m_permissions |= File::ePermissionsGroupExecute; + break; + case 'd': + m_permissions |= File::ePermissionsWorldRead; + break; + case 't': + m_permissions |= File::ePermissionsWorldWrite; + break; + case 'e': + m_permissions |= File::ePermissionsWorldExecute; + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting (CommandInterpreter &interpreter) + { + m_permissions = 0; + } + + virtual uint32_t + GetNumDefinitions () + { + return llvm::array_lengthof(g_permissions_options); + } + + const lldb_private::OptionDefinition* + GetDefinitions () + { + return g_permissions_options; + } + + // Instance variables to hold the values for command options. + + uint32_t m_permissions; +private: + DISALLOW_COPY_AND_ASSIGN(OptionPermissions); +}; //---------------------------------------------------------------------- // "platform select <platform-name>" @@ -274,11 +422,26 @@ protected: } else { - result.AppendError ("no platform us currently selected\n"); + result.AppendError ("no platform is currently selected\n"); result.SetStatus (eReturnStatusFailed); } return result.Succeeded(); } + + virtual Options * + GetOptions () + { + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + OptionGroupOptions* m_platform_options = NULL; + if (platform_sp) + { + m_platform_options = platform_sp->GetConnectionOptions(m_interpreter); + if (m_platform_options != NULL && !m_platform_options->m_did_finalize) + m_platform_options->Finalize(); + } + return m_platform_options; + } + }; //---------------------------------------------------------------------- @@ -359,6 +522,680 @@ protected: return result.Succeeded(); } }; + +//---------------------------------------------------------------------- +// "platform mkdir" +//---------------------------------------------------------------------- +class CommandObjectPlatformMkDir : public CommandObjectParsed +{ +public: + CommandObjectPlatformMkDir (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform shell", + "Make a new directory on the remote end.", + NULL, + 0), + m_options(interpreter) + { + } + + virtual + ~CommandObjectPlatformMkDir () + { + } + + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + if (platform_sp) + { + std::string cmd_line; + args.GetCommandString(cmd_line); + mode_t perms; + const OptionPermissions* options_permissions = (OptionPermissions*)m_options.GetGroupWithOption('r'); + if (options_permissions) + perms = options_permissions->m_permissions; + else + perms = 0000700 | 0000070 | 0000007; + uint32_t retcode = platform_sp->MakeDirectory(cmd_line,perms); + result.AppendMessageWithFormat("Status = %d\n",retcode); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError ("no platform currently selected\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + + virtual Options * + GetOptions () + { + if (m_options.DidFinalize() == false) + { + m_options.Append(new OptionPermissions()); + m_options.Finalize(); + } + return &m_options; + } + OptionGroupOptions m_options; + +}; + +//---------------------------------------------------------------------- +// "platform fopen" +//---------------------------------------------------------------------- +class CommandObjectPlatformFOpen : public CommandObjectParsed +{ +public: + CommandObjectPlatformFOpen (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform file open", + "Open a file on the remote end.", + NULL, + 0), + m_options(interpreter) + { + } + + virtual + ~CommandObjectPlatformFOpen () + { + } + + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + if (platform_sp) + { + Error error; + std::string cmd_line; + args.GetCommandString(cmd_line); + mode_t perms; + const OptionPermissions* options_permissions = (OptionPermissions*)m_options.GetGroupWithOption('r'); + if (options_permissions) + perms = options_permissions->m_permissions; + else + perms = 0000700 | 0000070 | 0000007; + lldb::user_id_t fd = platform_sp->OpenFile(FileSpec(cmd_line.c_str(),false), + File::eOpenOptionRead | File::eOpenOptionWrite | + File::eOpenOptionAppend | File::eOpenOptionCanCreate, + perms, + error); + if (error.Success()) + { + result.AppendMessageWithFormat("File Descriptor = %" PRIu64 "\n",fd); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError(error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("no platform currently selected\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + virtual Options * + GetOptions () + { + if (m_options.DidFinalize() == false) + { + m_options.Append(new OptionPermissions()); + m_options.Finalize(); + } + return &m_options; + } + OptionGroupOptions m_options; +}; + +//---------------------------------------------------------------------- +// "platform fclose" +//---------------------------------------------------------------------- +class CommandObjectPlatformFClose : public CommandObjectParsed +{ +public: + CommandObjectPlatformFClose (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform file close", + "Close a file on the remote end.", + NULL, + 0) + { + } + + virtual + ~CommandObjectPlatformFClose () + { + } + + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + if (platform_sp) + { + std::string cmd_line; + args.GetCommandString(cmd_line); + const lldb::user_id_t fd = Args::StringToUInt64(cmd_line.c_str(), UINT64_MAX); + Error error; + bool success = platform_sp->CloseFile(fd, error); + if (success) + { + result.AppendMessageWithFormat("file %" PRIu64 " closed.\n", fd); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError(error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("no platform currently selected\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +//---------------------------------------------------------------------- +// "platform fread" +//---------------------------------------------------------------------- +class CommandObjectPlatformFRead : public CommandObjectParsed +{ +public: + CommandObjectPlatformFRead (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform file read", + "Read data from a file on the remote end.", + NULL, + 0), + m_options (interpreter) + { + } + + virtual + ~CommandObjectPlatformFRead () + { + } + + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + if (platform_sp) + { + std::string cmd_line; + args.GetCommandString(cmd_line); + const lldb::user_id_t fd = Args::StringToUInt64(cmd_line.c_str(), UINT64_MAX); + std::string buffer(m_options.m_count,0); + Error error; + uint32_t retcode = platform_sp->ReadFile(fd, m_options.m_offset, &buffer[0], m_options.m_count, error); + result.AppendMessageWithFormat("Return = %d\n",retcode); + result.AppendMessageWithFormat("Data = \"%s\"\n",buffer.c_str()); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError ("no platform currently selected\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + char short_option = (char) m_getopt_table[option_idx].val; + bool success = false; + + switch (short_option) + { + case 'o': + m_offset = Args::StringToUInt32(option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat("invalid offset: '%s'", option_arg); + break; + case 'c': + m_count = Args::StringToUInt32(option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat("invalid offset: '%s'", option_arg); + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_offset = 0; + m_count = 1; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + uint32_t m_offset; + uint32_t m_count; + }; + CommandOptions m_options; +}; +OptionDefinition +CommandObjectPlatformFRead::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "offset" , 'o', required_argument, NULL, 0, eArgTypeIndex , "Offset into the file at which to start reading." }, + { LLDB_OPT_SET_1, false, "count" , 'c', required_argument, NULL, 0, eArgTypeCount , "Number of bytes to read from the file." }, + { 0 , false, NULL , 0 , 0 , NULL, 0, eArgTypeNone , NULL } +}; + + +//---------------------------------------------------------------------- +// "platform fwrite" +//---------------------------------------------------------------------- +class CommandObjectPlatformFWrite : public CommandObjectParsed +{ +public: + CommandObjectPlatformFWrite (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform file write", + "Write data to a file on the remote end.", + NULL, + 0), + m_options (interpreter) + { + } + + virtual + ~CommandObjectPlatformFWrite () + { + } + + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + if (platform_sp) + { + std::string cmd_line; + args.GetCommandString(cmd_line); + Error error; + const lldb::user_id_t fd = Args::StringToUInt64(cmd_line.c_str(), UINT64_MAX); + uint32_t retcode = platform_sp->WriteFile (fd, + m_options.m_offset, + &m_options.m_data[0], + m_options.m_data.size(), + error); + result.AppendMessageWithFormat("Return = %d\n",retcode); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError ("no platform currently selected\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + char short_option = (char) m_getopt_table[option_idx].val; + bool success = false; + + switch (short_option) + { + case 'o': + m_offset = Args::StringToUInt32(option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat("invalid offset: '%s'", option_arg); + break; + case 'd': + m_data.assign(option_arg); + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_offset = 0; + m_data.clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + uint32_t m_offset; + std::string m_data; + }; + CommandOptions m_options; +}; +OptionDefinition +CommandObjectPlatformFWrite::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "offset" , 'o', required_argument, NULL, 0, eArgTypeIndex , "Offset into the file at which to start reading." }, + { LLDB_OPT_SET_1, false, "data" , 'd', required_argument, NULL, 0, eArgTypeValue , "Text to write to the file." }, + { 0 , false, NULL , 0 , 0 , NULL, 0, eArgTypeNone , NULL } +}; + +class CommandObjectPlatformFile : public CommandObjectMultiword +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectPlatformFile (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "platform file", + "A set of commands to manage file access through a platform", + "platform file [open|close|read|write] ...") + { + LoadSubCommand ("open", CommandObjectSP (new CommandObjectPlatformFOpen (interpreter))); + LoadSubCommand ("close", CommandObjectSP (new CommandObjectPlatformFClose (interpreter))); + LoadSubCommand ("read", CommandObjectSP (new CommandObjectPlatformFRead (interpreter))); + LoadSubCommand ("write", CommandObjectSP (new CommandObjectPlatformFWrite (interpreter))); + } + + virtual + ~CommandObjectPlatformFile () + { + } + +private: + //------------------------------------------------------------------ + // For CommandObjectPlatform only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (CommandObjectPlatformFile); +}; + +//---------------------------------------------------------------------- +// "platform get-file remote-file-path host-file-path" +//---------------------------------------------------------------------- +class CommandObjectPlatformGetFile : public CommandObjectParsed +{ +public: + CommandObjectPlatformGetFile (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform get-file", + "Transfer a file from the remote end to the local host.", + "platform get-file <remote-file-spec> <local-file-spec>", + 0) + { + SetHelpLong( +"Examples: \n\ +\n\ + platform get-file /the/remote/file/path /the/local/file/path\n\ + # Transfer a file from the remote end with file path /the/remote/file/path to the local host.\n"); + + CommandArgumentEntry arg1, arg2; + CommandArgumentData file_arg_remote, file_arg_host; + + // Define the first (and only) variant of this arg. + file_arg_remote.arg_type = eArgTypeFilename; + file_arg_remote.arg_repetition = eArgRepeatPlain; + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (file_arg_remote); + + // Define the second (and only) variant of this arg. + file_arg_host.arg_type = eArgTypeFilename; + file_arg_host.arg_repetition = eArgRepeatPlain; + // There is only one variant this argument could be; put it into the argument entry. + arg2.push_back (file_arg_host); + + // Push the data for the first and the second arguments into the m_arguments vector. + m_arguments.push_back (arg1); + m_arguments.push_back (arg2); + } + + virtual + ~CommandObjectPlatformGetFile () + { + } + + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + // If the number of arguments is incorrect, issue an error message. + if (args.GetArgumentCount() != 2) + { + result.GetErrorStream().Printf("error: required arguments missing; specify both the source and destination file paths\n"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + if (platform_sp) + { + const char *remote_file_path = args.GetArgumentAtIndex(0); + const char *local_file_path = args.GetArgumentAtIndex(1); + Error error = platform_sp->GetFile(FileSpec(remote_file_path, false), + FileSpec(local_file_path, false)); + if (error.Success()) + { + result.AppendMessageWithFormat("successfully get-file from %s (remote) to %s (host)\n", + remote_file_path, local_file_path); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendMessageWithFormat("get-file failed: %s\n", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("no platform currently selected\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +//---------------------------------------------------------------------- +// "platform get-size remote-file-path" +//---------------------------------------------------------------------- +class CommandObjectPlatformGetSize : public CommandObjectParsed +{ +public: + CommandObjectPlatformGetSize (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform get-size", + "Get the file size from the remote end.", + "platform get-size <remote-file-spec>", + 0) + { + SetHelpLong( +"Examples: \n\ +\n\ + platform get-size /the/remote/file/path\n\ + # Get the file size from the remote end with path /the/remote/file/path.\n"); + + CommandArgumentEntry arg1; + CommandArgumentData file_arg_remote; + + // Define the first (and only) variant of this arg. + file_arg_remote.arg_type = eArgTypeFilename; + file_arg_remote.arg_repetition = eArgRepeatPlain; + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (file_arg_remote); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + } + + virtual + ~CommandObjectPlatformGetSize () + { + } + + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + // If the number of arguments is incorrect, issue an error message. + if (args.GetArgumentCount() != 1) + { + result.GetErrorStream().Printf("error: required argument missing; specify the source file path as the only argument\n"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + if (platform_sp) + { + std::string remote_file_path(args.GetArgumentAtIndex(0)); + user_id_t size = platform_sp->GetFileSize(FileSpec(remote_file_path.c_str(), false)); + if (size != UINT64_MAX) + { + result.AppendMessageWithFormat("File size of %s (remote): %" PRIu64 "\n", remote_file_path.c_str(), size); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendMessageWithFormat("Eroor getting file size of %s (remote)\n", remote_file_path.c_str()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("no platform currently selected\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +//---------------------------------------------------------------------- +// "platform put-file" +//---------------------------------------------------------------------- +class CommandObjectPlatformPutFile : public CommandObjectParsed +{ +public: + CommandObjectPlatformPutFile (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform put-file", + "Transfer a file from this system to the remote end.", + NULL, + 0) + { + } + + virtual + ~CommandObjectPlatformPutFile () + { + } + + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + const char* src = args.GetArgumentAtIndex(0); + const char* dst = args.GetArgumentAtIndex(1); + + FileSpec src_fs(src, true); + FileSpec dst_fs(dst, false); + + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + if (platform_sp) + { + Error error (platform_sp->PutFile(src_fs, dst_fs)); + if (error.Success()) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("no platform currently selected\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + //---------------------------------------------------------------------- // "platform process launch" //---------------------------------------------------------------------- @@ -868,7 +1705,205 @@ protected: } }; +class CommandObjectPlatformProcessAttach : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + char short_option = (char) m_getopt_table[option_idx].val; + bool success = false; + switch (short_option) + { + case 'p': + { + lldb::pid_t pid = Args::StringToUInt32 (option_arg, LLDB_INVALID_PROCESS_ID, 0, &success); + if (!success || pid == LLDB_INVALID_PROCESS_ID) + { + error.SetErrorStringWithFormat("invalid process ID '%s'", option_arg); + } + else + { + attach_info.SetProcessID (pid); + } + } + break; + + case 'P': + attach_info.SetProcessPluginName (option_arg); + break; + + case 'n': + attach_info.GetExecutableFile().SetFile(option_arg, false); + break; + + case 'w': + attach_info.SetWaitForLaunch(true); + break; + + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + } + return error; + } + + void + OptionParsingStarting () + { + attach_info.Clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + virtual bool + HandleOptionArgumentCompletion (Args &input, + int cursor_index, + int char_pos, + OptionElementVector &opt_element_vector, + int opt_element_index, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos; + int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index; + + // We are only completing the name option for now... + + const OptionDefinition *opt_defs = GetDefinitions(); + if (opt_defs[opt_defs_index].short_option == 'n') + { + // Are we in the name? + + // Look to see if there is a -P argument provided, and if so use that plugin, otherwise + // use the default plugin. + + const char *partial_name = NULL; + partial_name = input.GetArgumentAtIndex(opt_arg_pos); + + PlatformSP platform_sp (m_interpreter.GetPlatform (true)); + if (platform_sp) + { + ProcessInstanceInfoList process_infos; + ProcessInstanceInfoMatch match_info; + if (partial_name) + { + match_info.GetProcessInfo().GetExecutableFile().SetFile(partial_name, false); + match_info.SetNameMatchType(eNameMatchStartsWith); + } + platform_sp->FindProcesses (match_info, process_infos); + const uint32_t num_matches = process_infos.GetSize(); + if (num_matches > 0) + { + for (uint32_t i=0; i<num_matches; ++i) + { + matches.AppendString (process_infos.GetProcessNameAtIndex(i), + process_infos.GetProcessNameLengthAtIndex(i)); + } + } + } + } + + return false; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + ProcessAttachInfo attach_info; + }; + + CommandObjectPlatformProcessAttach (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform process attach", + "Attach to a process.", + "platform process attach <cmd-options>"), + m_options (interpreter) + { + } + + ~CommandObjectPlatformProcessAttach () + { + } + + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + if (platform_sp) + { + Error err; + ProcessSP remote_process_sp = + platform_sp->Attach(m_options.attach_info, m_interpreter.GetDebugger(), NULL, m_interpreter.GetDebugger().GetListener(), err); + if (err.Fail()) + { + result.AppendError(err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + else if (remote_process_sp.get() == NULL) + { + result.AppendError("could not attach: unknown reason"); + result.SetStatus (eReturnStatusFailed); + } + else + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError ("no platform is currently selected"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + + Options * + GetOptions () + { + return &m_options; + } + +protected: + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectPlatformProcessAttach::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "plugin", 'P', required_argument, NULL, 0, eArgTypePlugin, "Name of the process plugin you want to use."}, + { LLDB_OPT_SET_1, false, "pid", 'p', required_argument, NULL, 0, eArgTypePid, "The process ID of an existing process to attach to."}, + { LLDB_OPT_SET_2, false, "name", 'n', required_argument, NULL, 0, eArgTypeProcessName, "The name of the process to attach to."}, + { LLDB_OPT_SET_2, false, "waitfor",'w', no_argument, NULL, 0, eArgTypeNone, "Wait for the the process with <process-name> to launch."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; class CommandObjectPlatformProcess : public CommandObjectMultiword @@ -883,7 +1918,7 @@ public: "A set of commands to query, launch and attach to platform processes", "platform process [attach|launch|list] ...") { -// LoadSubCommand ("attach", CommandObjectSP (new CommandObjectPlatformProcessAttach (interpreter))); + LoadSubCommand ("attach", CommandObjectSP (new CommandObjectPlatformProcessAttach (interpreter))); LoadSubCommand ("launch", CommandObjectSP (new CommandObjectPlatformProcessLaunch (interpreter))); LoadSubCommand ("info" , CommandObjectSP (new CommandObjectPlatformProcessInfo (interpreter))); LoadSubCommand ("list" , CommandObjectSP (new CommandObjectPlatformProcessList (interpreter))); @@ -902,16 +1937,84 @@ private: DISALLOW_COPY_AND_ASSIGN (CommandObjectPlatformProcess); }; - +//---------------------------------------------------------------------- +// "platform shell" +//---------------------------------------------------------------------- class CommandObjectPlatformShell : public CommandObjectRaw { public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + } + + virtual + ~CommandOptions () + { + } + + virtual uint32_t + GetNumDefinitions () + { + return 1; + } + + virtual const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + virtual Error + SetOptionValue (uint32_t option_idx, + const char *option_value) + { + Error error; + + const char short_option = (char) g_option_table[option_idx].short_option; + + switch (short_option) + { + case 't': + { + bool success; + timeout = Args::StringToUInt32(option_value, 10, 10, &success); + if (!success) + error.SetErrorStringWithFormat("could not convert \"%s\" to a numeric value.", option_value); + break; + } + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + } + + return error; + } + + virtual void + OptionParsingStarting () + { + timeout = 10; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + uint32_t timeout; + }; + CommandObjectPlatformShell (CommandInterpreter &interpreter) : - CommandObjectRaw (interpreter, - "platform shell", - "Run a shell command on a the selected platform.", - "platform shell <shell-command>", - 0) + CommandObjectRaw (interpreter, + "platform shell", + "Run a shell command on a the selected platform.", + "platform shell <shell-command>", + 0), + m_options(interpreter) { } @@ -920,31 +2023,80 @@ public: { } -protected: virtual bool DoExecute (const char *raw_command_line, CommandReturnObject &result) { - // TODO: Implement "Platform::RunShellCommand()" and switch over to using - // the current platform when it is in the interface. - const char *working_dir = NULL; - std::string output; - int status = -1; - int signo = -1; - Error error (Host::RunShellCommand (raw_command_line, working_dir, &status, &signo, &output, 10)); - if (!output.empty()) - result.GetOutputStream().PutCString(output.c_str()); - if (status > 0) - { - if (signo > 0) - { - const char *signo_cstr = Host::GetSignalAsCString(signo); - if (signo_cstr) - result.GetOutputStream().Printf("error: command returned with status %i and signal %s\n", status, signo_cstr); + const char* expr = NULL; + + // Print out an usage syntax on an empty command line. + if (raw_command_line[0] == '\0') + { + result.GetOutputStream().Printf("%s\n", this->GetSyntax()); + return true; + } + + if (raw_command_line[0] == '-') + { + // We have some options and these options MUST end with --. + const char *end_options = NULL; + const char *s = raw_command_line; + while (s && s[0]) + { + end_options = ::strstr (s, "--"); + if (end_options) + { + end_options += 2; // Get past the "--" + if (::isspace (end_options[0])) + { + expr = end_options; + while (::isspace (*expr)) + ++expr; + break; + } + } + s = end_options; + } + + if (end_options) + { + Args args (raw_command_line, end_options - raw_command_line); + if (!ParseOptions (args, result)) + return false; + } + } + + if (expr == NULL) + expr = raw_command_line; + + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + Error error; + if (platform_sp) + { + const char *working_dir = NULL; + std::string output; + int status = -1; + int signo = -1; + error = (platform_sp->RunShellCommand (expr, working_dir, &status, &signo, &output, m_options.timeout)); + if (!output.empty()) + result.GetOutputStream().PutCString(output.c_str()); + if (status > 0) + { + if (signo > 0) + { + const char *signo_cstr = Host::GetSignalAsCString(signo); + if (signo_cstr) + result.GetOutputStream().Printf("error: command returned with status %i and signal %s\n", status, signo_cstr); + else + result.GetOutputStream().Printf("error: command returned with status %i and signal %i\n", status, signo); + } else - result.GetOutputStream().Printf("error: command returned with status %i and signal %i\n", status, signo); + result.GetOutputStream().Printf("error: command returned with status %i\n", status); } - else - result.GetOutputStream().Printf("error: command returned with status %i\n", status); + } + else + { + result.GetOutputStream().Printf("error: cannot run remote shell commands without a platform\n"); + error.SetErrorString("error: cannot run remote shell commands without a platform"); } if (error.Fail()) @@ -958,6 +2110,208 @@ protected: } return true; } + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectPlatformShell::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "timeout", 't', required_argument, NULL, 0, eArgTypeValue, "Seconds to wait for the remote host to finish running the command."}, +}; + +struct RecurseCopyBaton +{ + const std::string& destination; + const PlatformSP& platform_sp; + Error error; +}; + + +static FileSpec::EnumerateDirectoryResult +RecurseCopy_Callback (void *baton, + FileSpec::FileType file_type, + const FileSpec &spec) +{ + RecurseCopyBaton* rc_baton = (RecurseCopyBaton*)baton; + switch (file_type) + { + case FileSpec::eFileTypePipe: + case FileSpec::eFileTypeSocket: + // we have no way to copy pipes and sockets - ignore them and continue + return FileSpec::eEnumerateDirectoryResultNext; + break; + + case FileSpec::eFileTypeSymbolicLink: + // what to do for symlinks? + return FileSpec::eEnumerateDirectoryResultNext; + break; + + case FileSpec::eFileTypeDirectory: + { + // make the new directory and get in there + FileSpec new_directory(rc_baton->destination.c_str(),false); + new_directory.AppendPathComponent(spec.GetLastPathComponent()); + uint32_t errcode = rc_baton->platform_sp->MakeDirectory(new_directory, 0777); + std::string new_directory_path (new_directory.GetPath()); + if (errcode != 0) + { + rc_baton->error.SetErrorStringWithFormat("unable to setup directory %s on remote end",new_directory_path.c_str()); + return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out + } + + // now recurse + std::string local_path (spec.GetPath()); + RecurseCopyBaton rc_baton2 = { new_directory_path, rc_baton->platform_sp, Error() }; + FileSpec::EnumerateDirectory(local_path.c_str(), true, true, true, RecurseCopy_Callback, &rc_baton2); + if (rc_baton2.error.Fail()) + { + rc_baton->error.SetErrorString(rc_baton2.error.AsCString()); + return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out + } + return FileSpec::eEnumerateDirectoryResultNext; + } + break; + + case FileSpec::eFileTypeRegular: + { + // copy the file and keep going + std::string dest(rc_baton->destination); + dest.append(spec.GetFilename().GetCString()); + Error err = rc_baton->platform_sp->PutFile(spec, FileSpec(dest.c_str(), false)); + if (err.Fail()) + { + rc_baton->error.SetErrorString(err.AsCString()); + return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out + } + return FileSpec::eEnumerateDirectoryResultNext; + } + break; + + case FileSpec::eFileTypeInvalid: + case FileSpec::eFileTypeOther: + case FileSpec::eFileTypeUnknown: + default: + rc_baton->error.SetErrorStringWithFormat("invalid file detected during copy: %s/%s", spec.GetDirectory().GetCString(), spec.GetFilename().GetCString()); + return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out + break; + } +} + +//---------------------------------------------------------------------- +// "platform install" - install a target to a remote end +//---------------------------------------------------------------------- +class CommandObjectPlatformInstall : public CommandObjectParsed +{ +public: + CommandObjectPlatformInstall (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform target-install", + "Install a target (bundle or executable file) to the remote end.", + "platform target-install <local-thing> <remote-sandbox>", + 0) + { + } + + virtual + ~CommandObjectPlatformInstall () + { + } + + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + if (args.GetArgumentCount() != 2) + { + result.AppendError("platform target-install takes two arguments"); + result.SetStatus(eReturnStatusFailed); + return false; + } + // TODO: move the bulk of this code over to the platform itself + std::string local_thing(args.GetArgumentAtIndex(0)); + std::string remote_sandbox(args.GetArgumentAtIndex(1)); + FileSpec source(local_thing.c_str(), true); + if (source.Exists() == false) + { + result.AppendError("source location does not exist or is not accessible"); + result.SetStatus(eReturnStatusFailed); + return false; + } + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + if (!platform_sp) + { + result.AppendError ("no platform currently selected"); + result.SetStatus (eReturnStatusFailed); + return false; + } + FileSpec::FileType source_type(source.GetFileType()); + if (source_type == FileSpec::eFileTypeDirectory) + { + if (platform_sp->GetSupportsRSync()) + { + FileSpec remote_folder(remote_sandbox.c_str(), false); + Error rsync_err = platform_sp->PutFile(source, remote_folder); + if (rsync_err.Success()) + { + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + } + FileSpec remote_folder(remote_sandbox.c_str(), false); + remote_folder.AppendPathComponent(source.GetLastPathComponent()); + // TODO: default permissions are bad + uint32_t errcode = platform_sp->MakeDirectory(remote_folder, 0777); + if (errcode != 0) + { + result.AppendError("unable to setup target directory on remote end"); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + // now recurse + std::string remote_folder_path (remote_folder.GetPath()); + Error err = RecurseCopy(source,remote_folder_path,platform_sp); + if (err.Fail()) + { + result.AppendError(err.AsCString()); + result.SetStatus(eReturnStatusFailed); + } + else + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + else if (source_type == FileSpec::eFileTypeRegular) + { + // just a plain file - push it to remote and be done + remote_sandbox.append(source.GetFilename().GetCString()); + FileSpec destination(remote_sandbox.c_str(),false); + Error err = platform_sp->PutFile(source, destination); + if (err.Success()) + result.SetStatus(eReturnStatusSuccessFinishResult); + else + { + result.AppendError(err.AsCString()); + result.SetStatus(eReturnStatusFailed); + } + return result.Succeeded(); + } + else + { + result.AppendError("source is not a known type of file"); + result.SetStatus(eReturnStatusFailed); + return result.Succeeded(); + } + } +private: + + Error + RecurseCopy (const FileSpec& source, + const std::string& destination, + const PlatformSP& platform_sp) + { + std::string source_path (source.GetPath()); + RecurseCopyBaton baton = { destination, platform_sp, Error() }; + FileSpec::EnumerateDirectory(source_path.c_str(), true, true, true, RecurseCopy_Callback, &baton); + return baton.error; + } }; //---------------------------------------------------------------------- @@ -974,8 +2328,16 @@ CommandObjectPlatform::CommandObjectPlatform(CommandInterpreter &interpreter) : LoadSubCommand ("status", CommandObjectSP (new CommandObjectPlatformStatus (interpreter))); LoadSubCommand ("connect", CommandObjectSP (new CommandObjectPlatformConnect (interpreter))); LoadSubCommand ("disconnect", CommandObjectSP (new CommandObjectPlatformDisconnect (interpreter))); +#ifdef LLDB_CONFIGURATION_DEBUG + LoadSubCommand ("mkdir", CommandObjectSP (new CommandObjectPlatformMkDir (interpreter))); + LoadSubCommand ("file", CommandObjectSP (new CommandObjectPlatformFile (interpreter))); + LoadSubCommand ("get-file", CommandObjectSP (new CommandObjectPlatformGetFile (interpreter))); + LoadSubCommand ("get-size", CommandObjectSP (new CommandObjectPlatformGetSize (interpreter))); + LoadSubCommand ("put-file", CommandObjectSP (new CommandObjectPlatformPutFile (interpreter))); +#endif LoadSubCommand ("process", CommandObjectSP (new CommandObjectPlatformProcess (interpreter))); LoadSubCommand ("shell", CommandObjectSP (new CommandObjectPlatformShell (interpreter))); + LoadSubCommand ("target-install", CommandObjectSP (new CommandObjectPlatformInstall (interpreter))); } diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index 27c6702f8f3..945199eb729 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -158,6 +158,7 @@ public: m_arch_option (), m_platform_options(true), // Do include the "--platform" option in the platform settings by passing true m_core_file (LLDB_OPT_SET_1, false, "core", 'c', 0, eArgTypeFilename, "Fullpath to a core file to use for this target."), + m_platform_path (LLDB_OPT_SET_1, false, "platform-path", 'P', 0, eArgTypePath, "Path to the remote file to use for this target."), m_symbol_file (LLDB_OPT_SET_1, false, "symfile", 's', 0, eArgTypeFilename, "Fullpath to a stand alone debug symbols file for when debug symbols are not in the executable."), m_remote_file (LLDB_OPT_SET_1, false, "remote-file", 'r', 0, eArgTypeFilename, "Fullpath to the file on the remote host if debugging remotely."), m_add_dependents (LLDB_OPT_SET_1, false, "no-dependents", 'd', "Don't load dependent files when creating the target, just add the specified executable.", true, true) @@ -178,6 +179,7 @@ public: m_option_group.Append (&m_arch_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_platform_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_core_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Append (&m_platform_path, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_symbol_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_remote_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append (&m_add_dependents, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); @@ -226,7 +228,7 @@ protected: FileSpec core_file (m_core_file.GetOptionValue().GetCurrentValue()); FileSpec remote_file (m_remote_file.GetOptionValue().GetCurrentValue()); - if (argc == 1 || core_file) + if (argc == 1 || core_file || remote_file) { FileSpec symfile (m_symbol_file.GetOptionValue().GetCurrentValue()); if (symfile) @@ -243,11 +245,70 @@ protected: const char *file_path = command.GetArgumentAtIndex(0); Timer scoped_timer(__PRETTY_FUNCTION__, "(lldb) target create '%s'", file_path); - TargetSP target_sp; + FileSpec file_spec; + + if (file_path) + file_spec.SetFile (file_path, true); + + bool must_set_platform_path = false; + Debugger &debugger = m_interpreter.GetDebugger(); + PlatformSP platform_sp(debugger.GetPlatformList().GetSelectedPlatform ()); + + if (remote_file) + { + // I have a remote file.. two possible cases + if (file_spec && file_spec.Exists()) + { + // if the remote file does not exist, push it there + if (!platform_sp->GetFileExists (remote_file)) + { + Error err = platform_sp->PutFile(file_spec, remote_file); + if (err.Fail()) + { + result.AppendError(err.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + else + { + // there is no local file and we need one + // in order to make the remote ---> local transfer we need a platform + // TODO: if the user has passed in a --platform argument, use it to fetch the right platform + if (!platform_sp) + { + result.AppendError("unable to perform remote debugging without a platform"); + result.SetStatus (eReturnStatusFailed); + return false; + } + if (file_path) + { + // copy the remote file to the local file + Error err = platform_sp->GetFile(remote_file, file_spec); + if (err.Fail()) + { + result.AppendError(err.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + // make up a local file + result.AppendError("remote --> local transfer without local path is not implemented yet"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + + TargetSP target_sp; const char *arch_cstr = m_arch_option.GetArchitectureName(); const bool get_dependent_files = m_add_dependents.GetOptionValue().GetCurrentValue(); Error error (debugger.GetTargetList().CreateTarget (debugger, +// remote_file ? remote_file : file_spec, file_path, arch_cstr, get_dependent_files, @@ -273,6 +334,13 @@ protected: } debugger.GetTargetList().SetSelectedTarget(target_sp.get()); + if (must_set_platform_path) + { + ModuleSpec main_module_spec(file_spec); + ModuleSP module_sp = target_sp->GetSharedModule(main_module_spec); + if (module_sp) + module_sp->SetPlatformFileSpec(remote_file); + } if (core_file) { char core_path[PATH_MAX]; @@ -341,6 +409,7 @@ private: OptionGroupArchitecture m_arch_option; OptionGroupPlatform m_platform_options; OptionGroupFile m_core_file; + OptionGroupFile m_platform_path; OptionGroupFile m_symbol_file; OptionGroupFile m_remote_file; OptionGroupBoolean m_add_dependents; diff --git a/lldb/source/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt index f2946ce1be1..165fb1de640 100644 --- a/lldb/source/Core/CMakeLists.txt +++ b/lldb/source/Core/CMakeLists.txt @@ -51,6 +51,7 @@ add_lldb_library(lldbCore StreamAsynchronousIO.cpp
StreamCallback.cpp
StreamFile.cpp
+ StreamGDBRemote.cpp
StreamString.cpp
StringList.cpp
Timer.cpp
diff --git a/lldb/source/Core/Error.cpp b/lldb/source/Core/Error.cpp index e29f12f0b2c..9ba4b1ef5f2 100644 --- a/lldb/source/Core/Error.cpp +++ b/lldb/source/Core/Error.cpp @@ -238,7 +238,7 @@ Error::LogIfError (Log *log, const char *format, ...) if (err_str == NULL) err_str = "???"; - SetErrorStringWithFormat("error: %s err = %s (0x%8.8x)", arg_msg, err_str, m_code); + SetErrorStringWithFormat("%s err = %s (0x%8.8x)", arg_msg, err_str, m_code); if (log) log->Error("%s", m_string.c_str()); diff --git a/lldb/source/Core/StreamGDBRemote.cpp b/lldb/source/Core/StreamGDBRemote.cpp new file mode 100644 index 00000000000..46cb99ce98a --- /dev/null +++ b/lldb/source/Core/StreamGDBRemote.cpp @@ -0,0 +1,54 @@ +//===-- StreamGDBRemote.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/StreamGDBRemote.h" +#include <stdio.h> + +using namespace lldb; +using namespace lldb_private; + +StreamGDBRemote::StreamGDBRemote () : +StreamString () +{ +} + +StreamGDBRemote::StreamGDBRemote(uint32_t flags, uint32_t addr_size, ByteOrder byte_order) : +StreamString (flags, addr_size, byte_order) +{ +} + +StreamGDBRemote::~StreamGDBRemote() +{ +} + + +int +StreamGDBRemote::PutEscapedBytes (const void* s, + size_t src_len) +{ + int bytes_written = 0; + const uint8_t *src = (const uint8_t *)s; + bool binary_is_set = m_flags.Test(eBinary); + m_flags.Clear(eBinary); + while (src_len) + { + uint8_t byte = *src; + src++; src_len--; + if (byte == 0x23 || byte == 0x24 || byte == 0x7d || byte == 0x2a) + { + bytes_written += PutChar(0x7d); + byte ^= 0x20; + } + bytes_written += PutChar(byte); + }; + if (binary_is_set) + m_flags.Set(eBinary); + return bytes_written; +} + diff --git a/lldb/source/Host/common/File.cpp b/lldb/source/Host/common/File.cpp index 343312fb484..ec33bca4905 100644 --- a/lldb/source/Host/common/File.cpp +++ b/lldb/source/Host/common/File.cpp @@ -83,6 +83,20 @@ File::File(const char *path, uint32_t options, uint32_t permissions) : Open (path, options, permissions); } +File::File (const FileSpec& filespec, + uint32_t options, + uint32_t permissions) : + m_descriptor (kInvalidDescriptor), + m_stream (kInvalidStream), + m_options (0), + m_owned (false) +{ + if (filespec) + { + Open (filespec.GetPath().c_str(), options, permissions); + } +} + File::File (const File &rhs) : m_descriptor (kInvalidDescriptor), m_stream (kInvalidStream), @@ -261,6 +275,53 @@ File::Open (const char *path, uint32_t options, uint32_t permissions) return error; } +uint32_t +File::GetPermissions (const char *path, Error &error) +{ + if (path && path[0]) + { + struct stat file_stats; + if (::stat (path, &file_stats) == -1) + error.SetErrorToErrno(); + else + { + error.Clear(); + return file_stats.st_mode; // All bits from lldb_private::File::Permissions match those in POSIX mode bits + } + } + else + { + if (path) + error.SetErrorString ("invalid path"); + else + error.SetErrorString ("empty path"); + } + return 0; +} + +uint32_t +File::GetPermissions(Error &error) const +{ + int fd = GetDescriptor(); + if (fd != kInvalidDescriptor) + { + struct stat file_stats; + if (::fstat (fd, &file_stats) == -1) + error.SetErrorToErrno(); + else + { + error.Clear(); + return file_stats.st_mode; // All bits from lldb_private::File::Permissions match those in POSIX mode bits + } + } + else + { + error.SetErrorString ("invalid file descriptor"); + } + return 0; +} + + Error File::Close () { @@ -755,3 +816,51 @@ File::PrintfVarArg (const char *format, va_list args) } return result; } + +mode_t +File::ConvertOpenOptionsForPOSIXOpen (uint32_t open_options) +{ + mode_t mode = 0; + if (open_options & eOpenOptionRead && open_options & eOpenOptionWrite) + mode |= O_RDWR; + else if (open_options & eOpenOptionWrite) + mode |= O_WRONLY; + + if (open_options & eOpenOptionAppend) + mode |= O_APPEND; + + if (open_options & eOpenOptionTruncate) + mode |= O_TRUNC; + + if (open_options & eOpenOptionNonBlocking) + mode |= O_NONBLOCK; + + if (open_options & eOpenOptionCanCreateNewOnly) + mode |= O_CREAT | O_EXCL; + else if (open_options & eOpenOptionCanCreate) + mode |= O_CREAT; + + return mode; +} + +#define O_RDONLY 0x0000 /* open for reading only */ +#define O_WRONLY 0x0001 /* open for writing only */ +#define O_RDWR 0x0002 /* open for reading and writing */ +#define O_ACCMODE 0x0003 /* mask for above modes */ +#define O_NONBLOCK 0x0004 /* no delay */ +#define O_APPEND 0x0008 /* set append mode */ +#define O_SYNC 0x0080 /* synch I/O file integrity */ +#define O_SHLOCK 0x0010 /* open with shared file lock */ +#define O_EXLOCK 0x0020 /* open with exclusive file lock */ +#define O_ASYNC 0x0040 /* signal pgrp when data ready */ +#define O_FSYNC O_SYNC /* source compatibility: do not use */ +#define O_NOFOLLOW 0x0100 /* don't follow symlinks */ +#define O_CREAT 0x0200 /* create if nonexistant */ +#define O_TRUNC 0x0400 /* truncate to zero length */ +#define O_EXCL 0x0800 /* error if already exists */ +#define O_EVTONLY 0x8000 /* descriptor requested for event notifications only */ +#define O_NOCTTY 0x20000 /* don't assign controlling terminal */ +#define O_DIRECTORY 0x100000 +#define O_SYMLINK 0x200000 /* allow open of a symlink */ +#define O_DSYNC 0x400000 /* synch I/O data integrity */ +#define O_CLOEXEC 0x1000000 /* implicitly set FD_CLOEXEC */ diff --git a/lldb/source/Host/common/FileSpec.cpp b/lldb/source/Host/common/FileSpec.cpp index 0bf7681aa48..66818453bc0 100644 --- a/lldb/source/Host/common/FileSpec.cpp +++ b/lldb/source/Host/common/FileSpec.cpp @@ -29,6 +29,7 @@ #include "llvm/Support/Path.h" #include "llvm/Support/Program.h" +#include "lldb/Core/StreamString.h" #include "lldb/Host/File.h" #include "lldb/Host/FileSpec.h" #include "lldb/Core/DataBufferHeap.h" @@ -1106,6 +1107,140 @@ FileSpec::EnumerateDirectory return eEnumerateDirectoryResultNext; } +FileSpec +FileSpec::CopyByAppendingPathComponent (const char *new_path) const +{ + const bool resolve = false; + if (m_filename.IsEmpty() && m_directory.IsEmpty()) + return FileSpec(new_path,resolve); + StreamString stream; + if (m_filename.IsEmpty()) + stream.Printf("%s/%s",m_directory.GetCString(),new_path); + else if (m_directory.IsEmpty()) + stream.Printf("%s/%s",m_filename.GetCString(),new_path); + else + stream.Printf("%s/%s/%s",m_directory.GetCString(), m_filename.GetCString(),new_path); + return FileSpec(stream.GetData(),resolve); +} + +FileSpec +FileSpec::CopyByRemovingLastPathComponent () const +{ + const bool resolve = false; + if (m_filename.IsEmpty() && m_directory.IsEmpty()) + return FileSpec("",resolve); + if (m_directory.IsEmpty()) + return FileSpec("",resolve); + if (m_filename.IsEmpty()) + { + const char* dir_cstr = m_directory.GetCString(); + const char* last_slash_ptr = ::strrchr(dir_cstr, '/'); + + // check for obvious cases before doing the full thing + if (!last_slash_ptr) + return FileSpec("",resolve); + if (last_slash_ptr == dir_cstr) + return FileSpec("/",resolve); + + size_t last_slash_pos = last_slash_ptr - dir_cstr+1; + ConstString new_path(dir_cstr,last_slash_pos); + return FileSpec(new_path.GetCString(),resolve); + } + else + return FileSpec(m_directory.GetCString(),resolve); +} + +const char* +FileSpec::GetLastPathComponent () const +{ + if (m_filename.IsEmpty() && m_directory.IsEmpty()) + return NULL; + if (m_filename.IsEmpty()) + { + const char* dir_cstr = m_directory.GetCString(); + const char* last_slash_ptr = ::strrchr(dir_cstr, '/'); + if (last_slash_ptr == NULL) + return m_directory.GetCString(); + if (last_slash_ptr == dir_cstr) + { + if (last_slash_ptr[1] == 0) + return last_slash_ptr; + else + return last_slash_ptr+1; + } + if (last_slash_ptr[1] != 0) + return last_slash_ptr+1; + const char* penultimate_slash_ptr = last_slash_ptr; + while (*penultimate_slash_ptr) + { + --penultimate_slash_ptr; + if (penultimate_slash_ptr == dir_cstr) + break; + if (*penultimate_slash_ptr == '/') + break; + } + ConstString new_path(penultimate_slash_ptr+1,last_slash_ptr-penultimate_slash_ptr); + return new_path.AsCString(); + } + return m_filename.GetCString(); +} + +void +FileSpec::AppendPathComponent (const char *new_path) +{ + const bool resolve = false; + if (m_filename.IsEmpty() && m_directory.IsEmpty()) + { + SetFile(new_path,resolve); + return; + } + StreamString stream; + if (m_filename.IsEmpty()) + stream.Printf("%s/%s",m_directory.GetCString(),new_path); + else if (m_directory.IsEmpty()) + stream.Printf("%s/%s",m_filename.GetCString(),new_path); + else + stream.Printf("%s/%s/%s",m_directory.GetCString(), m_filename.GetCString(),new_path); + SetFile(stream.GetData(), resolve); +} + +void +FileSpec::RemoveLastPathComponent () +{ + const bool resolve = false; + if (m_filename.IsEmpty() && m_directory.IsEmpty()) + { + SetFile("",resolve); + return; + } + if (m_directory.IsEmpty()) + { + SetFile("",resolve); + return; + } + if (m_filename.IsEmpty()) + { + const char* dir_cstr = m_directory.GetCString(); + const char* last_slash_ptr = ::strrchr(dir_cstr, '/'); + + // check for obvious cases before doing the full thing + if (!last_slash_ptr) + { + SetFile("",resolve); + return; + } + if (last_slash_ptr == dir_cstr) + { + SetFile("/",resolve); + return; + } + size_t last_slash_pos = last_slash_ptr - dir_cstr+1; + ConstString new_path(dir_cstr,last_slash_pos); + SetFile(new_path.GetCString(),resolve); + } + else + SetFile(m_directory.GetCString(),resolve); +} //------------------------------------------------------------------ /// Returns true if the filespec represents an implementation source /// file (files with a ".c", ".cpp", ".m", ".mm" (many more) diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 826069b358c..efe97237d42 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -1472,7 +1472,11 @@ Host::RunShellCommand (const char *command, error = LaunchProcess (launch_info); const lldb::pid_t pid = launch_info.GetProcessID(); - if (pid != LLDB_INVALID_PROCESS_ID) + + if (error.Success() && pid == LLDB_INVALID_PROCESS_ID) + error.SetErrorString("failed to get process ID"); + + if (error.Success()) { // The process successfully launched, so we can defer ownership of // "shell_info" to the MonitorShellCommand callback function that will @@ -1524,10 +1528,6 @@ Host::RunShellCommand (const char *command, } shell_info->can_delete.SetValue(true, eBroadcastAlways); } - else - { - error.SetErrorString("failed to get process ID"); - } if (output_file_path) ::unlink (output_file_path); @@ -1613,9 +1613,169 @@ Host::SetCrashDescription (const char *description) } lldb::pid_t -LaunchApplication (const FileSpec &app_file_spec) +Host::LaunchApplication (const FileSpec &app_file_spec) { return LLDB_INVALID_PROCESS_ID; } +uint32_t +Host::MakeDirectory (const char* path, mode_t mode) +{ + return UINT32_MAX; +} +#endif + +typedef std::map<lldb::user_id_t, lldb::FileSP> FDToFileMap; +FDToFileMap& GetFDToFileMap() +{ + static FDToFileMap g_fd2filemap; + return g_fd2filemap; +} + +lldb::user_id_t +Host::OpenFile (const FileSpec& file_spec, + uint32_t flags, + mode_t mode, + Error &error) +{ + std::string path (file_spec.GetPath()); + if (path.empty()) + { + error.SetErrorString("empty path"); + return UINT64_MAX; + } + FileSP file_sp(new File()); + error = file_sp->Open(path.c_str(),flags,mode); + if (file_sp->IsValid() == false) + return UINT64_MAX; + lldb::user_id_t fd = file_sp->GetDescriptor(); + GetFDToFileMap()[fd] = file_sp; + return fd; +} + +bool +Host::CloseFile (lldb::user_id_t fd, Error &error) +{ + if (fd == UINT64_MAX) + { + error.SetErrorString ("invalid file descriptor"); + return false; + } + FDToFileMap& file_map = GetFDToFileMap(); + FDToFileMap::iterator pos = file_map.find(fd); + if (pos == file_map.end()) + { + error.SetErrorStringWithFormat ("invalid host file descriptor %" PRIu64, fd); + return false; + } + FileSP file_sp = pos->second; + if (!file_sp) + { + error.SetErrorString ("invalid host backing file"); + return false; + } + error = file_sp->Close(); + file_map.erase(pos); + return error.Success(); +} + +uint64_t +Host::WriteFile (lldb::user_id_t fd, uint64_t offset, const void* src, uint64_t src_len, Error &error) +{ + if (fd == UINT64_MAX) + { + error.SetErrorString ("invalid file descriptor"); + return UINT64_MAX; + } + FDToFileMap& file_map = GetFDToFileMap(); + FDToFileMap::iterator pos = file_map.find(fd); + if (pos == file_map.end()) + { + error.SetErrorStringWithFormat("invalid host file descriptor %" PRIu64 , fd); + return false; + } + FileSP file_sp = pos->second; + if (!file_sp) + { + error.SetErrorString ("invalid host backing file"); + return UINT64_MAX; + } + if (file_sp->SeekFromStart(offset, &error) != offset || error.Fail()) + return UINT64_MAX; + size_t bytes_written = src_len; + error = file_sp->Write(src, bytes_written); + if (error.Fail()) + return UINT64_MAX; + return bytes_written; +} + +uint64_t +Host::ReadFile (lldb::user_id_t fd, uint64_t offset, void* dst, uint64_t dst_len, Error &error) +{ + if (fd == UINT64_MAX) + { + error.SetErrorString ("invalid file descriptor"); + return UINT64_MAX; + } + FDToFileMap& file_map = GetFDToFileMap(); + FDToFileMap::iterator pos = file_map.find(fd); + if (pos == file_map.end()) + { + error.SetErrorStringWithFormat ("invalid host file descriptor %" PRIu64, fd); + return false; + } + FileSP file_sp = pos->second; + if (!file_sp) + { + error.SetErrorString ("invalid host backing file"); + return UINT64_MAX; + } + if (file_sp->SeekFromStart(offset, &error) != offset || error.Fail()) + return UINT64_MAX; + size_t bytes_read = dst_len; + error = file_sp->Read(dst ,bytes_read); + if (error.Fail()) + return UINT64_MAX; + return bytes_read; +} + +lldb::user_id_t +Host::GetFileSize (const FileSpec& file_spec) +{ + return file_spec.GetByteSize(); +} + +bool +Host::GetFileExists (const FileSpec& file_spec) +{ + return file_spec.Exists(); +} + +bool +Host::CalculateMD5 (const FileSpec& file_spec, + uint64_t &low, + uint64_t &high) +{ +#if defined (__APPLE__) + StreamString md5_cmd_line; + md5_cmd_line.Printf("md5 -q '%s'", file_spec.GetPath().c_str()); + std::string hash_string; + Error err = Host::RunShellCommand(md5_cmd_line.GetData(), NULL, NULL, NULL, &hash_string, 60); + if (err.Fail()) + return false; + // a correctly formed MD5 is 16-bytes, that is 32 hex digits + // if the output is any other length it is probably wrong + if (hash_string.size() != 32) + return false; + std::string part1(hash_string,0,16); + std::string part2(hash_string,16); + const char* part1_cstr = part1.c_str(); + const char* part2_cstr = part2.c_str(); + high = ::strtoull(part1_cstr, NULL, 16); + low = ::strtoull(part2_cstr, NULL, 16); + return true; +#else + // your own MD5 implementation here + return false; #endif +} diff --git a/lldb/source/Host/freebsd/Host.cpp b/lldb/source/Host/freebsd/Host.cpp index 405e7eacf5a..b748cdfa4da 100644 --- a/lldb/source/Host/freebsd/Host.cpp +++ b/lldb/source/Host/freebsd/Host.cpp @@ -15,11 +15,13 @@ #include <sys/user.h> #include <sys/utsname.h> #include <sys/sysctl.h> +#include <sys/proc.h> #include <sys/ptrace.h> #include <sys/exec.h> #include <machine/elf.h> +#include <spawn.h> // C++ Includes // Other libraries and framework includes @@ -27,13 +29,17 @@ #include "lldb/Core/Error.h" #include "lldb/Host/Endian.h" #include "lldb/Host/Host.h" +#include "lldb/Core/Module.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/StreamString.h" #include "lldb/Target/Process.h" +#include "lldb/Target/Platform.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/DataExtractor.h" +#include "lldb/Utility/CleanUp.h" + #include "llvm/Support/Host.h" @@ -44,7 +50,6 @@ extern "C" { using namespace lldb; using namespace lldb_private; - class FreeBSDThread { public: @@ -120,7 +125,8 @@ Host::GetEnvironment (StringList &env) { char *v; char **var = environ; - for (; var != NULL && *var != NULL; ++var) { + for (; var != NULL && *var != NULL; ++var) + { v = strchr(*var, (int)'-'); if (v == NULL) continue; @@ -135,8 +141,8 @@ Host::GetOSVersion(uint32_t &major, uint32_t &update) { struct utsname un; - int status; + ::memset(&un, 0, sizeof(utsname)); if (uname(&un) < 0) return false; @@ -144,11 +150,196 @@ Host::GetOSVersion(uint32_t &major, return status == 2; } +// The posix_spawn() and posix_spawnp() functions first appeared in FreeBSD 8.0. +static Error +LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_info, ::pid_t &pid) +{ + Error error; + LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); + + assert(exe_path); + assert(!launch_info.GetFlags().Test (eLaunchFlagDebug)); + + posix_spawnattr_t attr; + + error.SetError( ::posix_spawnattr_init (&attr), eErrorTypePOSIX); + error.LogIfError(log.get(), "::posix_spawnattr_init ( &attr )"); + if (error.Fail()) + return error; + + // Make a quick class that will cleanup the posix spawn attributes in case + // we return in the middle of this function. + lldb_utility::CleanUp <posix_spawnattr_t *, int> posix_spawnattr_cleanup(&attr, posix_spawnattr_destroy); + + sigset_t no_signals; + sigset_t all_signals; + sigemptyset (&no_signals); + sigfillset (&all_signals); + ::posix_spawnattr_setsigmask(&attr, &all_signals); + ::posix_spawnattr_setsigdefault(&attr, &no_signals); + + short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; + + error.SetError( ::posix_spawnattr_setflags (&attr, flags), eErrorTypePOSIX); + error.LogIfError(log.get(), "::posix_spawnattr_setflags ( &attr, flags=0x%8.8x )", flags); + if (error.Fail()) + return error; + + const size_t num_file_actions = launch_info.GetNumFileActions (); + posix_spawn_file_actions_t file_actions, *file_action_ptr = NULL; + // Make a quick class that will cleanup the posix spawn attributes in case + // we return in the middle of this function. + lldb_utility::CleanUp <posix_spawn_file_actions_t *, int> + posix_spawn_file_actions_cleanup (file_action_ptr, NULL, posix_spawn_file_actions_destroy); + + if (num_file_actions > 0) + { + error.SetError( ::posix_spawn_file_actions_init (&file_actions), eErrorTypePOSIX); + error.LogIfError(log.get(), "::posix_spawn_file_actions_init ( &file_actions )"); + if (error.Fail()) + return error; + + file_action_ptr = &file_actions; + posix_spawn_file_actions_cleanup.set(file_action_ptr); + + for (size_t i = 0; i < num_file_actions; ++i) + { + const ProcessLaunchInfo::FileAction *launch_file_action = launch_info.GetFileActionAtIndex(i); + if (launch_file_action && + !ProcessLaunchInfo::FileAction::AddPosixSpawnFileAction (&file_actions, + launch_file_action, + log.get(), + error)) + return error; + } + } + + // Change working directory if neccessary. + char current_dir[PATH_MAX]; + current_dir[0] = '\0'; + + const char *working_dir = launch_info.GetWorkingDirectory(); + if (working_dir != NULL) + { + if (::getcwd(current_dir, sizeof(current_dir)) == NULL) + { + error.SetError(errno, eErrorTypePOSIX); + error.LogIfError(log.get(), "unable to save the current directory"); + return error; + } + + if (::chdir(working_dir) == -1) + { + error.SetError(errno, eErrorTypePOSIX); + error.LogIfError(log.get(), "unable to change working directory to %s", working_dir); + return error; + } + } + + const char *tmp_argv[2]; + char * const *argv = (char * const*)launch_info.GetArguments().GetConstArgumentVector(); + char * const *envp = (char * const*)launch_info.GetEnvironmentEntries().GetConstArgumentVector(); + + // Prepare minimal argument list if we didn't get it from the launch_info structure. + // We must pass argv into posix_spawnp and it must contain at least two items - + // pointer to an executable and NULL. + if (argv == NULL) + { + tmp_argv[0] = exe_path; + tmp_argv[1] = NULL; + argv = (char * const*)tmp_argv; + } + + error.SetError (::posix_spawnp (&pid, + exe_path, + (num_file_actions > 0) ? &file_actions : NULL, + &attr, + argv, + envp), + eErrorTypePOSIX); + + error.LogIfError(log.get(), "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", + pid, exe_path, file_action_ptr, &attr, argv, envp); + + // Change back the current directory. + // NOTE: do not override previously established error from posix_spawnp. + if (working_dir != NULL && ::chdir(current_dir) == -1 && error.Success()) + { + error.SetError(errno, eErrorTypePOSIX); + error.LogIfError(log.get(), "unable to change current directory back to %s", + current_dir); + } + + return error; +} + + Error Host::LaunchProcess (ProcessLaunchInfo &launch_info) { Error error; - assert(!"Not implemented yet!!!"); + char exe_path[PATH_MAX]; + + PlatformSP host_platform_sp (Platform::GetDefaultPlatform ()); + + const ArchSpec &arch_spec = launch_info.GetArchitecture(); + + FileSpec exe_spec(launch_info.GetExecutableFile()); + + FileSpec::FileType file_type = exe_spec.GetFileType(); + if (file_type != FileSpec::eFileTypeRegular) + { + lldb::ModuleSP exe_module_sp; + error = host_platform_sp->ResolveExecutable (exe_spec, + arch_spec, + exe_module_sp, + NULL); + + if (error.Fail()) + return error; + + if (exe_module_sp) + exe_spec = exe_module_sp->GetFileSpec(); +} + + if (exe_spec.Exists()) + { + exe_spec.GetPath (exe_path, sizeof(exe_path)); + } + else + { + launch_info.GetExecutableFile().GetPath (exe_path, sizeof(exe_path)); + error.SetErrorStringWithFormat ("executable doesn't exist: '%s'", exe_path); + return error; + } + + assert(!launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY)); + + ::pid_t pid = LLDB_INVALID_PROCESS_ID; + + error = LaunchProcessPosixSpawn(exe_path, launch_info, pid); + + if (pid != LLDB_INVALID_PROCESS_ID) + { + // If all went well, then set the process ID into the launch info + launch_info.SetProcessID(pid); + + // Make sure we reap any processes we spawn or we will have zombies. + if (!launch_info.MonitorProcess()) + { + const bool monitor_signals = false; + StartMonitoringChildProcess (Process::SetProcessExitStatus, + NULL, + pid, + monitor_signals); + } + } + else + { + // Invalid process ID, something didn't go well + if (error.Success()) + error.SetErrorString ("process launch failed for unknown reasons"); + } return error; } @@ -156,13 +347,17 @@ bool Host::GetOSBuildString (std::string &s) { int mib[2] = { CTL_KERN, KERN_OSREV }; - char cstr[PATH_MAX]; - size_t cstr_len = sizeof(cstr); - if (::sysctl (mib, 2, cstr, &cstr_len, NULL, 0) == 0) + char osrev_str[12]; + uint32_t osrev = 0; + size_t osrev_len = sizeof(osrev); + + if (::sysctl (mib, 2, &osrev, &osrev_len, NULL, 0) == 0) { - s.assign (cstr, cstr_len); + ::snprintf(osrev_str, sizeof(osrev_str), "%-8.8u", osrev); + s.assign (osrev_str); return true; } + s.clear(); return false; } @@ -170,23 +365,25 @@ Host::GetOSBuildString (std::string &s) bool Host::GetOSKernelDescription (std::string &s) { - int mib[2] = { CTL_KERN, KERN_VERSION }; - char cstr[PATH_MAX]; - size_t cstr_len = sizeof(cstr); - if (::sysctl (mib, 2, cstr, &cstr_len, NULL, 0) == 0) - { - s.assign (cstr, cstr_len); - return true; - } + struct utsname un; + + ::memset(&un, 0, sizeof(utsname)); s.clear(); + + if (uname(&un) < 0) return false; + + s.assign (un.version); + + return true; } static bool GetFreeBSDProcessArgs (const ProcessInstanceInfoMatch *match_info_ptr, ProcessInstanceInfo &process_info) { - if (process_info.ProcessIDIsValid()) { + if (process_info.ProcessIDIsValid()) + { int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ARGS, (int)process_info.GetProcessID() }; char arg_data[8192]; @@ -235,7 +432,8 @@ GetFreeBSDProcessArgs (const ProcessInstanceInfoMatch *match_info_ptr, static bool GetFreeBSDProcessCPUType (ProcessInstanceInfo &process_info) { - if (process_info.ProcessIDIsValid()) { + if (process_info.ProcessIDIsValid()) + { process_info.GetArchitecture() = Host::GetArchitecture (Host::eSystemDefaultArchitecture); return true; } @@ -279,16 +477,95 @@ GetFreeBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) return false; } +uint32_t +Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) +{ + std::vector<struct kinfo_proc> kinfos; + + int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; + + size_t pid_data_size = 0; + if (::sysctl (mib, 3, NULL, &pid_data_size, NULL, 0) != 0) + return 0; + + // Add a few extra in case a few more show up + const size_t estimated_pid_count = (pid_data_size / sizeof(struct kinfo_proc)) + 10; + + kinfos.resize (estimated_pid_count); + pid_data_size = kinfos.size() * sizeof(struct kinfo_proc); + + if (::sysctl (mib, 3, &kinfos[0], &pid_data_size, NULL, 0) != 0) + return 0; + + const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc)); + + bool all_users = match_info.GetMatchAllUsers(); + const lldb::pid_t our_pid = getpid(); + const uid_t our_uid = getuid(); + for (int i = 0; i < actual_pid_count; i++) + { + const struct kinfo_proc &kinfo = kinfos[i]; + const bool kinfo_user_matches = (all_users || + (kinfo.ki_ruid == our_uid) || + // Special case, if lldb is being run as root we can attach to anything. + (our_uid == 0) + ); + + if (kinfo_user_matches == false || // Make sure the user is acceptable + kinfo.ki_pid == our_pid || // Skip this process + kinfo.ki_pid == 0 || // Skip kernel (kernel pid is zero) + kinfo.ki_stat == SZOMB || // Zombies are bad, they like brains... + kinfo.ki_flag & P_TRACED || // Being debugged? + kinfo.ki_flag & P_WEXIT) // Working on exiting + continue; + + // Every thread is a process in FreeBSD, but all the threads of a single process + // have the same pid. Do not store the process info in the result list if a process + // with given identifier is already registered there. + bool already_registered = false; + for (uint32_t pi = 0; + !already_registered && + (const int)kinfo.ki_numthreads > 1 && + pi < (const uint32_t)process_infos.GetSize(); pi++) + already_registered = (process_infos.GetProcessIDAtIndex(pi) == (uint32_t)kinfo.ki_pid); + + if (already_registered) + continue; + + ProcessInstanceInfo process_info; + process_info.SetProcessID (kinfo.ki_pid); + process_info.SetParentProcessID (kinfo.ki_ppid); + process_info.SetUserID (kinfo.ki_ruid); + process_info.SetGroupID (kinfo.ki_rgid); + process_info.SetEffectiveUserID (kinfo.ki_svuid); + process_info.SetEffectiveGroupID (kinfo.ki_svgid); + + // Make sure our info matches before we go fetch the name and cpu type + if (match_info.Matches (process_info) && + GetFreeBSDProcessArgs (&match_info, process_info)) + { + GetFreeBSDProcessCPUType (process_info); + if (match_info.Matches (process_info)) + process_infos.Append (process_info); + } + } + + return process_infos.GetSize(); +} + bool Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) { process_info.SetProcessID(pid); - if (GetFreeBSDProcessArgs(NULL, process_info)) { + + if (GetFreeBSDProcessArgs(NULL, process_info)) + { // should use libprocstat instead of going right into sysctl? GetFreeBSDProcessCPUType(process_info); GetFreeBSDProcessUserAndGroup(process_info); return true; } + process_info.Clear(); return false; } diff --git a/lldb/source/Host/macosx/Host.mm b/lldb/source/Host/macosx/Host.mm index 47a9b0aac6b..2a1891bf3fe 100644 --- a/lldb/source/Host/macosx/Host.mm +++ b/lldb/source/Host/macosx/Host.mm @@ -1984,3 +1984,10 @@ Host::GetAuxvData(lldb_private::Process *process) { return lldb::DataBufferSP(); } + +uint32_t +Host::MakeDirectory (const char* path, mode_t mode) +{ + return ::mkdir(path,mode); +} + diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp index 291dc401357..fa5a9a0b2e8 100644 --- a/lldb/source/Interpreter/CommandObject.cpp +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -1123,6 +1123,9 @@ CommandObject::g_arguments_data[] = { eArgTypeOffset, "offset", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, { eArgTypeOldPathPrefix, "old-path-prefix", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, { eArgTypeOneLiner, "one-line-command", CommandCompletions::eNoCompletion, { NULL, false }, "A command that is entered as a single line of text." }, + { eArgTypePath, "path", CommandCompletions::eDiskFileCompletion, { NULL, false }, "Path." }, + { eArgTypePermissionsNumber, "perms-numeric", CommandCompletions::eNoCompletion, { NULL, false }, "Permissions given as an octal number (e.g. 755)." }, + { eArgTypePermissionsString, "perms=string", CommandCompletions::eNoCompletion, { NULL, false }, "Permissions given as a string value (e.g. rw-r-xr--)." }, { eArgTypePid, "pid", CommandCompletions::eNoCompletion, { NULL, false }, "The process ID number." }, { eArgTypePlugin, "plugin", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, { eArgTypeProcessName, "process-name", CommandCompletions::eNoCompletion, { NULL, false }, "The name of the process." }, diff --git a/lldb/source/Interpreter/Options.cpp b/lldb/source/Interpreter/Options.cpp index 293d7535663..af700ea9a95 100644 --- a/lldb/source/Interpreter/Options.cpp +++ b/lldb/source/Interpreter/Options.cpp @@ -992,6 +992,18 @@ OptionGroupOptions::Append (OptionGroup* group) } } +const OptionGroup* +OptionGroupOptions::GetGroupWithOption (char short_opt) +{ + for (uint32_t i = 0; i < m_option_defs.size(); i++) + { + OptionDefinition opt_def = m_option_defs[i]; + if (opt_def.short_option == short_opt) + return m_option_infos[i].option_group; + } + return NULL; +} + void OptionGroupOptions::Append (OptionGroup* group, uint32_t src_mask, diff --git a/lldb/source/Interpreter/ScriptInterpreterPython.cpp b/lldb/source/Interpreter/ScriptInterpreterPython.cpp index 9d9b8d93fb4..73619371ba9 100644 --- a/lldb/source/Interpreter/ScriptInterpreterPython.cpp +++ b/lldb/source/Interpreter/ScriptInterpreterPython.cpp @@ -52,6 +52,7 @@ static ScriptInterpreter::SWIGPythonCalculateNumChildren g_swig_calc_children = static ScriptInterpreter::SWIGPythonGetChildAtIndex g_swig_get_child_index = NULL; static ScriptInterpreter::SWIGPythonGetIndexOfChildWithName g_swig_get_index_child = NULL; static ScriptInterpreter::SWIGPythonCastPyObjectToSBValue g_swig_cast_to_sbvalue = NULL; +static ScriptInterpreter::SWIGPythonGetValueObjectSPFromSBValue g_swig_get_valobj_sp_from_sbvalue = NULL; static ScriptInterpreter::SWIGPythonUpdateSynthProviderInstance g_swig_update_provider = NULL; static ScriptInterpreter::SWIGPythonMightHaveChildrenSynthProviderInstance g_swig_mighthavechildren_provider = NULL; static ScriptInterpreter::SWIGPythonCallCommand g_swig_call_command = NULL; @@ -104,6 +105,9 @@ LLDBSwigPython_GetIndexOfChildWithName (void *implementor, const char* child_nam extern "C" void * LLDBSWIGPython_CastPyObjectToSBValue (void* data); +extern lldb::ValueObjectSP +LLDBSWIGPython_GetValueObjectSPFromSBValue (void* data); + extern "C" bool LLDBSwigPython_UpdateSynthProviderInstance (void* implementor); @@ -2451,20 +2455,18 @@ ScriptInterpreterPython::GetChildAtIndex (const lldb::ScriptInterpreterObjectSP& if (!g_swig_get_child_index || !g_swig_cast_to_sbvalue) return lldb::ValueObjectSP(); - void* child_ptr = NULL; - lldb::SBValue* value_sb = NULL; lldb::ValueObjectSP ret_val; { Locker py_lock(this); - child_ptr = g_swig_get_child_index (implementor,idx); + void* child_ptr = g_swig_get_child_index (implementor,idx); if (child_ptr != NULL && child_ptr != Py_None) { - value_sb = (lldb::SBValue*)g_swig_cast_to_sbvalue(child_ptr); - if (value_sb == NULL) + lldb::SBValue* sb_value_ptr = (lldb::SBValue*)g_swig_cast_to_sbvalue(child_ptr); + if (sb_value_ptr == NULL) Py_XDECREF(child_ptr); else - ret_val = value_sb->GetSP(); + ret_val = g_swig_get_valobj_sp_from_sbvalue (sb_value_ptr); } else { @@ -3058,6 +3060,7 @@ ScriptInterpreterPython::InitializeInterpreter (SWIGInitCallback python_swig_ini g_swig_get_child_index = LLDBSwigPython_GetChildAtIndex; g_swig_get_index_child = LLDBSwigPython_GetIndexOfChildWithName; g_swig_cast_to_sbvalue = LLDBSWIGPython_CastPyObjectToSBValue; + g_swig_get_valobj_sp_from_sbvalue = LLDBSWIGPython_GetValueObjectSPFromSBValue; g_swig_update_provider = LLDBSwigPython_UpdateSynthProviderInstance; g_swig_mighthavechildren_provider = LLDBSwigPython_MightHaveChildrenSynthProviderInstance; g_swig_call_command = LLDBSwigPythonCallCommand; diff --git a/lldb/source/Plugins/Platform/CMakeLists.txt b/lldb/source/Plugins/Platform/CMakeLists.txt index 1770a64f728..3576d86bceb 100644 --- a/lldb/source/Plugins/Platform/CMakeLists.txt +++ b/lldb/source/Plugins/Platform/CMakeLists.txt @@ -8,4 +8,5 @@ # add_subdirectory(Windows)
#endif()
+add_subdirectory(POSIX)
add_subdirectory(gdb-server)
diff --git a/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp b/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp index cdca4463fff..0da54d44ecb 100644 --- a/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp +++ b/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp @@ -156,6 +156,26 @@ PlatformFreeBSD::~PlatformFreeBSD() { } +//TODO:VK: inherit PlatformPOSIX +lldb_private::Error +PlatformFreeBSD::RunShellCommand (const char *command, + const char *working_dir, + int *status_ptr, + int *signo_ptr, + std::string *command_output, + uint32_t timeout_sec) +{ + if (IsHost()) + return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec); + else + { + if (m_remote_platform_sp) + return m_remote_platform_sp->RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec); + else + return Error("unable to run a remote command without a platform"); + } +} + Error PlatformFreeBSD::ResolveExecutable (const FileSpec &exe_file, @@ -165,10 +185,10 @@ PlatformFreeBSD::ResolveExecutable (const FileSpec &exe_file, { Error error; // Nothing special to do here, just use the actual file and architecture - + char exe_path[PATH_MAX]; FileSpec resolved_exe_file (exe_file); - + if (IsHost()) { // If we have "ls" as the exe_file, resolve the executable location based on @@ -178,10 +198,10 @@ PlatformFreeBSD::ResolveExecutable (const FileSpec &exe_file, exe_file.GetPath(exe_path, sizeof(exe_path)); resolved_exe_file.SetFile(exe_path, true); } - + if (!resolved_exe_file.Exists()) resolved_exe_file.ResolveExecutableLocation (); - + if (resolved_exe_file.Exists()) error.Clear(); else @@ -216,8 +236,8 @@ PlatformFreeBSD::ResolveExecutable (const FileSpec &exe_file, } } } - - + + if (error.Success()) { ModuleSpec module_spec (resolved_exe_file, exe_arch); @@ -228,7 +248,7 @@ PlatformFreeBSD::ResolveExecutable (const FileSpec &exe_file, module_search_paths_ptr, NULL, NULL); - + if (!exe_module_sp || exe_module_sp->GetObjectFile() == NULL) { exe_module_sp.reset(); @@ -259,12 +279,12 @@ PlatformFreeBSD::ResolveExecutable (const FileSpec &exe_file, else error.SetErrorToGenericError(); } - + if (idx > 0) arch_names.PutCString (", "); arch_names.PutCString (platform_arch.GetArchitectureName()); } - + if (error.Fail() || !exe_module_sp) { error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", @@ -279,7 +299,7 @@ PlatformFreeBSD::ResolveExecutable (const FileSpec &exe_file, error.SetErrorStringWithFormat ("'%s' does not exist", exe_file.GetPath().c_str()); } - + return error; } @@ -642,11 +662,18 @@ PlatformFreeBSD::GetStatus (Stream &strm) #ifndef LLDB_DISABLE_POSIX struct utsname un; - if (uname(&un)) { - strm << "FreeBSD"; - return; - } + strm << " Host: "; + + ::memset(&un, 0, sizeof(utsname)); + if (uname(&un) == -1) + strm << "FreeBSD" << '\n'; + + strm << un.sysname << ' ' << un.release; + if (un.nodename[0] != '\0') + strm << " (" << un.nodename << ')'; + strm << '\n'; + // Dump a common information about the platform status. strm << "Host: " << un.sysname << ' ' << un.release << ' ' << un.version << '\n'; #endif diff --git a/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h b/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h index 4aa158eb6e8..11d1cd60977 100644 --- a/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h +++ b/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h @@ -72,6 +72,14 @@ public: // lldb_private::Platform functions //------------------------------------------------------------ virtual lldb_private::Error + RunShellCommand (const char *command, + const char *working_dir, + int *status_ptr, + int *signo_ptr, + std::string *command_output, + uint32_t timeout_sec); + + virtual lldb_private::Error ResolveExecutable (const lldb_private::FileSpec &exe_file, const lldb_private::ArchSpec &arch, lldb::ModuleSP &module_sp, diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp index 4d85a7312e3..f055597d141 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp @@ -18,6 +18,7 @@ #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/Timer.h" @@ -36,8 +37,7 @@ using namespace lldb_private; /// Default Constructor //------------------------------------------------------------------ PlatformDarwin::PlatformDarwin (bool is_host) : - Platform(is_host), // This is the local host platform - m_remote_platform_sp (), + PlatformPOSIX(is_host), // This is the local host platform m_developer_directory () { } @@ -209,11 +209,11 @@ PlatformDarwin::ResolveExecutable (const FileSpec &exe_file, StreamString arch_names; for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, module_spec.GetArchitecture()); ++idx) { - error = ModuleList::GetSharedModule (module_spec, - exe_module_sp, - module_search_paths_ptr, - NULL, - NULL); + error = GetSharedModule (module_spec, + exe_module_sp, + module_search_paths_ptr, + NULL, + NULL); // Did we find an executable using one of the if (error.Success()) { @@ -268,7 +268,144 @@ PlatformDarwin::ResolveSymbolFile (Target &target, } +static lldb_private::Error +MakeCacheFolderForFile (const FileSpec& module_cache_spec) +{ + FileSpec module_cache_folder = module_cache_spec.CopyByRemovingLastPathComponent(); + StreamString mkdir_folder_cmd; + mkdir_folder_cmd.Printf("mkdir -p %s/%s", module_cache_folder.GetDirectory().AsCString(), module_cache_folder.GetFilename().AsCString()); + return Host::RunShellCommand(mkdir_folder_cmd.GetData(), + NULL, + NULL, + NULL, + NULL, + 60); +} + +static lldb_private::Error +BringInRemoteFile (Platform* platform, + const lldb_private::ModuleSpec &module_spec, + const FileSpec& module_cache_spec) +{ + MakeCacheFolderForFile(module_cache_spec); + Error err = platform->GetFile(module_spec.GetFileSpec(), module_cache_spec); + return err; +} + +lldb_private::Error +PlatformDarwin::GetSharedModuleWithLocalCache (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[%s] Trying to find module %s/%s - platform path %s/%s symbol path %s/%s\n", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString(), + module_spec.GetPlatformFileSpec().GetDirectory().AsCString(), + module_spec.GetPlatformFileSpec().GetFilename().AsCString(), + module_spec.GetSymbolFileSpec().GetDirectory().AsCString(), + module_spec.GetSymbolFileSpec().GetFilename().AsCString()); + + std::string cache_path(GetLocalCacheDirectory()); + std::string module_path (module_spec.GetFileSpec().GetPath()); + cache_path.append(module_path); + FileSpec module_cache_spec(cache_path.c_str(),false); + + // if rsync is supported, always bring in the file - rsync will be very efficient + // when files are the same on the local and remote end of the connection + if (this->GetSupportsRSync()) + { + Error err = BringInRemoteFile (this, module_spec, module_cache_spec); + if (err.Fail()) + return err; + if (module_cache_spec.Exists()) + { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[%s] module %s/%s was rsynced and is now there\n", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString()); + ModuleSpec local_spec(module_cache_spec, module_spec.GetArchitecture()); + module_sp.reset(new Module(local_spec)); + module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); + return Error(); + } + } + if (module_spec.GetFileSpec().Exists() && !module_sp) + { + module_sp.reset(new Module(module_spec)); + return Error(); + } + + // try to find the module in the cache + if (module_cache_spec.Exists()) + { + // get the local and remote MD5 and compare + { + // when going over the *slow* GDB remote transfer mechanism we first check + // the hashes of the files - and only do the actual transfer if they differ + uint64_t high_local,high_remote,low_local,low_remote; + Host::CalculateMD5 (module_cache_spec, low_local, high_local); + m_remote_platform_sp->CalculateMD5(module_spec.GetFileSpec(), low_remote, high_remote); + if (low_local != low_remote || high_local != high_remote) + { + // bring in the remote file + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[%s] module %s/%s needs to be replaced from remote copy\n", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString()); + Error err = BringInRemoteFile (this, module_spec, module_cache_spec); + if (err.Fail()) + return err; + } + } + + ModuleSpec local_spec(module_cache_spec, module_spec.GetArchitecture()); + module_sp.reset(new Module(local_spec)); + module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[%s] module %s/%s was found in the cache\n", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString()); + return Error(); + } + + // bring in the remote module file + if (log) + log->Printf("[%s] module %s/%s needs to come in remotely\n", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString()); + Error err = BringInRemoteFile (this, module_spec, module_cache_spec); + if (err.Fail()) + return err; + if (module_cache_spec.Exists()) + { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[%s] module %s/%s is now cached and fine\n", + (IsHost() ? "host" : "remote"), + module_spec.GetFileSpec().GetDirectory().AsCString(), + module_spec.GetFileSpec().GetFilename().AsCString()); + ModuleSpec local_spec(module_cache_spec, module_spec.GetArchitecture()); + module_sp.reset(new Module(local_spec)); + module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); + return Error(); + } + else + return Error("unable to obtain valid module file"); +} Error PlatformDarwin::GetSharedModule (const ModuleSpec &module_spec, @@ -508,26 +645,39 @@ PlatformDarwin::ConnectRemote (Args& args) if (!m_remote_platform_sp) m_remote_platform_sp = Platform::Create ("remote-gdb-server", error); - if (m_remote_platform_sp) - { - if (error.Success()) - { - if (m_remote_platform_sp) - { - error = m_remote_platform_sp->ConnectRemote (args); - } - else - { - error.SetErrorString ("\"platform connect\" takes a single argument: <connect-url>"); - } - } - } + if (m_remote_platform_sp && error.Success()) + error = m_remote_platform_sp->ConnectRemote (args); else error.SetErrorString ("failed to create a 'remote-gdb-server' platform"); if (error.Fail()) m_remote_platform_sp.reset(); } + + if (error.Success() && m_remote_platform_sp) + { + if (m_options.get()) + { + OptionGroupOptions* options = m_options.get(); + OptionGroupPlatformRSync* m_rsync_options = (OptionGroupPlatformRSync*)options->GetGroupWithOption('r'); + OptionGroupPlatformSSH* m_ssh_options = (OptionGroupPlatformSSH*)options->GetGroupWithOption('s'); + OptionGroupPlatformCaching* m_cache_options = (OptionGroupPlatformCaching*)options->GetGroupWithOption('c'); + + if (m_rsync_options->m_rsync) + { + SetSupportsRSync(true); + SetRSyncOpts(m_rsync_options->m_rsync_opts.c_str()); + SetRSyncPrefix(m_rsync_options->m_rsync_prefix.c_str()); + SetIgnoresRemoteHostname(m_rsync_options->m_ignores_remote_hostname); + } + if (m_ssh_options->m_ssh) + { + SetSupportsSSH(true); + SetSSHOpts(m_ssh_options->m_ssh_opts.c_str()); + } + SetLocalCacheDirectory(m_cache_options->m_cache_dir.c_str()); + } + } return error; } diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h index a37ae29b8c2..326203cc010 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h @@ -14,9 +14,9 @@ // C++ Includes // Other libraries and framework includes // Project includes -#include "lldb/Target/Platform.h" +#include "Plugins/Platform/POSIX/PlatformPOSIX.h" -class PlatformDarwin : public lldb_private::Platform +class PlatformDarwin : public PlatformPOSIX { public: PlatformDarwin (bool is_host); @@ -118,12 +118,18 @@ public: x86GetSupportedArchitectureAtIndex (uint32_t idx, lldb_private::ArchSpec &arch); protected: - lldb::PlatformSP m_remote_platform_sp; // Allow multiple ways to connect to a remote darwin OS + virtual lldb_private::Error + GetSharedModuleWithLocalCache (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr); + std::string m_developer_directory; const char * GetDeveloperDirectory(); - + private: DISALLOW_COPY_AND_ASSIGN (PlatformDarwin); diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp index 3b0e159e454..9ca49731ee9 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp @@ -12,14 +12,19 @@ // C Includes #ifndef LLDB_DISABLE_POSIX +#include <sys/stat.h> #include <sys/sysctl.h> #endif // C++ Includes + +#include <sstream> + // Other libraries and framework includes // Project includes #include "lldb/Core/Error.h" #include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/PluginManager.h" @@ -161,9 +166,9 @@ PlatformMacOSX::~PlatformMacOSX() } Error -PlatformMacOSX::GetFile (const FileSpec &platform_file, - const UUID *uuid_ptr, - FileSpec &local_file) +PlatformMacOSX::GetSymbolFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) { if (IsRemote()) { @@ -176,6 +181,62 @@ PlatformMacOSX::GetFile (const FileSpec &platform_file, return Error(); } +lldb_private::Error +PlatformMacOSX::GetFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file) +{ + if (IsRemote() && m_remote_platform_sp) + { + std::string local_os_build; + Host::GetOSBuildString(local_os_build); + std::string remote_os_build; + m_remote_platform_sp->GetOSBuildString(remote_os_build); + if (local_os_build.compare(remote_os_build) == 0) + { + // same OS version: the local file is good enough + local_file = platform_file; + return Error(); + } + else + { + // try to find the file in the cache + std::string cache_path(GetLocalCacheDirectory()); + std::string module_path (platform_file.GetPath()); + cache_path.append(module_path); + FileSpec module_cache_spec(cache_path.c_str(),false); + if (module_cache_spec.Exists()) + { + local_file = module_cache_spec; + return Error(); + } + // bring in the remote module file + FileSpec module_cache_folder = module_cache_spec.CopyByRemovingLastPathComponent(); + StreamString mkdir_folder_cmd; + // try to make the local directory first + mkdir_folder_cmd.Printf("mkdir -p %s/%s", module_cache_folder.GetDirectory().AsCString(), module_cache_folder.GetFilename().AsCString()); + Host::RunShellCommand(mkdir_folder_cmd.GetData(), + NULL, + NULL, + NULL, + NULL, + 60); + Error err = GetFile(platform_file, module_cache_spec); + if (err.Fail()) + return err; + if (module_cache_spec.Exists()) + { + local_file = module_cache_spec; + return Error(); + } + else + return Error("unable to obtain valid module file"); + } + } + local_file = platform_file; + return Error(); +} + bool PlatformMacOSX::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) { @@ -186,3 +247,12 @@ PlatformMacOSX::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) #endif } +lldb_private::Error +PlatformMacOSX::GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + return GetSharedModuleWithLocalCache(module_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr); +} diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.h b/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.h index b93c5fa6921..57a5a25981a 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.h +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.h @@ -61,17 +61,36 @@ public: return 1; } + virtual lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr); + virtual const char * GetDescription () { return GetDescriptionStatic (IsHost()); } + lldb_private::Error + GetSymbolFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file); + + virtual lldb_private::Error + GetFile (const lldb_private::FileSpec& source, + const lldb_private::FileSpec& destination) + { + return PlatformDarwin::GetFile (source,destination); + } + virtual lldb_private::Error GetFile (const lldb_private::FileSpec &platform_file, const lldb_private::UUID *uuid_ptr, lldb_private::FileSpec &local_file); - + virtual bool GetSupportedArchitectureAtIndex (uint32_t idx, lldb_private::ArchSpec &arch); diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.cpp index 7969aa819cf..7b5ab9aec41 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.cpp @@ -749,6 +749,15 @@ PlatformRemoteiOS::GetSharedModule (const ModuleSpec &module_spec, // Not the module we are looking for... Nothing to see here... module_sp.reset(); } + else + { + // This may not be an SDK-related module. Try whether we can bring in the thing to our local cache. + error = GetSharedModuleWithLocalCache(module_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr); + if (error.Success()) + return error; + else + error.Clear(); // Clear the error and fall-through. + } const bool always_create = false; error = ModuleList::GetSharedModule (module_spec, @@ -764,24 +773,6 @@ PlatformRemoteiOS::GetSharedModule (const ModuleSpec &module_spec, return error; } - -uint32_t -PlatformRemoteiOS::FindProcesses (const ProcessInstanceInfoMatch &match_info, - ProcessInstanceInfoList &process_infos) -{ - // TODO: if connected, send a packet to get the remote process infos by name - process_infos.Clear(); - return 0; -} - -bool -PlatformRemoteiOS::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) -{ - // TODO: if connected, send a packet to get the remote process info - process_info.Clear(); - return false; -} - bool PlatformRemoteiOS::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) { diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.h b/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.h index ef59b124d8e..7eb2660dc6f 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.h +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteiOS.h @@ -93,14 +93,6 @@ public: lldb::ModuleSP *old_module_sp_ptr, bool *did_create_ptr); - virtual uint32_t - FindProcesses (const lldb_private::ProcessInstanceInfoMatch &match_info, - lldb_private::ProcessInstanceInfoList &process_infos); - - virtual bool - GetProcessInfo (lldb::pid_t pid, - lldb_private::ProcessInstanceInfo &proc_info); - virtual bool GetSupportedArchitectureAtIndex (uint32_t idx, lldb_private::ArchSpec &arch); diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.cpp index 806eb7d60ea..f12843965a1 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.cpp @@ -315,9 +315,9 @@ PlatformiOSSimulator::GetSDKDirectory() } Error -PlatformiOSSimulator::GetFile (const FileSpec &platform_file, - const UUID *uuid_ptr, - FileSpec &local_file) +PlatformiOSSimulator::GetSymbolFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) { Error error; char platform_file_path[PATH_MAX]; @@ -370,7 +370,7 @@ PlatformiOSSimulator::GetSharedModule (const ModuleSpec &module_spec, Error error; FileSpec local_file; const FileSpec &platform_file = module_spec.GetFileSpec(); - error = GetFile (platform_file, module_spec.GetUUIDPtr(), local_file); + error = GetSymbolFile (platform_file, module_spec.GetUUIDPtr(), local_file); if (error.Success()) { error = ResolveExecutable (local_file, module_spec.GetArchitecture(), module_sp, module_search_paths_ptr); diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.h b/lldb/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.h index ce818c431a7..e75cf894904 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.h +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformiOSSimulator.h @@ -80,9 +80,9 @@ public: GetStatus (lldb_private::Stream &strm); virtual lldb_private::Error - GetFile (const lldb_private::FileSpec &platform_file, - const lldb_private::UUID *uuid_ptr, - lldb_private::FileSpec &local_file); + GetSymbolFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file); virtual lldb_private::Error GetSharedModule (const lldb_private::ModuleSpec &module_spec, diff --git a/lldb/source/Plugins/Platform/Makefile b/lldb/source/Plugins/Platform/Makefile index ceecfaad771..f709362841e 100644 --- a/lldb/source/Plugins/Platform/Makefile +++ b/lldb/source/Plugins/Platform/Makefile @@ -1,26 +1,26 @@ ##===- source/Plugins/Platform/Makefile --------------------*- Makefile -*-===## -# +# # The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. -# +# ##===----------------------------------------------------------------------===## LLDB_LEVEL := ../../.. include $(LLDB_LEVEL)/../../Makefile.config -DIRS := gdb-server MacOSX Linux FreeBSD +DIRS := gdb-server MacOSX Linux FreeBSD POSIX # ifeq ($(HOST_OS),Darwin) # DIRS += MacOSX # endif -# +# # ifeq ($(HOST_OS),Linux) # DIRS += Linux # endif -# +# # ifeq ($(HOST_OS),FreeBSD) # DIRS += FreeBSD # endif diff --git a/lldb/source/Plugins/Platform/POSIX/CMakeLists.txt b/lldb/source/Plugins/Platform/POSIX/CMakeLists.txt new file mode 100644 index 00000000000..6802d2e8029 --- /dev/null +++ b/lldb/source/Plugins/Platform/POSIX/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_NO_RTTI 1)
+
+add_lldb_library(lldbPluginPlatformPOSIX
+ PlatformPOSIX.cpp
+ )
diff --git a/lldb/source/Plugins/Platform/POSIX/Makefile b/lldb/source/Plugins/Platform/POSIX/Makefile new file mode 100644 index 00000000000..eca927720ba --- /dev/null +++ b/lldb/source/Plugins/Platform/POSIX/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Platform/POSIX/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginPlatformPOSIX +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp b/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp new file mode 100644 index 00000000000..34316c48427 --- /dev/null +++ b/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp @@ -0,0 +1,541 @@ +//===-- PlatformPOSIX.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PlatformPOSIX.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/File.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" + +using namespace lldb; +using namespace lldb_private; + + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformPOSIX::PlatformPOSIX (bool is_host) : +Platform(is_host), // This is the local host platform +m_remote_platform_sp () +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformPOSIX::~PlatformPOSIX() +{ +} + +lldb_private::OptionGroupOptions* +PlatformPOSIX::GetConnectionOptions (lldb_private::CommandInterpreter& interpreter) +{ + if (m_options.get() == NULL) + { + m_options.reset(new OptionGroupOptions(interpreter)); + m_options->Append(new OptionGroupPlatformRSync()); + m_options->Append(new OptionGroupPlatformSSH()); + m_options->Append(new OptionGroupPlatformCaching()); + } + return m_options.get(); +} + +lldb_private::Error +PlatformPOSIX::RunShellCommand (const char *command, // Shouldn't be NULL + const char *working_dir, // Pass NULL to use the current working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit + std::string *command_output, // Pass NULL if you don't want the command output + uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish +{ + if (IsHost()) + return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec); + else + { + if (m_remote_platform_sp) + return m_remote_platform_sp->RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec); + else + return Error("unable to run a remote command without a platform"); + } +} + +uint32_t +PlatformPOSIX::MakeDirectory (const std::string &path, + mode_t mode) +{ + if (IsHost()) + { + return Host::MakeDirectory (path.c_str(), mode); + } + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->MakeDirectory(path, mode); + return Platform::MakeDirectory(path,mode); +} + +lldb::user_id_t +PlatformPOSIX::OpenFile (const FileSpec& file_spec, + uint32_t flags, + mode_t mode, + Error &error) +{ + if (IsHost()) + { + return Host::OpenFile(file_spec, flags, mode, error); + } + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->OpenFile(file_spec, flags, mode, error); + return Platform::OpenFile(file_spec, flags, mode, error); +} + +bool +PlatformPOSIX::CloseFile (lldb::user_id_t fd, Error &error) +{ + if (IsHost()) + { + return Host::CloseFile(fd, error); + } + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->CloseFile(fd, error); + return Platform::CloseFile(fd, error); +} + +uint64_t +PlatformPOSIX::ReadFile (lldb::user_id_t fd, + uint64_t offset, + void *dst, + uint64_t dst_len, + Error &error) +{ + if (IsHost()) + { + return Host::ReadFile(fd, offset, dst, dst_len, error); + } + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->ReadFile(fd, offset, dst, dst_len, error); + return Platform::ReadFile(fd, offset, dst, dst_len, error); +} + +uint64_t +PlatformPOSIX::WriteFile (lldb::user_id_t fd, + uint64_t offset, + const void* src, + uint64_t src_len, + Error &error) +{ + if (IsHost()) + { + return Host::WriteFile(fd, offset, src, src_len, error); + } + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->WriteFile(fd, offset, src, src_len, error); + + return Platform::WriteFile(fd, offset, src, src_len, error); +} + +static uint32_t +chown_file(Platform *platform, + const char* path, + uint32_t uid = UINT32_MAX, + uint32_t gid = UINT32_MAX) +{ + if (!platform || !path || *path == 0) + return UINT32_MAX; + + if (uid == UINT32_MAX && gid == UINT32_MAX) + return 0; // pretend I did chown correctly - actually I just didn't care + + StreamString command; + command.PutCString("chown "); + if (uid != UINT32_MAX) + command.Printf("%d",uid); + if (gid != UINT32_MAX) + command.Printf(":%d",gid); + command.Printf("%s",path); + int status; + platform->RunShellCommand(command.GetData(), + NULL, + &status, + NULL, + NULL, + 10); + return status; +} + +lldb_private::Error +PlatformPOSIX::PutFile (const lldb_private::FileSpec& source, + const lldb_private::FileSpec& destination, + uint32_t uid, + uint32_t gid) +{ + if (IsHost()) + { + if (FileSpec::Equal(source, destination, true)) + return Error(); + // cp src dst + // chown uid:gid dst + std::string src_path (source.GetPath()); + if (src_path.empty()) + return Error("unable to get file path for source"); + std::string dst_path (destination.GetPath()); + if (dst_path.empty()) + return Error("unable to get file path for destination"); + StreamString command; + command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str()); + int status; + RunShellCommand(command.GetData(), + NULL, + &status, + NULL, + NULL, + 10); + if (status != 0) + return Error("unable to perform copy"); + if (uid == UINT32_MAX && gid == UINT32_MAX) + return Error(); + if (chown_file(this,dst_path.c_str(),uid,gid) != 0) + return Error("unable to perform chown"); + return Error(); + } + else if (IsRemote() && m_remote_platform_sp) + { + if (GetSupportsRSync()) + { + std::string src_path (source.GetPath()); + if (src_path.empty()) + return Error("unable to get file path for source"); + std::string dst_path (destination.GetPath()); + if (dst_path.empty()) + return Error("unable to get file path for destination"); + StreamString command; + if (GetIgnoresRemoteHostname()) + { + if (!GetRSyncPrefix()) + command.Printf("rsync %s %s %s", + GetRSyncOpts(), + src_path.c_str(), + dst_path.c_str()); + else + command.Printf("rsync %s %s %s%s", + GetRSyncOpts(), + src_path.c_str(), + GetRSyncPrefix(), + dst_path.c_str()); + } + else + command.Printf("rsync %s %s %s:%s", + GetRSyncOpts(), + src_path.c_str(), + GetHostname(), + dst_path.c_str()); + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[PutFile] Running command: %s\n", command.GetData()); + int retcode; + Host::RunShellCommand(command.GetData(), + NULL, + &retcode, + NULL, + NULL, + 60); + if (retcode == 0) + { + // Don't chown a local file for a remote system +// if (chown_file(this,dst_path.c_str(),uid,gid) != 0) +// return Error("unable to perform chown"); + return Error(); + } + // if we are still here rsync has failed - let's try the slow way before giving up + } + // open + // read, write, read, write, ... + // close + // chown uid:gid dst + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[PutFile] Using block by block transfer....\n"); + File source_file(source, File::eOpenOptionRead, File::ePermissionsUserRW); + if (!source_file.IsValid()) + return Error("unable to open source file"); + Error error; + lldb::user_id_t dest_file = OpenFile (destination, + File::eOpenOptionCanCreate | File::eOpenOptionWrite | File::eOpenOptionTruncate, + File::ePermissionsUserRWX | File::ePermissionsGroupRX | File::ePermissionsWorldRX, + error); + if (log) + log->Printf ("dest_file = %" PRIu64 "\n", dest_file); + if (error.Fail()) + return error; + if (dest_file == UINT64_MAX) + return Error("unable to open target file"); + lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0)); + uint64_t offset = 0; + while (error.Success()) + { + size_t bytes_read = buffer_sp->GetByteSize(); + error = source_file.Read(buffer_sp->GetBytes(), bytes_read); + if (bytes_read) + { + WriteFile(dest_file, offset, buffer_sp->GetBytes(), bytes_read, error); + offset += bytes_read; + } + else + break; + } + CloseFile(dest_file, error); + if (uid == UINT32_MAX && gid == UINT32_MAX) + return error; + // This is remopve, don't chown a local file... +// std::string dst_path (destination.GetPath()); +// if (chown_file(this,dst_path.c_str(),uid,gid) != 0) +// return Error("unable to perform chown"); + return error; + } + return Platform::PutFile(source,destination,uid,gid); +} + +lldb::user_id_t +PlatformPOSIX::GetFileSize (const FileSpec& file_spec) +{ + if (IsHost()) + { + return Host::GetFileSize(file_spec); + } + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->GetFileSize(file_spec); + return Platform::GetFileSize(file_spec); +} + +bool +PlatformPOSIX::GetFileExists (const FileSpec& file_spec) +{ + if (IsHost()) + { + return file_spec.Exists(); + } + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->GetFileExists(file_spec); + return Platform::GetFileExists(file_spec); +} + +uint32_t +PlatformPOSIX::GetFilePermissions (const lldb_private::FileSpec &file_spec, + lldb_private::Error &error) +{ + if (IsHost()) + { + return File::GetPermissions(file_spec.GetPath().c_str(), error); + } + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->GetFilePermissions(file_spec, error); + return Platform::GetFilePermissions(file_spec, error); + +} + + +lldb_private::Error +PlatformPOSIX::GetFile (const lldb_private::FileSpec& source /* remote file path */, + const lldb_private::FileSpec& destination /* local file path */) +{ + // Check the args, first. + std::string src_path (source.GetPath()); + if (src_path.empty()) + return Error("unable to get file path for source"); + std::string dst_path (destination.GetPath()); + if (dst_path.empty()) + return Error("unable to get file path for destination"); + if (IsHost()) + { + if (FileSpec::Equal(source, destination, true)) + return Error("local scenario->source and destination are the same file path: no operation performed"); + // cp src dst + StreamString cp_command; + cp_command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str()); + int status; + RunShellCommand(cp_command.GetData(), + NULL, + &status, + NULL, + NULL, + 10); + if (status != 0) + return Error("unable to perform copy"); + return Error(); + } + else if (IsRemote() && m_remote_platform_sp) + { + if (GetSupportsRSync()) + { + StreamString command; + if (GetIgnoresRemoteHostname()) + { + if (!GetRSyncPrefix()) + command.Printf("rsync %s %s %s", + GetRSyncOpts(), + src_path.c_str(), + dst_path.c_str()); + else + command.Printf("rsync %s %s%s %s", + GetRSyncOpts(), + GetRSyncPrefix(), + src_path.c_str(), + dst_path.c_str()); + } + else + command.Printf("rsync %s %s:%s %s", + GetRSyncOpts(), + m_remote_platform_sp->GetHostname(), + src_path.c_str(), + dst_path.c_str()); + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[GetFile] Running command: %s\n", command.GetData()); + int retcode; + Host::RunShellCommand(command.GetData(), + NULL, + &retcode, + NULL, + NULL, + 60); + if (retcode == 0) + return Error(); + // If we are here, rsync has failed - let's try the slow way before giving up + } + // open src and dst + // read/write, read/write, read/write, ... + // close src + // close dst + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("[GetFile] Using block by block transfer....\n"); + Error error; + user_id_t fd_src = OpenFile (source, + File::eOpenOptionRead, + File::ePermissionsDefault, + error); + + if (fd_src == UINT64_MAX) + return Error("unable to open source file"); + + uint32_t permissions = GetFilePermissions(source, error); + + if (permissions == 0) + permissions = File::ePermissionsDefault; + + user_id_t fd_dst = Host::OpenFile(destination, + File::eOpenOptionCanCreate | File::eOpenOptionWrite | File::eOpenOptionTruncate, + permissions, + error); + + if (fd_dst == UINT64_MAX) + { + if (error.Success()) + error.SetErrorString("unable to open destination file"); + } + + if (error.Success()) + { + lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0)); + uint64_t offset = 0; + error.Clear(); + while (error.Success()) + { + const uint64_t n_read = ReadFile (fd_src, + offset, + buffer_sp->GetBytes(), + buffer_sp->GetByteSize(), + error); + if (error.Fail()) + break; + if (n_read == 0) + break; + if (Host::WriteFile(fd_dst, + offset, + buffer_sp->GetBytes(), + n_read, + error) != n_read) + { + if (!error.Fail()) + error.SetErrorString("unable to write to destination file"); + break; + } + offset += n_read; + } + } + // Ignore the close error of src. + if (fd_src != UINT64_MAX) + CloseFile(fd_src, error); + // And close the dst file descriptot. + if (fd_dst != UINT64_MAX && !Host::CloseFile(fd_dst, error)) + { + if (!error.Fail()) + error.SetErrorString("unable to close destination file"); + + } + return error; + } + return Platform::GetFile(source,destination); +} + +std::string +PlatformPOSIX::GetPlatformSpecificConnectionInformation() +{ + StreamString stream; + if (GetSupportsRSync()) + { + stream.PutCString("rsync"); + if ( (GetRSyncOpts() && *GetRSyncOpts()) || + (GetRSyncPrefix() && *GetRSyncPrefix()) || + GetIgnoresRemoteHostname()) + { + stream.Printf(", options: "); + if (GetRSyncOpts() && *GetRSyncOpts()) + stream.Printf("'%s' ",GetRSyncOpts()); + stream.Printf(", prefix: "); + if (GetRSyncPrefix() && *GetRSyncPrefix()) + stream.Printf("'%s' ",GetRSyncPrefix()); + if (GetIgnoresRemoteHostname()) + stream.Printf("ignore remote-hostname "); + } + } + if (GetSupportsSSH()) + { + stream.PutCString("ssh"); + if (GetSSHOpts() && *GetSSHOpts()) + stream.Printf(", options: '%s' ",GetSSHOpts()); + } + if (GetLocalCacheDirectory() && *GetLocalCacheDirectory()) + stream.Printf("cache dir: %s",GetLocalCacheDirectory()); + if (stream.GetSize()) + return stream.GetData(); + else + return ""; +} + +bool +PlatformPOSIX::CalculateMD5 (const FileSpec& file_spec, + uint64_t &low, + uint64_t &high) +{ + if (IsHost()) + return Platform::CalculateMD5 (file_spec, low, high); + if (m_remote_platform_sp) + return m_remote_platform_sp->CalculateMD5(file_spec, low, high); + return false; +} diff --git a/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.h b/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.h new file mode 100644 index 00000000000..4119e452bf7 --- /dev/null +++ b/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.h @@ -0,0 +1,111 @@ +//===-- PlatformPOSIX.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformPOSIX_h_ +#define liblldb_PlatformPOSIX_h_ + +// C Includes +// C++ Includes + +#include <memory> + +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Target/Platform.h" + +class PlatformPOSIX : public lldb_private::Platform +{ +public: + PlatformPOSIX (bool is_host); + + virtual + ~PlatformPOSIX(); + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + virtual lldb_private::OptionGroupOptions* + GetConnectionOptions (lldb_private::CommandInterpreter& interpreter); + + virtual lldb_private::Error + PutFile (const lldb_private::FileSpec& source, + const lldb_private::FileSpec& destination, + uint32_t uid = UINT32_MAX, + uint32_t gid = UINT32_MAX); + + virtual lldb::user_id_t + OpenFile (const lldb_private::FileSpec& file_spec, + uint32_t flags, + mode_t mode, + lldb_private::Error &error); + + virtual bool + CloseFile (lldb::user_id_t fd, + lldb_private::Error &error); + + virtual uint64_t + ReadFile (lldb::user_id_t fd, + uint64_t offset, + void *dst, + uint64_t dst_len, + lldb_private::Error &error); + + virtual uint64_t + WriteFile (lldb::user_id_t fd, + uint64_t offset, + const void* src, + uint64_t src_len, + lldb_private::Error &error); + + virtual lldb::user_id_t + GetFileSize (const lldb_private::FileSpec& file_spec); + + virtual lldb_private::Error + GetFile (const lldb_private::FileSpec& source, + const lldb_private::FileSpec& destination); + + virtual lldb_private::Error + RunShellCommand (const char *command, // Shouldn't be NULL + const char *working_dir, // Pass NULL to use the current working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit + std::string *command_output, // Pass NULL if you don't want the command output + uint32_t timeout_sec); // Timeout in seconds to wait for shell program to finish + + virtual uint32_t + MakeDirectory (const std::string &path, + mode_t mode); + + virtual bool + GetFileExists (const lldb_private::FileSpec& file_spec); + + virtual uint32_t + GetFilePermissions (const lldb_private::FileSpec &file_spec, + lldb_private::Error &error); + + virtual std::string + GetPlatformSpecificConnectionInformation(); + + virtual bool + CalculateMD5 (const lldb_private::FileSpec& file_spec, + uint64_t &low, + uint64_t &high); + +protected: + std::auto_ptr<lldb_private::OptionGroupOptions> m_options; + + lldb::PlatformSP m_remote_platform_sp; // Allow multiple ways to connect to a remote POSIX-compliant OS + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformPOSIX); + +}; + +#endif // liblldb_PlatformPOSIX_h_ diff --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp index 5d15db53de7..4aeec032d71 100644 --- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp +++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -110,7 +110,11 @@ PlatformRemoteGDBServer::ResolveExecutable (const FileSpec &exe_file, const FileSpecList *module_search_paths_ptr) { Error error; - error.SetErrorString ("PlatformRemoteGDBServer::ResolveExecutable() is unimplemented"); + //error.SetErrorString ("PlatformRemoteGDBServer::ResolveExecutable() is unimplemented"); + if (m_gdb_client.GetFileExists(exe_file)) + return error; + // TODO: get the remote end to somehow resolve this file + error.SetErrorString("file not found on remote end"); return error; } @@ -233,6 +237,11 @@ PlatformRemoteGDBServer::ConnectRemote (Args& args) error.SetErrorString ("\"platform connect\" takes a single argument: <connect-url>"); } } + + if (error.Success()) + { + + } return error; } @@ -327,6 +336,12 @@ PlatformRemoteGDBServer::LaunchProcess (ProcessLaunchInfo &launch_info) break; } } + + ArchSpec arch_spec = launch_info.GetArchitecture(); + const char *arch_triple = arch_spec.GetTriple().str().c_str(); + + m_gdb_client.SendLaunchArchPacket(arch_triple); + const uint32_t old_packet_timeout = m_gdb_client.SetPacketTimeout (5); int arg_packet_err = m_gdb_client.SendArgumentsPacket (argv); m_gdb_client.SetPacketTimeout (old_packet_timeout); @@ -363,7 +378,8 @@ PlatformRemoteGDBServer::Attach (lldb_private::ProcessAttachInfo &attach_info, { if (IsConnected()) { - uint16_t port = m_gdb_client.LaunchGDBserverAndGetPort(); + lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; + uint16_t port = m_gdb_client.LaunchGDBserverAndGetPort(debugserver_pid); if (port == 0) { @@ -397,15 +413,22 @@ PlatformRemoteGDBServer::Attach (lldb_private::ProcessAttachInfo &attach_info, if (process_sp) { char connect_url[256]; + const char *override_hostname = getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_HOSTNAME"); + const char *port_offset_c_str = getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_PORT_OFFSET"); + int port_offset = port_offset_c_str ? ::atoi(port_offset_c_str) : 0; const int connect_url_len = ::snprintf (connect_url, sizeof(connect_url), "connect://%s:%u", - GetHostname (), - port); + override_hostname ? override_hostname : GetHostname (), + port + port_offset); assert (connect_url_len < (int)sizeof(connect_url)); error = process_sp->ConnectRemote (NULL, connect_url); if (error.Success()) error = process_sp->Attach(attach_info); + else if (debugserver_pid != LLDB_INVALID_PROCESS_ID) + { + m_gdb_client.KillSpawnedProcess(debugserver_pid); + } } } } @@ -418,4 +441,83 @@ PlatformRemoteGDBServer::Attach (lldb_private::ProcessAttachInfo &attach_info, return process_sp; } +uint32_t +PlatformRemoteGDBServer::MakeDirectory (const std::string &path, + mode_t mode) +{ + return m_gdb_client.MakeDirectory(path,mode); +} + +lldb::user_id_t +PlatformRemoteGDBServer::OpenFile (const lldb_private::FileSpec& file_spec, + uint32_t flags, + mode_t mode, + Error &error) +{ + return m_gdb_client.OpenFile (file_spec, flags, mode, error); +} + +bool +PlatformRemoteGDBServer::CloseFile (lldb::user_id_t fd, Error &error) +{ + return m_gdb_client.CloseFile (fd, error); +} + +lldb::user_id_t +PlatformRemoteGDBServer::GetFileSize (const lldb_private::FileSpec& file_spec) +{ + return m_gdb_client.GetFileSize(file_spec); +} + +uint32_t +PlatformRemoteGDBServer::GetFilePermissions (const lldb_private::FileSpec &file_spec, + lldb_private::Error &error) +{ + return m_gdb_client.GetFilePermissions(file_spec, error); +} + +uint64_t +PlatformRemoteGDBServer::ReadFile (lldb::user_id_t fd, + uint64_t offset, + void *dst, + uint64_t dst_len, + Error &error) +{ + return m_gdb_client.ReadFile (fd, offset, dst, dst_len, error); +} + +uint64_t +PlatformRemoteGDBServer::WriteFile (lldb::user_id_t fd, + uint64_t offset, + const void* src, + uint64_t src_len, + Error &error) +{ + return m_gdb_client.WriteFile (fd, offset, src, src_len, error); +} +lldb_private::Error +PlatformRemoteGDBServer::PutFile (const lldb_private::FileSpec& source, + const lldb_private::FileSpec& destination, + uint32_t uid, + uint32_t gid) +{ + return Platform::PutFile(source,destination,uid,gid); +} + +bool +PlatformRemoteGDBServer::GetFileExists (const lldb_private::FileSpec& file_spec) +{ + return m_gdb_client.GetFileExists (file_spec); +} + +lldb_private::Error +PlatformRemoteGDBServer::RunShellCommand (const char *command, // Shouldn't be NULL + const char *working_dir, // Pass NULL to use the current working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit + std::string *command_output, // Pass NULL if you don't want the command output + uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish +{ + return m_gdb_client.RunShellCommand (command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec); +} diff --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h index 22b3dd49be5..70db6bafacd 100644 --- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h +++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h @@ -134,6 +134,58 @@ public: virtual lldb_private::Error DisconnectRemote (); + + virtual uint32_t + MakeDirectory (const std::string &path, + mode_t mode); + + virtual lldb::user_id_t + OpenFile (const lldb_private::FileSpec& file_spec, + uint32_t flags, + mode_t mode, + lldb_private::Error &error); + + virtual bool + CloseFile (lldb::user_id_t fd, + lldb_private::Error &error); + + virtual uint64_t + ReadFile (lldb::user_id_t fd, + uint64_t offset, + void *data_ptr, + uint64_t len, + lldb_private::Error &error); + + virtual uint64_t + WriteFile (lldb::user_id_t fd, + uint64_t offset, + const void* data, + uint64_t len, + lldb_private::Error &error); + + virtual lldb::user_id_t + GetFileSize (const lldb_private::FileSpec& file_spec); + + virtual lldb_private::Error + PutFile (const lldb_private::FileSpec& source, + const lldb_private::FileSpec& destination, + uint32_t uid = UINT32_MAX, + uint32_t gid = UINT32_MAX); + + virtual bool + GetFileExists (const lldb_private::FileSpec& file_spec); + + virtual uint32_t + GetFilePermissions (const lldb_private::FileSpec &file_spec, + lldb_private::Error &error); + + virtual lldb_private::Error + RunShellCommand (const char *command, // Shouldn't be NULL + const char *working_dir, // Pass NULL to use the current working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit + std::string *command_output, // Pass NULL if you don't want the command output + uint32_t timeout_sec); // Timeout in seconds to wait for shell program to finish protected: GDBRemoteCommunicationClient m_gdb_client; diff --git a/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp b/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp index ea26d972b86..23fcfa62563 100644 --- a/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp +++ b/lldb/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp @@ -125,8 +125,27 @@ ProcessFreeBSD::Terminate() bool ProcessFreeBSD::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) { - // XXX haxx - new_thread_list = old_thread_list; - - return false; + LogSP log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) + log->Printf ("ProcessFreeBSD::%s() (pid = %i)", __FUNCTION__, GetID()); + + bool has_updated = false; + const tid_t tid = Host::GetCurrentThreadID(); + const lldb::pid_t pid = GetID(); + // Update the process thread list with this new thread. + // FIXME: We should be using tid, not pid. + assert(m_monitor); + ThreadSP thread_sp (old_thread_list.FindThreadByID (pid, false)); + if (!thread_sp) { + ProcessSP me = this->shared_from_this(); + thread_sp.reset(new POSIXThread(me, pid)); + has_updated = true; + } + + if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) + log->Printf ("ProcessFreeBSD::%s() updated tid = %i", __FUNCTION__, pid); + + new_thread_list.AddThread(thread_sp); + + return has_updated; // the list has been updated } diff --git a/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp index f04631ddf91..5a78b874e1e 100644 --- a/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp +++ b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp @@ -813,19 +813,21 @@ ProcessPOSIX::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thre if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) log->Printf ("ProcessPOSIX::%s() (pid = %" PRIi64 ")", __FUNCTION__, GetID()); + bool has_updated = false; // Update the process thread list with this new thread. // FIXME: We should be using tid, not pid. assert(m_monitor); ThreadSP thread_sp (old_thread_list.FindThreadByID (GetID(), false)); if (!thread_sp) { thread_sp.reset(CreateNewPOSIXThread(*this, GetID())); + has_updated = true; } if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) log->Printf ("ProcessPOSIX::%s() updated pid = %" PRIi64, __FUNCTION__, GetID()); new_thread_list.AddThread(thread_sp); - return new_thread_list.GetSize(false) > 0; + return has_updated; // the list has been updated } ByteOrder diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp index d7efdf2302d..46bd9ca8069 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -133,7 +133,11 @@ GDBRemoteCommunication::GDBRemoteCommunication(const char *comm_name, const char *listener_name, bool is_platform) : Communication(comm_name), +#ifdef LLDB_CONFIGURATION_DEBUG + m_packet_timeout (1000), +#else m_packet_timeout (1), +#endif m_sequence_mutex (Mutex::eMutexTypeRecursive), m_public_is_running (false), m_private_is_running (false), diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 6a6e063e35b..e3183fca1db 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -20,6 +20,7 @@ #include "lldb/Core/ConnectionFileDescriptor.h" #include "lldb/Core/Log.h" #include "lldb/Core/State.h" +#include "lldb/Core/StreamGDBRemote.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/Endian.h" #include "lldb/Host/Host.h" @@ -2137,21 +2138,37 @@ GDBRemoteCommunicationClient::SendSpeedTestPacket (uint32_t send_size, uint32_t } uint16_t -GDBRemoteCommunicationClient::LaunchGDBserverAndGetPort () +GDBRemoteCommunicationClient::LaunchGDBserverAndGetPort (lldb::pid_t &pid) { + pid = LLDB_INVALID_PROCESS_ID; StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse("qLaunchGDBServer", strlen("qLaunchGDBServer"), response, false)) + StreamString stream; + stream.PutCString("qLaunchGDBServer:port:0;"); + std::string hostname; + if (Host::GetHostname (hostname)) + { + // Make the GDB server we launch only accept connections from this host + stream.Printf("host:%s;", hostname.c_str()); + } + else + { + // Make the GDB server we launch accept connections from any host since we can't figure out the hostname + stream.Printf("host:*;"); + } + const char *packet = stream.GetData(); + int packet_len = stream.GetSize(); + + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) { std::string name; std::string value; uint16_t port = 0; - //lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; while (response.GetNameColonValue(name, value)) { - if (name.size() == 4 && name.compare("port") == 0) + if (name.compare("port") == 0) port = Args::StringToUInt32(value.c_str(), 0, 0); -// if (name.size() == 3 && name.compare("pid") == 0) -// pid = Args::StringToUInt32(value.c_str(), LLDB_INVALID_PROCESS_ID, 0); + else if (name.compare("pid") == 0) + pid = Args::StringToUInt64(value.c_str(), LLDB_INVALID_PROCESS_ID, 0); } return port; } @@ -2159,6 +2176,23 @@ GDBRemoteCommunicationClient::LaunchGDBserverAndGetPort () } bool +GDBRemoteCommunicationClient::KillSpawnedProcess (lldb::pid_t pid) +{ + StreamString stream; + stream.Printf ("qKillSpawnedProcess:%" PRId64 , pid); + const char *packet = stream.GetData(); + int packet_len = stream.GetSize(); + pid = LLDB_INVALID_PROCESS_ID; + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.IsOKResponse()) + return true; + } + return false; +} + +bool GDBRemoteCommunicationClient::SetCurrentThread (uint64_t tid) { if (m_curr_tid == tid) @@ -2351,3 +2385,326 @@ GDBRemoteCommunicationClient::GetShlibInfoAddr() return LLDB_INVALID_ADDRESS; } +lldb_private::Error +GDBRemoteCommunicationClient::RunShellCommand (const char *command, // Shouldn't be NULL + const char *working_dir, // Pass NULL to use the current working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit + std::string *command_output, // Pass NULL if you don't want the command output + uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish +{ + lldb_private::StreamString stream; + stream.PutCString("qPlatform_RunCommand:"); + stream.PutBytesAsRawHex8(command, strlen(command)); + stream.PutChar(','); + stream.PutHex32(timeout_sec); + if (working_dir && *working_dir) + { + stream.PutChar(','); + stream.PutBytesAsRawHex8(working_dir, strlen(working_dir)); + } + const char *packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.GetChar() != 'F') + return Error("malformed reply"); + if (response.GetChar() != ',') + return Error("malformed reply"); + uint32_t exitcode = response.GetHexMaxU32(false, UINT32_MAX); + if (exitcode == UINT32_MAX) + return Error("unable to run remote process"); + else if (status_ptr) + *status_ptr = exitcode; + if (response.GetChar() != ',') + return Error("malformed reply"); + uint32_t signo = response.GetHexMaxU32(false, UINT32_MAX); + if (signo_ptr) + *signo_ptr = signo; + if (response.GetChar() != ',') + return Error("malformed reply"); + std::string output; + response.GetEscapedBinaryData(output); + if (command_output) + command_output->assign(output); + return Error(); + } + return Error("unable to send packet"); +} + +uint32_t +GDBRemoteCommunicationClient::MakeDirectory (const std::string &path, + mode_t mode) +{ + lldb_private::StreamString stream; + stream.PutCString("qPlatform_IO_MkDir:"); + stream.PutHex32(mode); + stream.PutChar(','); + stream.PutBytesAsRawHex8(path.c_str(), path.size()); + const char *packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + return response.GetHexMaxU32(false, UINT32_MAX); + } + return UINT32_MAX; + +} + +static uint64_t +ParseHostIOPacketResponse (StringExtractorGDBRemote &response, + uint64_t fail_result, + Error &error) +{ + response.SetFilePos(0); + if (response.GetChar() != 'F') + return fail_result; + int32_t result = response.GetS32 (-2); + if (result == -2) + return fail_result; + if (response.GetChar() == ',') + { + int result_errno = response.GetS32 (-2); + if (result_errno != -2) + error.SetError(result_errno, eErrorTypePOSIX); + else + error.SetError(-1, eErrorTypeGeneric); + } + else + error.Clear(); + return result; +} +lldb::user_id_t +GDBRemoteCommunicationClient::OpenFile (const lldb_private::FileSpec& file_spec, + uint32_t flags, + mode_t mode, + Error &error) +{ + lldb_private::StreamString stream; + stream.PutCString("vFile:open:"); + std::string path (file_spec.GetPath()); + if (path.empty()) + return UINT64_MAX; + stream.PutCStringAsRawHex8(path.c_str()); + stream.PutChar(','); + const uint32_t posix_open_flags = File::ConvertOpenOptionsForPOSIXOpen(flags); + stream.PutHex32(posix_open_flags); + stream.PutChar(','); + stream.PutHex32(mode); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + return ParseHostIOPacketResponse (response, UINT64_MAX, error); + } + return UINT64_MAX; +} + +bool +GDBRemoteCommunicationClient::CloseFile (lldb::user_id_t fd, + Error &error) +{ + lldb_private::StreamString stream; + stream.Printf("vFile:close:%i", (int)fd); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + return ParseHostIOPacketResponse (response, -1, error) == 0; + } + return UINT64_MAX; +} + +// Extension of host I/O packets to get the file size. +lldb::user_id_t +GDBRemoteCommunicationClient::GetFileSize (const lldb_private::FileSpec& file_spec) +{ + lldb_private::StreamString stream; + stream.PutCString("vFile:size:"); + std::string path (file_spec.GetPath()); + stream.PutCStringAsRawHex8(path.c_str()); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.GetChar() != 'F') + return UINT64_MAX; + uint32_t retcode = response.GetHexMaxU64(false, UINT64_MAX); + return retcode; + } + return UINT64_MAX; +} + +uint32_t +GDBRemoteCommunicationClient::GetFilePermissions(const lldb_private::FileSpec& file_spec, Error &error) +{ + lldb_private::StreamString stream; + stream.PutCString("vFile:mode:"); + std::string path (file_spec.GetPath()); + stream.PutCStringAsRawHex8(path.c_str()); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.GetChar() != 'F') + { + error.SetErrorStringWithFormat ("invalid response to '%s' packet", packet); + return 0; + } + const uint32_t mode = response.GetS32(-1); + if (mode == -1) + { + if (response.GetChar() == ',') + { + int response_errno = response.GetS32(-1); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + else + error.SetErrorToGenericError(); + } + } + else + error.Clear(); + return mode & (S_IRWXU|S_IRWXG|S_IRWXO); + } + else + { + error.SetErrorStringWithFormat ("failed to send '%s' packet", packet); + } + return 0; +} + +uint64_t +GDBRemoteCommunicationClient::ReadFile (lldb::user_id_t fd, + uint64_t offset, + void *dst, + uint64_t dst_len, + Error &error) +{ + lldb_private::StreamString stream; + stream.Printf("vFile:pread:%i,%" PRId64 ",%" PRId64, (int)fd, dst_len, offset); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.GetChar() != 'F') + return 0; + uint32_t retcode = response.GetHexMaxU32(false, UINT32_MAX); + if (retcode == UINT32_MAX) + return retcode; + const char next = (response.Peek() ? *response.Peek() : 0); + if (next == ',') + return 0; + if (next == ';') + { + response.GetChar(); // skip the semicolon + std::string buffer; + if (response.GetEscapedBinaryData(buffer)) + { + const uint64_t data_to_write = std::min<uint64_t>(dst_len, buffer.size()); + if (data_to_write > 0) + memcpy(dst, &buffer[0], data_to_write); + return data_to_write; + } + } + } + return 0; +} + +uint64_t +GDBRemoteCommunicationClient::WriteFile (lldb::user_id_t fd, + uint64_t offset, + const void* src, + uint64_t src_len, + Error &error) +{ + lldb_private::StreamGDBRemote stream; + stream.Printf("vFile:pwrite:%i,%" PRId64 ",", (int)fd, offset); + stream.PutEscapedBytes(src, src_len); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.GetChar() != 'F') + { + error.SetErrorStringWithFormat("write file failed"); + return 0; + } + uint64_t bytes_written = response.GetU64(UINT64_MAX); + if (bytes_written == UINT64_MAX) + { + error.SetErrorToGenericError(); + if (response.GetChar() == ',') + { + int response_errno = response.GetS32(-1); + if (response_errno > 0) + error.SetError(response_errno, lldb::eErrorTypePOSIX); + } + return 0; + } + return bytes_written; + } + else + { + error.SetErrorString ("failed to send vFile:pwrite packet"); + } + return 0; +} + +// Extension of host I/O packets to get whether a file exists. +bool +GDBRemoteCommunicationClient::GetFileExists (const lldb_private::FileSpec& file_spec) +{ + lldb_private::StreamString stream; + stream.PutCString("vFile:exists:"); + std::string path (file_spec.GetPath()); + stream.PutCStringAsRawHex8(path.c_str()); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.GetChar() != 'F') + return false; + if (response.GetChar() != ',') + return false; + bool retcode = (response.GetChar() != '0'); + return retcode; + } + return false; +} + +bool +GDBRemoteCommunicationClient::CalculateMD5 (const lldb_private::FileSpec& file_spec, + uint64_t &high, + uint64_t &low) +{ + lldb_private::StreamString stream; + stream.PutCString("vFile:MD5:"); + std::string path (file_spec.GetPath()); + stream.PutCStringAsRawHex8(path.c_str()); + const char* packet = stream.GetData(); + int packet_len = stream.GetSize(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.GetChar() != 'F') + return false; + if (response.GetChar() != ',') + return false; + if (response.Peek() && *response.Peek() == 'x') + return false; + low = response.GetHexMaxU64(false, UINT64_MAX); + high = response.GetHexMaxU64(false, UINT64_MAX); + return true; + } + return false; +} diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 5bb8387b909..c8809cbf445 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -89,7 +89,10 @@ public: GetLaunchSuccess (std::string &error_str); uint16_t - LaunchGDBserverAndGetPort (); + LaunchGDBserverAndGetPort (lldb::pid_t &pid); + + bool + KillSpawnedProcess (lldb::pid_t pid); //------------------------------------------------------------------ /// Sends a GDB remote protocol 'A' packet that delivers program @@ -347,10 +350,61 @@ public: return m_interrupt_sent; } + virtual lldb::user_id_t + OpenFile (const lldb_private::FileSpec& file_spec, + uint32_t flags, + mode_t mode, + lldb_private::Error &error); + + virtual bool + CloseFile (lldb::user_id_t fd, + lldb_private::Error &error); + + virtual lldb::user_id_t + GetFileSize (const lldb_private::FileSpec& file_spec); + + virtual uint32_t + GetFilePermissions(const lldb_private::FileSpec& file_spec, + lldb_private::Error &error); + + virtual uint64_t + ReadFile (lldb::user_id_t fd, + uint64_t offset, + void *dst, + uint64_t dst_len, + lldb_private::Error &error); + + virtual uint64_t + WriteFile (lldb::user_id_t fd, + uint64_t offset, + const void* src, + uint64_t src_len, + lldb_private::Error &error); + + virtual uint32_t + MakeDirectory (const std::string &path, + mode_t mode); + + virtual bool + GetFileExists (const lldb_private::FileSpec& file_spec); + + virtual lldb_private::Error + RunShellCommand (const char *command, // Shouldn't be NULL + const char *working_dir, // Pass NULL to use the current working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit + std::string *command_output, // Pass NULL if you don't want the command output + uint32_t timeout_sec); // Timeout in seconds to wait for shell program to finish + + virtual bool + CalculateMD5 (const lldb_private::FileSpec& file_spec, + uint64_t &high, + uint64_t &low); + std::string HarmonizeThreadIdsForProfileData (ProcessGDBRemote *process, StringExtractorGDBRemote &inputStringExtractor); - + protected: bool diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp index 050ed00ff6d..f6fdf1513eb 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -9,6 +9,7 @@ #include "GDBRemoteCommunicationServer.h" +#include "lldb/Core/StreamGDBRemote.h" // C Includes // C++ Includes @@ -20,6 +21,7 @@ #include "lldb/Core/State.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/Endian.h" +#include "lldb/Host/File.h" #include "lldb/Host/Host.h" #include "lldb/Host/TimeValue.h" #include "lldb/Target/Process.h" @@ -40,11 +42,31 @@ GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(bool is_platform) : m_async_thread (LLDB_INVALID_HOST_THREAD), m_process_launch_info (), m_process_launch_error (), + m_spawned_pids (), + m_spawned_pids_mutex (Mutex::eMutexTypeRecursive), m_proc_infos (), m_proc_infos_index (0), m_lo_port_num (0), - m_hi_port_num (0) + m_hi_port_num (0), + m_next_port (0), + m_use_port_range (false) { + // We seldom need to override the port number that the debugserver process + // starts with. We just pass in 0 to let the system choose a random port. + // In rare situation where the need arises, use two environment variables + // to override. + uint16_t lo_port_num = 0; + uint16_t hi_port_num = 0; + const char *lo_port_c_str = getenv("LLDB_PLATFORM_START_DEBUG_SERVER_LO_PORT"); + if (lo_port_c_str) + lo_port_num = ::atoi(lo_port_c_str); + const char *hi_port_c_str = getenv("LLDB_PLATFORM_START_DEBUG_SERVER_HI_PORT"); + if (hi_port_c_str) + hi_port_num = ::atoi(hi_port_c_str); + if (lo_port_num && hi_port_num && lo_port_num < hi_port_num) + { + SetPortRange(lo_port_num, hi_port_num); + } } //---------------------------------------------------------------------- @@ -125,6 +147,9 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, case StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer: return Handle_qLaunchGDBServer (packet); + case StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess: + return Handle_qKillSpawnedProcess (packet); + case StringExtractorGDBRemote::eServerPacketType_qLaunchSuccess: return Handle_qLaunchSuccess (packet); @@ -142,6 +167,9 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, case StringExtractorGDBRemote::eServerPacketType_QEnvironment: return Handle_QEnvironment (packet); + + case StringExtractorGDBRemote::eServerPacketType_QLaunchArch: + return Handle_QLaunchArch (packet); case StringExtractorGDBRemote::eServerPacketType_QSetDisableASLR: return Handle_QSetDisableASLR (packet); @@ -160,6 +188,39 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, case StringExtractorGDBRemote::eServerPacketType_QStartNoAckMode: return Handle_QStartNoAckMode (packet); + + case StringExtractorGDBRemote::eServerPacketType_qPlatform_IO_MkDir: + return Handle_qPlatform_IO_MkDir (packet); + + case StringExtractorGDBRemote::eServerPacketType_qPlatform_RunCommand: + return Handle_qPlatform_RunCommand (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_Open: + return Handle_vFile_Open (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_Close: + return Handle_vFile_Close (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_pRead: + return Handle_vFile_pRead (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_pWrite: + return Handle_vFile_pWrite (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_Size: + return Handle_vFile_Size (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_Mode: + return Handle_vFile_Mode (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_Exists: + return Handle_vFile_Exists (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_Stat: + return Handle_vFile_Stat (packet); + + case StringExtractorGDBRemote::eServerPacketType_vFile_MD5: + return Handle_vFile_MD5 (packet); } return true; } @@ -666,6 +727,24 @@ GDBRemoteCommunicationServer::Handle_qC (StringExtractorGDBRemote &packet) } bool +GDBRemoteCommunicationServer::DebugserverProcessReaped (lldb::pid_t pid) +{ + Mutex::Locker locker (m_spawned_pids_mutex); + return m_spawned_pids.erase(pid) > 0; +} +bool +GDBRemoteCommunicationServer::ReapDebugserverProcess (void *callback_baton, + lldb::pid_t pid, + bool exited, + int signal, // Zero for no signal + int status) // Exit value of process if signal is zero +{ + GDBRemoteCommunicationServer *server = (GDBRemoteCommunicationServer *)callback_baton; + server->DebugserverProcessReaped (pid); + return true; +} + +bool GDBRemoteCommunicationServer::Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet) { #ifdef _WIN32 @@ -681,47 +760,97 @@ GDBRemoteCommunicationServer::Handle_qLaunchGDBServer (StringExtractorGDBRemote ConnectionFileDescriptor file_conn; char connect_url[PATH_MAX]; Error error; - char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX"; + std::string hostname; + char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX"; if (::mktemp (unix_socket_name) == NULL) { error.SetErrorString ("failed to make temporary path for a unix socket"); } else { + packet.SetFilePos(::strlen ("qLaunchGDBServer:")); + std::string name; + std::string value; + uint16_t port = UINT16_MAX; + while (packet.GetNameColonValue(name, value)) + { + if (name.compare ("host") == 0) + hostname.swap(value); + else if (name.compare ("port") == 0) + port = Args::StringToUInt32(value.c_str(), 0, 0); + } + if (port == UINT16_MAX) + port = GetAndUpdateNextPort(); + ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name); // Spawn a new thread to accept the port that gets bound after // binding to port 0 (zero). - lldb::thread_t accept_thread = Host::ThreadCreate (unix_socket_name, - AcceptPortFromInferior, - connect_url, - &error); + lldb::thread_t accept_thread = NULL; + + if (port == 0) + { + accept_thread = Host::ThreadCreate (unix_socket_name, + AcceptPortFromInferior, + connect_url, + &error); + } if (IS_VALID_LLDB_HOST_THREAD(accept_thread)) { - // Spawn a debugserver and try to get + // Spawn a debugserver and try to get the port it listens to. ProcessLaunchInfo debugserver_launch_info; - error = StartDebugserverProcess ("localhost:0", + StreamString host_and_port; + if (hostname.empty()) + hostname = "localhost"; + host_and_port.Printf("%s:%u", hostname.c_str(), port); + const char *host_and_port_cstr = host_and_port.GetString().c_str(); + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf("Launching debugserver with: %s...\n", host_and_port_cstr); + error = StartDebugserverProcess (host_and_port_cstr, unix_socket_name, debugserver_launch_info); lldb::pid_t debugserver_pid = debugserver_launch_info.GetProcessID(); + + if (debugserver_pid != LLDB_INVALID_PROCESS_ID) + { + { + Mutex::Locker locker (m_spawned_pids_mutex); + m_spawned_pids.insert(debugserver_pid); + } + Host::StartMonitoringChildProcess (ReapDebugserverProcess, this, debugserver_pid, false); + } + if (error.Success()) { bool success = false; - thread_result_t accept_thread_result = NULL; - if (Host::ThreadJoin (accept_thread, &accept_thread_result, &error)) + if (accept_thread) { - if (accept_thread_result) + thread_result_t accept_thread_result = NULL; + if (Host::ThreadJoin (accept_thread, &accept_thread_result, &error)) { - uint16_t port = (intptr_t)accept_thread_result; - char response[256]; - const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port); - assert (response_len < (int)sizeof(response)); - //m_port_to_pid_map[port] = debugserver_launch_info.GetProcessID(); - success = SendPacketNoLock (response, response_len) > 0; + if (accept_thread_result) + { + port = (intptr_t)accept_thread_result; + char response[256]; + const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port); + assert (response_len < sizeof(response)); + //m_port_to_pid_map[port] = debugserver_launch_info.GetProcessID(); + success = SendPacketNoLock (response, response_len) > 0; + } } } + else + { + char response[256]; + const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port); + assert (response_len < sizeof(response)); + //m_port_to_pid_map[port] = debugserver_launch_info.GetProcessID(); + success = SendPacketNoLock (response, response_len) > 0; + + } ::unlink (unix_socket_name); if (!success) @@ -734,11 +863,64 @@ GDBRemoteCommunicationServer::Handle_qLaunchGDBServer (StringExtractorGDBRemote } } } - return SendErrorResponse (13); + return SendErrorResponse (9); #endif } bool +GDBRemoteCommunicationServer::Handle_qKillSpawnedProcess (StringExtractorGDBRemote &packet) +{ + // Spawn a local debugserver as a platform so we can then attach or launch + // a process... + + if (m_is_platform) + { + packet.SetFilePos(::strlen ("qKillSpawnedProcess:")); + + lldb::pid_t pid = packet.GetU64(LLDB_INVALID_PROCESS_ID); + + // Scope for locker + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return SendErrorResponse (10); + } + kill (pid, SIGTERM); + + for (size_t i=0; i<10; ++i) + { + // Scope for locker + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return true; + } + usleep (10000); + } + + // Scope for locker + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return true; + } + kill (pid, SIGKILL); + + for (size_t i=0; i<10; ++i) + { + // Scope for locker + { + Mutex::Locker locker (m_spawned_pids_mutex); + if (m_spawned_pids.find(pid) == m_spawned_pids.end()) + return true; + } + usleep (10000); + } + } + return SendErrorResponse (10); +} + +bool GDBRemoteCommunicationServer::Handle_qLaunchSuccess (StringExtractorGDBRemote &packet) { if (m_process_launch_error.Success()) @@ -759,7 +941,22 @@ GDBRemoteCommunicationServer::Handle_QEnvironment (StringExtractorGDBRemote &pa m_process_launch_info.GetEnvironmentEntries ().AppendArgument (packet.Peek()); return SendOKResponse (); } - return SendErrorResponse (9); + return SendErrorResponse (11); +} + +bool +GDBRemoteCommunicationServer::Handle_QLaunchArch (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("QLaunchArch:")); + const uint32_t bytes_left = packet.GetBytesLeft(); + if (bytes_left > 0) + { + const char* arch_triple = packet.Peek(); + ArchSpec arch_spec(arch_triple,NULL); + m_process_launch_info.SetArchitecture(arch_spec); + return SendOKResponse(); + } + return SendErrorResponse(12); } bool @@ -797,7 +994,7 @@ GDBRemoteCommunicationServer::Handle_QSetSTDIN (StringExtractorGDBRemote &packet m_process_launch_info.AppendFileAction(file_action); return SendOKResponse (); } - return SendErrorResponse (10); + return SendErrorResponse (13); } bool @@ -814,7 +1011,7 @@ GDBRemoteCommunicationServer::Handle_QSetSTDOUT (StringExtractorGDBRemote &packe m_process_launch_info.AppendFileAction(file_action); return SendOKResponse (); } - return SendErrorResponse (11); + return SendErrorResponse (14); } bool @@ -831,7 +1028,7 @@ GDBRemoteCommunicationServer::Handle_QSetSTDERR (StringExtractorGDBRemote &packe m_process_launch_info.AppendFileAction(file_action); return SendOKResponse (); } - return SendErrorResponse (12); + return SendErrorResponse (15); } bool @@ -842,3 +1039,265 @@ GDBRemoteCommunicationServer::Handle_QStartNoAckMode (StringExtractorGDBRemote & m_send_acks = false; return true; } + +bool +GDBRemoteCommunicationServer::Handle_qPlatform_IO_MkDir (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("qPlatform_IO_MkDir:")); + mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX); + if (packet.GetChar() != ',') + return false; + std::string path; + packet.GetHexByteString(path); + uint32_t retcode = Host::MakeDirectory(path.c_str(),mode); + StreamString response; + response.PutHex32(retcode); + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Open (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:open:")); + std::string path; + packet.GetHexByteStringTerminatedBy(path,','); + if (path.size() == 0) + return false; + if (packet.GetChar() != ',') + return false; + uint32_t flags = packet.GetHexMaxU32(false, UINT32_MAX); + if (packet.GetChar() != ',') + return false; + mode_t mode = packet.GetHexMaxU32(false, UINT32_MAX); + Error error; + int fd = ::open (path.c_str(), flags, mode); + const int save_errno = fd == -1 ? errno : 0; + StreamString response; + response.PutChar('F'); + response.Printf("%i", fd); + if (save_errno) + response.Printf(",%i", save_errno); + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Close (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:close:")); + int fd = packet.GetS32(-1); + Error error; + int err = -1; + int save_errno = 0; + if (fd >= 0) + { + err = close(fd); + save_errno = err == -1 ? errno : 0; + } + else + { + save_errno = EINVAL; + } + StreamString response; + response.PutChar('F'); + response.Printf("%i", err); + if (save_errno) + response.Printf(",%i", save_errno); + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_pRead (StringExtractorGDBRemote &packet) +{ + StreamGDBRemote response; + packet.SetFilePos(::strlen("vFile:pread:")); + int fd = packet.GetS32(-1); + if (packet.GetChar() != ',') + return false; + uint64_t count = packet.GetU64(UINT64_MAX); + if (packet.GetChar() != ',') + return false; + uint64_t offset = packet.GetU64(UINT32_MAX); + if (count == UINT64_MAX) + { + response.Printf("F-1:%i", EINVAL); + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; + } + std::string buffer(count, 0); + const ssize_t bytes_read = ::pread (fd, &buffer[0], buffer.size(), offset); + const int save_errno = bytes_read == -1 ? errno : 0; + response.PutChar('F'); + response.Printf("%zi", bytes_read); + if (save_errno) + response.Printf(",%i", save_errno); + else + { + response.PutChar(';'); + response.PutEscapedBytes(&buffer[0], bytes_read); + } + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_pWrite (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:pwrite:")); + + StreamGDBRemote response; + response.PutChar('F'); + + int fd = packet.GetU32(UINT32_MAX); + if (packet.GetChar() != ',') + return false; + off_t offset = packet.GetU64(UINT32_MAX); + if (packet.GetChar() != ',') + return false; + std::string buffer; + if (packet.GetEscapedBinaryData(buffer)) + { + const ssize_t bytes_written = ::pwrite (fd, buffer.data(), buffer.size(), offset); + const int save_errno = bytes_written == -1 ? errno : 0; + response.Printf("%zi", bytes_written); + if (save_errno) + response.Printf(",%i", save_errno); + } + else + { + response.Printf ("-1,%i", EINVAL); + } + + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Size (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:size:")); + std::string path; + packet.GetHexByteString(path); + if (path.empty()) + return false; + lldb::user_id_t retcode = Host::GetFileSize(FileSpec(path.c_str(), false)); + StreamString response; + response.PutChar('F'); + response.PutHex64(retcode); + if (retcode == UINT64_MAX) + { + response.PutChar(','); + response.PutHex64(retcode); // TODO: replace with Host::GetSyswideErrorCode() + } + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Mode (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:mode:")); + std::string path; + packet.GetHexByteString(path); + if (path.empty()) + return false; + Error error; + const uint32_t mode = File::GetPermissions(path.c_str(), error); + StreamString response; + response.Printf("F%u", mode); + if (mode == 0 || error.Fail()) + response.Printf(",%i", (int)error.GetError()); + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Exists (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:exists:")); + std::string path; + packet.GetHexByteString(path); + if (path.empty()) + return false; + bool retcode = Host::GetFileExists(FileSpec(path.c_str(), false)); + StreamString response; + response.PutChar('F'); + response.PutChar(','); + if (retcode) + response.PutChar('1'); + else + response.PutChar('0'); + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} + +bool +GDBRemoteCommunicationServer::Handle_qPlatform_RunCommand (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("qPlatform_RunCommand:")); + std::string path; + std::string working_dir; + packet.GetHexByteStringTerminatedBy(path,','); + if (path.size() == 0) + return false; + if (packet.GetChar() != ',') + return false; + uint32_t timeout = packet.GetHexMaxU32(false, 32); + if (packet.GetChar() == ',') + packet.GetHexByteString(working_dir); + int status, signo; + std::string output; + Error err = Host::RunShellCommand(path.c_str(), + working_dir.empty() ? NULL : working_dir.c_str(), + &status, &signo, &output, timeout); + StreamGDBRemote response; + if (err.Fail()) + { + response.PutCString("F,"); + response.PutHex32(UINT32_MAX); + } + else + { + response.PutCString("F,"); + response.PutHex32(status); + response.PutChar(','); + response.PutHex32(signo); + response.PutChar(','); + response.PutEscapedBytes(output.c_str(), output.size()); + } + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_Stat (StringExtractorGDBRemote &packet) +{ + return false; +} + +bool +GDBRemoteCommunicationServer::Handle_vFile_MD5 (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen("vFile:exists:")); + std::string path; + packet.GetHexByteString(path); + if (path.size() == 0) + return false; + uint64_t a,b; + StreamGDBRemote response; + if (Host::CalculateMD5(FileSpec(path.c_str(),false),a,b) == false) + { + response.PutCString("F,"); + response.PutCString("x"); + } + else + { + response.PutCString("F,"); + response.PutHex64(a); + response.PutHex64(b); + } + SendPacketNoLock(response.GetData(), response.GetSize()); + return true; +} diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h index cce0e4e64c1..64f6f8de1a2 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h @@ -12,10 +12,12 @@ // C Includes // C++ Includes +#include <vector> +#include <set> // Other libraries and framework includes // Project includes +#include "lldb/Host/Mutex.h" #include "lldb/Target/Process.h" - #include "GDBRemoteCommunication.h" class ProcessGDBRemote; @@ -60,6 +62,21 @@ public: { m_lo_port_num = lo_port_num; m_hi_port_num = hi_port_num; + m_next_port = m_lo_port_num; + m_use_port_range = true; + } + + // If we are using a port range, get and update the next port to be used variable. + // Otherwise, just return 0. + uint16_t + GetAndUpdateNextPort () + { + if (!m_use_port_range) + return 0; + uint16_t val = m_next_port; + if (++m_next_port > m_hi_port_num) + m_next_port = m_lo_port_num; + return val; } protected: @@ -68,11 +85,16 @@ protected: lldb::thread_t m_async_thread; lldb_private::ProcessLaunchInfo m_process_launch_info; lldb_private::Error m_process_launch_error; + std::set<lldb::pid_t> m_spawned_pids; + lldb_private::Mutex m_spawned_pids_mutex; lldb_private::ProcessInstanceInfoList m_proc_infos; uint32_t m_proc_infos_index; uint16_t m_lo_port_num; uint16_t m_hi_port_num; //PortToPIDMap m_port_to_pid_map; + uint16_t m_next_port; + bool m_use_port_range; + size_t SendUnimplementedResponse (const char *packet); @@ -85,7 +107,7 @@ protected: bool Handle_A (StringExtractorGDBRemote &packet); - + bool Handle_qLaunchSuccess (StringExtractorGDBRemote &packet); @@ -94,8 +116,14 @@ protected: bool Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet); + + bool + Handle_qKillSpawnedProcess (StringExtractorGDBRemote &packet); bool + Handle_qPlatform_IO_MkDir (StringExtractorGDBRemote &packet); + + bool Handle_qProcessInfoPID (StringExtractorGDBRemote &packet); bool @@ -120,6 +148,9 @@ protected: Handle_QEnvironment (StringExtractorGDBRemote &packet); bool + Handle_QLaunchArch (StringExtractorGDBRemote &packet); + + bool Handle_QSetDisableASLR (StringExtractorGDBRemote &packet); bool @@ -137,7 +168,47 @@ protected: bool Handle_QSetSTDERR (StringExtractorGDBRemote &packet); + bool + Handle_vFile_Open (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_Close (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_pRead (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_pWrite (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_Size (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_Mode (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_Exists (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_Stat (StringExtractorGDBRemote &packet); + + bool + Handle_vFile_MD5 (StringExtractorGDBRemote &packet); + + bool + Handle_qPlatform_RunCommand (StringExtractorGDBRemote &packet); + private: + bool + DebugserverProcessReaped (lldb::pid_t pid); + + static bool + ReapDebugserverProcess (void *callback_baton, + lldb::pid_t pid, + bool exited, + int signal, + int status); + //------------------------------------------------------------------ // For GDBRemoteCommunicationServer only //------------------------------------------------------------------ diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp index e6d3bc7a55d..eaedbffe6f1 100644 --- a/lldb/source/Target/Platform.cpp +++ b/lldb/source/Target/Platform.cpp @@ -22,6 +22,7 @@ #include "lldb/Host/Host.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" +#include "lldb/Utility/Utils.h" using namespace lldb; using namespace lldb_private; @@ -247,7 +248,13 @@ Platform::Platform (bool is_host) : m_uid_map(), m_gid_map(), m_max_uid_name_len (0), - m_max_gid_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) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); if (log) @@ -311,6 +318,14 @@ Platform::GetStatus (Stream &strm) strm.Printf(" Hostname: %s\n", GetHostname()); strm.Printf(" Connected: %s\n", is_connected ? "yes" : "no"); } + + if (!IsConnected()) + return; + + std::string specific_info(GetPlatformSpecificConnectionInformation()); + + if (specific_info.empty() == false) + strm.Printf("Platform-specific connection: %s\n", specific_info.c_str()); } @@ -760,9 +775,161 @@ Platform::IsCompatibleArchitecture (const ArchSpec &arch, bool exact_arch_match, if (compatible_arch_ptr) compatible_arch_ptr->Clear(); return false; +} + +uint32_t +Platform::MakeDirectory (const FileSpec &spec, + mode_t mode) +{ + std::string path(spec.GetPath()); + return this->MakeDirectory(path,mode); +} + +Error +Platform::PutFile (const FileSpec& source, + const FileSpec& destination, + uint32_t uid, + uint32_t gid) +{ + Error error("unimplemented"); + return error; +} + +Error +Platform::GetFile (const FileSpec& source, + const FileSpec& destination) +{ + Error error("unimplemented"); + return error; +} + +bool +Platform::GetFileExists (const lldb_private::FileSpec& file_spec) +{ + return false; +} + +lldb_private::Error +Platform::RunShellCommand (const char *command, // Shouldn't be NULL + const char *working_dir, // Pass NULL to use the current working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit + std::string *command_output, // Pass NULL if you don't want the command output + uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish +{ + if (IsHost()) + return Host::RunShellCommand (command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec); + else + return Error("unimplemented"); +} + + +bool +Platform::CalculateMD5 (const FileSpec& file_spec, + uint64_t &low, + uint64_t &high) +{ + if (IsHost()) + return Host::CalculateMD5(file_spec, low, high); + else + return false; +} + +void +Platform::SetLocalCacheDirectory (const char* local) +{ + m_local_cache_directory.assign(local); +} + +const char* +Platform::GetLocalCacheDirectory () +{ + return m_local_cache_directory.c_str(); +} + +static OptionDefinition +g_rsync_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "rsync" , 'r', no_argument, NULL, 0, eArgTypeNone , "Enable rsync." }, + { LLDB_OPT_SET_ALL, false, "rsync-opts" , 'R', required_argument, NULL, 0, eArgTypeCommandName , "Platform-specific options required for rsync to work." }, + { LLDB_OPT_SET_ALL, false, "rsync-prefix" , 'P', required_argument, NULL, 0, eArgTypeCommandName , "Platform-specific rsync prefix put before the remote path." }, + { LLDB_OPT_SET_ALL, false, "ignore-remote-hostname" , 'i', no_argument, NULL, 0, eArgTypeNone , "Do not automatically fill in the remote hostname when composing the rsync command." }, +}; + +static OptionDefinition +g_ssh_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "ssh" , 's', no_argument, NULL, 0, eArgTypeNone , "Enable SSH." }, + { LLDB_OPT_SET_ALL, false, "ssh-opts" , 'S', required_argument, NULL, 0, eArgTypeCommandName , "Platform-specific options required for SSH to work." }, +}; + +static OptionDefinition +g_caching_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "local-cache-dir" , 'c', required_argument, NULL, 0, eArgTypePath , "Path in which to store local copies of files." }, +}; + +OptionGroupPlatformRSync::OptionGroupPlatformRSync () +{ +} + +OptionGroupPlatformRSync::~OptionGroupPlatformRSync () +{ +} + +const lldb_private::OptionDefinition* +OptionGroupPlatformRSync::GetDefinitions () +{ + return g_rsync_option_table; +} + +void +OptionGroupPlatformRSync::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_rsync = false; + m_rsync_opts.clear(); + m_rsync_prefix.clear(); + m_ignores_remote_hostname = false; +} + +lldb_private::Error +OptionGroupPlatformRSync::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error 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; } +uint32_t +OptionGroupPlatformRSync::GetNumDefinitions () +{ + return llvm::array_lengthof(g_rsync_option_table); +} lldb::BreakpointSP Platform::SetThreadCreationBreakpoint (lldb_private::Target &target) @@ -770,10 +937,108 @@ Platform::SetThreadCreationBreakpoint (lldb_private::Target &target) return lldb::BreakpointSP(); } +OptionGroupPlatformSSH::OptionGroupPlatformSSH () +{ +} + +OptionGroupPlatformSSH::~OptionGroupPlatformSSH () +{ +} + +const lldb_private::OptionDefinition* +OptionGroupPlatformSSH::GetDefinitions () +{ + return g_ssh_option_table; +} + +void +OptionGroupPlatformSSH::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_ssh = false; + m_ssh_opts.clear(); +} + +lldb_private::Error +OptionGroupPlatformSSH::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error 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; +} + +uint32_t +OptionGroupPlatformSSH::GetNumDefinitions () +{ + return llvm::array_lengthof(g_ssh_option_table); +} + +OptionGroupPlatformCaching::OptionGroupPlatformCaching () +{ +} + +OptionGroupPlatformCaching::~OptionGroupPlatformCaching () +{ +} + +const lldb_private::OptionDefinition* +OptionGroupPlatformCaching::GetDefinitions () +{ + return g_caching_option_table; +} + +void +OptionGroupPlatformCaching::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_cache_dir.clear(); +} + +lldb_private::Error +OptionGroupPlatformCaching::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error 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; +} + +uint32_t +OptionGroupPlatformCaching::GetNumDefinitions () +{ + return llvm::array_lengthof(g_caching_option_table); +} + size_t Platform::GetEnvironment (StringList &environment) { environment.Clear(); return false; } - diff --git a/lldb/source/Utility/StringExtractor.cpp b/lldb/source/Utility/StringExtractor.cpp index 2f4bcecda8f..d4ce470d56f 100644 --- a/lldb/source/Utility/StringExtractor.cpp +++ b/lldb/source/Utility/StringExtractor.cpp @@ -168,10 +168,68 @@ StringExtractor::GetU32 (uint32_t fail_value, int base) { char *end = NULL; const char *start = m_packet.c_str(); - const char *uint_cstr = start + m_index; - uint32_t result = ::strtoul (uint_cstr, &end, base); + const char *cstr = start + m_index; + uint32_t result = ::strtoul (cstr, &end, base); - if (end && end != uint_cstr) + if (end && end != cstr) + { + m_index = end - start; + return result; + } + } + return fail_value; +} + +int32_t +StringExtractor::GetS32 (int32_t fail_value, int base) +{ + if (m_index < m_packet.size()) + { + char *end = NULL; + const char *start = m_packet.c_str(); + const char *cstr = start + m_index; + int32_t result = ::strtol (cstr, &end, base); + + if (end && end != cstr) + { + m_index = end - start; + return result; + } + } + return fail_value; +} + + +uint64_t +StringExtractor::GetU64 (uint64_t fail_value, int base) +{ + if (m_index < m_packet.size()) + { + char *end = NULL; + const char *start = m_packet.c_str(); + const char *cstr = start + m_index; + uint64_t result = ::strtoull (cstr, &end, base); + + if (end && end != cstr) + { + m_index = end - start; + return result; + } + } + return fail_value; +} + +int64_t +StringExtractor::GetS64 (int64_t fail_value, int base) +{ + if (m_index < m_packet.size()) + { + char *end = NULL; + const char *start = m_packet.c_str(); + const char *cstr = start + m_index; + int64_t result = ::strtoll (cstr, &end, base); + + if (end && end != cstr) { m_index = end - start; return result; @@ -371,6 +429,20 @@ StringExtractor::GetHexByteString (std::string &str) return str.size(); } +size_t +StringExtractor::GetHexByteStringTerminatedBy (std::string &str, + char terminator) +{ + str.clear(); + char ch; + while ((ch = GetHexU8(0,false)) != '\0') + str.append(1, ch); + if (Peek() && *Peek() == terminator) + return str.size(); + str.clear(); + return str.size(); +} + bool StringExtractor::GetNameColonValue (std::string &name, std::string &value) { diff --git a/lldb/source/Utility/StringExtractor.h b/lldb/source/Utility/StringExtractor.h index 0ded3101fcc..2aab3b09f47 100644 --- a/lldb/source/Utility/StringExtractor.h +++ b/lldb/source/Utility/StringExtractor.h @@ -101,9 +101,18 @@ public: bool GetNameColonValue (std::string &name, std::string &value); + int32_t + GetS32 (int32_t fail_value, int base = 0); + uint32_t GetU32 (uint32_t fail_value, int base = 0); + int64_t + GetS64 (int64_t fail_value, int base = 0); + + uint64_t + GetU64 (uint64_t fail_value, int base = 0); + uint32_t GetHexMaxU32 (bool little_endian, uint32_t fail_value); @@ -119,6 +128,10 @@ public: size_t GetHexByteString (std::string &str); + size_t + GetHexByteStringTerminatedBy (std::string &str, + char terminator); + const char * Peek () { diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp index 7e06a0f59bc..6e32481ccc5 100644 --- a/lldb/source/Utility/StringExtractorGDBRemote.cpp +++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -94,6 +94,9 @@ StringExtractorGDBRemote::GetServerPacketType () const else if (PACKET_STARTS_WITH ("QSetSTDERR:")) return eServerPacketType_QSetSTDERR; else if (PACKET_STARTS_WITH ("QSetWorkingDir:")) return eServerPacketType_QSetWorkingDir; break; + case 'L': + if (PACKET_STARTS_WITH ("QLaunchArch:")) return eServerPacketType_QLaunchArch; + break; } break; @@ -120,14 +123,21 @@ StringExtractorGDBRemote::GetServerPacketType () const if (PACKET_MATCHES ("qHostInfo")) return eServerPacketType_qHostInfo; break; + case 'K': + if (PACKET_STARTS_WITH ("qKillSpawnedProcess")) return eServerPacketType_qKillSpawnedProcess; + break; + case 'L': - if (PACKET_MATCHES ("qLaunchGDBServer")) return eServerPacketType_qLaunchGDBServer; + if (PACKET_STARTS_WITH ("qLaunchGDBServer")) return eServerPacketType_qLaunchGDBServer; if (PACKET_MATCHES ("qLaunchSuccess")) return eServerPacketType_qLaunchSuccess; break; case 'P': - if (PACKET_STARTS_WITH ("qProcessInfoPID:")) return eServerPacketType_qProcessInfoPID; + if (PACKET_STARTS_WITH ("qProcessInfoPID:")) return eServerPacketType_qProcessInfoPID; + if (PACKET_STARTS_WITH ("qPlatform_RunCommand:")) return eServerPacketType_qPlatform_RunCommand; + if (PACKET_STARTS_WITH ("qPlatform_IO_MkDir:")) return eServerPacketType_qPlatform_IO_MkDir; break; + case 'S': if (PACKET_STARTS_WITH ("qSpeedTest:")) return eServerPacketType_qSpeedTest; @@ -138,6 +148,21 @@ StringExtractorGDBRemote::GetServerPacketType () const break; } break; + case 'v': + if (PACKET_STARTS_WITH("vFile:")) + { + if (PACKET_STARTS_WITH("vFile:open:")) return eServerPacketType_vFile_Open; + else if (PACKET_STARTS_WITH("vFile:close:")) return eServerPacketType_vFile_Close; + else if (PACKET_STARTS_WITH("vFile:pread")) return eServerPacketType_vFile_pRead; + else if (PACKET_STARTS_WITH("vFile:pwrite")) return eServerPacketType_vFile_pWrite; + else if (PACKET_STARTS_WITH("vFile:size")) return eServerPacketType_vFile_Size; + else if (PACKET_STARTS_WITH("vFile:exists")) return eServerPacketType_vFile_Exists; + else if (PACKET_STARTS_WITH("vFile:stat")) return eServerPacketType_vFile_Stat; + else if (PACKET_STARTS_WITH("vFile:mode")) return eServerPacketType_vFile_Mode; + else if (PACKET_STARTS_WITH("vFile:MD5")) return eServerPacketType_vFile_MD5; + + } + break; } return eServerPacketType_unimplemented; } @@ -180,3 +205,19 @@ StringExtractorGDBRemote::GetError () } return 0; } + +size_t +StringExtractorGDBRemote::GetEscapedBinaryData (std::string &str) +{ + str.clear(); + char ch; + while (GetBytesLeft()) + { + ch = GetChar(); + if (ch == 0x7d) + ch = (GetChar() ^ 0x20); + str.append(1,ch); + } + return str.size(); +} + diff --git a/lldb/source/Utility/StringExtractorGDBRemote.h b/lldb/source/Utility/StringExtractorGDBRemote.h index 5a24d894b10..fe500ecda5d 100644 --- a/lldb/source/Utility/StringExtractorGDBRemote.h +++ b/lldb/source/Utility/StringExtractorGDBRemote.h @@ -53,17 +53,30 @@ public: eServerPacketType_qGroupName, eServerPacketType_qHostInfo, eServerPacketType_qLaunchGDBServer, + eServerPacketType_qKillSpawnedProcess, eServerPacketType_qLaunchSuccess, eServerPacketType_qProcessInfoPID, eServerPacketType_qSpeedTest, eServerPacketType_qUserName, eServerPacketType_QEnvironment, + eServerPacketType_QLaunchArch, eServerPacketType_QSetDisableASLR, eServerPacketType_QSetSTDIN, eServerPacketType_QSetSTDOUT, eServerPacketType_QSetSTDERR, eServerPacketType_QSetWorkingDir, - eServerPacketType_QStartNoAckMode + eServerPacketType_QStartNoAckMode, + eServerPacketType_qPlatform_RunCommand, + eServerPacketType_qPlatform_IO_MkDir, + eServerPacketType_vFile_Open, + eServerPacketType_vFile_Close, + eServerPacketType_vFile_pRead, + eServerPacketType_vFile_pWrite, + eServerPacketType_vFile_Size, + eServerPacketType_vFile_Mode, + eServerPacketType_vFile_Exists, + eServerPacketType_vFile_MD5, + eServerPacketType_vFile_Stat }; ServerPacketType @@ -98,6 +111,10 @@ public: // digits. Otherwise the error encoded in XX is returned. uint8_t GetError(); + + size_t + GetEscapedBinaryData (std::string &str); + }; #endif // utility_StringExtractorGDBRemote_h_ diff --git a/lldb/source/lldb-log.cpp b/lldb/source/lldb-log.cpp index c53fa56ef01..78355a8eefa 100644 --- a/lldb/source/lldb-log.cpp +++ b/lldb/source/lldb-log.cpp @@ -126,6 +126,7 @@ lldb_private::DisableLog (const char **categories, Stream *feedback_strm) else if (0 == ::strncasecmp(arg, "expr", 4)) flag_bits &= ~LIBLLDB_LOG_EXPRESSIONS; else if (0 == ::strncasecmp(arg, "object", 6)) flag_bits &= ~LIBLLDB_LOG_OBJECT; else if (0 == ::strcasecmp(arg, "process")) flag_bits &= ~LIBLLDB_LOG_PROCESS; + else if (0 == ::strcasecmp(arg, "platform")) flag_bits &= ~LIBLLDB_LOG_PLATFORM; else if (0 == ::strcasecmp(arg, "script")) flag_bits &= ~LIBLLDB_LOG_SCRIPT; else if (0 == ::strcasecmp(arg, "state")) flag_bits &= ~LIBLLDB_LOG_STATE; else if (0 == ::strcasecmp(arg, "step")) flag_bits &= ~LIBLLDB_LOG_STEP; @@ -196,6 +197,7 @@ lldb_private::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, const ch else if (0 == ::strncasecmp(arg, "expr", 4)) flag_bits |= LIBLLDB_LOG_EXPRESSIONS; else if (0 == ::strncasecmp(arg, "object", 6)) flag_bits |= LIBLLDB_LOG_OBJECT; else if (0 == ::strcasecmp(arg, "process")) flag_bits |= LIBLLDB_LOG_PROCESS; + else if (0 == ::strcasecmp(arg, "platform")) flag_bits |= LIBLLDB_LOG_PLATFORM; else if (0 == ::strcasecmp(arg, "script")) flag_bits |= LIBLLDB_LOG_SCRIPT; else if (0 == ::strcasecmp(arg, "state")) flag_bits |= LIBLLDB_LOG_STATE; else if (0 == ::strcasecmp(arg, "step")) flag_bits |= LIBLLDB_LOG_STEP; @@ -244,6 +246,7 @@ lldb_private::ListLogCategories (Stream *strm) " object - log object construction/destruction for important objects\n" " module - log module activities such as when modules are created, detroyed, replaced, and more\n" " process - log process events and activities\n" + " platform - log platform events and activities\n" " script - log events about the script interpreter\n" " state - log private and public process state changes\n" " step - log step related activities\n" diff --git a/lldb/source/lldb.cpp b/lldb/source/lldb.cpp index 1ac03f4f190..aefa5516637 100644 --- a/lldb/source/lldb.cpp +++ b/lldb/source/lldb.cpp @@ -42,6 +42,7 @@ #include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h" #include "Plugins/Platform/FreeBSD/PlatformFreeBSD.h" #include "Plugins/Platform/Linux/PlatformLinux.h" +#include "Plugins/Platform/POSIX/PlatformPOSIX.h" #include "Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h" #ifndef LLDB_DISABLE_PYTHON #include "Plugins/OperatingSystem/Python/OperatingSystemPython.h" diff --git a/lldb/test/api/check_public_api_headers/TestPublicAPIHeaders.py b/lldb/test/api/check_public_api_headers/TestPublicAPIHeaders.py index 9df567c3f3a..63618e05c36 100644 --- a/lldb/test/api/check_public_api_headers/TestPublicAPIHeaders.py +++ b/lldb/test/api/check_public_api_headers/TestPublicAPIHeaders.py @@ -22,6 +22,9 @@ class SBDirCheckerCase(TestBase): def test_sb_api_directory(self): """Test the SB API directory and make sure there's no unwanted stuff.""" + # Only proceed if this is "darwin", "x86_64", and local platform. + if not (sys.platform.startswith("darwin") and self.getArchitecture() == "x86_64" and not lldb.test_remote): + self.skipTest("This test is only for LLDB.framework built 64-bit and !lldb.test_remote") if self.getArchitecture() == "i386": self.skipTest("LLDB is 64-bit and cannot be linked to 32-bit test program.") diff --git a/lldb/test/api/multithreaded/TestMultithreaded.py b/lldb/test/api/multithreaded/TestMultithreaded.py index bb5c26271e5..b8d74ef7655 100644 --- a/lldb/test/api/multithreaded/TestMultithreaded.py +++ b/lldb/test/api/multithreaded/TestMultithreaded.py @@ -61,12 +61,13 @@ class SBBreakpointCallbackCase(TestBase): exe = [os.path.join(os.getcwd(), test_name), self.inferior] + env = {self.dylibPath : self.getLLDBLibraryEnvVal()} if self.TraceOn(): print "Running test %s" % " ".join(exe) - - check_call(exe, env={self.dylibPath : self.getLLDBLibraryEnvVal()}) - - + check_call(exe, env=env) + else: + with open(os.devnull, 'w') as fnull: + check_call(exe, env=env, stdout=fnull, stderr=fnull) def build_program(self, sources, program): return self.buildDriver(sources, program) diff --git a/lldb/test/dotest.py b/lldb/test/dotest.py index 4bc4dab2dd6..1c412f4c2fe 100755 --- a/lldb/test/dotest.py +++ b/lldb/test/dotest.py @@ -154,6 +154,9 @@ config = {} # The pre_flight and post_flight functions come from reading a config file. pre_flight = None post_flight = None +# So do the lldbtest_remote_sandbox and lldbtest_remote_shell_template variables. +lldbtest_remote_sandbox = None +lldbtest_remote_shell_template = None # The 'archs' and 'compilers' can be specified via either command line or configFile, # with the command line overriding the configFile. The corresponding options can be @@ -745,11 +748,11 @@ def parseOptionsAndInitTestdirs(): # respectively. # # See also lldb-trunk/examples/test/usage-config. - global config, pre_flight, post_flight + global config, pre_flight, post_flight, lldbtest_remote_sandbox, lldbtest_remote_shell_template if configFile: # Pass config (a dictionary) as the locals namespace for side-effect. execfile(configFile, globals(), config) - print "config:", config + #print "config:", config if "pre_flight" in config: pre_flight = config["pre_flight"] if not callable(pre_flight): @@ -760,6 +763,10 @@ def parseOptionsAndInitTestdirs(): if not callable(post_flight): print "fatal error: post_flight is not callable, exiting." sys.exit(1) + if "lldbtest_remote_sandbox" in config: + lldbtest_remote_sandbox = config["lldbtest_remote_sandbox"] + if "lldbtest_remote_shell_template" in config: + lldbtest_remote_shell_template = config["lldbtest_remote_shell_template"] #print "sys.stderr:", sys.stderr #print "sys.stdout:", sys.stdout @@ -1199,6 +1206,17 @@ if not noHeaders: print "lldb.pre_flight:", getsource_if_available(lldb.pre_flight) print "lldb.post_flight:", getsource_if_available(lldb.post_flight) +# If either pre_flight or post_flight is defined, set lldb.test_remote to True. +if lldb.pre_flight or lldb.post_flight: + lldb.test_remote = True +else: + lldb.test_remote = False + +# So do the lldbtest_remote_sandbox and lldbtest_remote_shell_template variables. +lldb.lldbtest_remote_sandbox = lldbtest_remote_sandbox +lldb.lldbtest_remote_sandboxed_executable = None +lldb.lldbtest_remote_shell_template = lldbtest_remote_shell_template + # Put all these test decorators in the lldb namespace. lldb.dont_do_python_api_test = dont_do_python_api_test lldb.just_do_python_api_test = just_do_python_api_test diff --git a/lldb/test/functionalities/abbreviation/TestAbbreviations.py b/lldb/test/functionalities/abbreviation/TestAbbreviations.py index 4df8cbc006a..38b9005537a 100644 --- a/lldb/test/functionalities/abbreviation/TestAbbreviations.py +++ b/lldb/test/functionalities/abbreviation/TestAbbreviations.py @@ -90,7 +90,10 @@ class AbbreviationsTestCase(TestBase): def running_abbreviations (self): exe = os.path.join (os.getcwd(), "a.out") - self.expect("fil " + exe, + # Use "file", i.e., no abbreviation. We're exactly matching the command + # verbatim when dealing with remote testsuite execution. + # For more details, see TestBase.runCmd(). + self.expect("file " + exe, patterns = [ "Current executable set to .*a.out.*" ]) # By default, the setting interpreter.expand-regex-aliases is false. diff --git a/lldb/test/functionalities/conditional_break/.lldb b/lldb/test/functionalities/conditional_break/.lldb index 52fc2ddd647..d077db551cb 100644 --- a/lldb/test/functionalities/conditional_break/.lldb +++ b/lldb/test/functionalities/conditional_break/.lldb @@ -1,4 +1,4 @@ -file a.out +#file a.out breakpoint set -n c #script import sys, os #script sys.path.append(os.path.join(os.getcwd(), os.pardir)) diff --git a/lldb/test/functionalities/conditional_break/TestConditionalBreak.py b/lldb/test/functionalities/conditional_break/TestConditionalBreak.py index b9625cff09b..806cb436fd6 100644 --- a/lldb/test/functionalities/conditional_break/TestConditionalBreak.py +++ b/lldb/test/functionalities/conditional_break/TestConditionalBreak.py @@ -114,6 +114,10 @@ class ConditionalBreakTestCase(TestBase): if not self.TraceOn(): self.HideStdout() + + # Separate out the "file a.out" command from .lldb file, for the sake of + # remote testsuite. + self.runCmd("file a.out") self.runCmd("command source .lldb") self.runCmd ("break list") diff --git a/lldb/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py b/lldb/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py index dcab8c1a009..2d5f2965f02 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py +++ b/lldb/test/functionalities/data-formatter/data-formatter-objc/TestDataFormatterObjC.py @@ -447,7 +447,7 @@ class ObjCDataFormatterTestCase(TestBase): self.expect('frame variable bundle_string bundle_url main_bundle', substrs = ['(NSBundle *) bundle_string = ',' @"/System/Library/Frameworks/Accelerate.framework"', '(NSBundle *) bundle_url = ',' @"/System/Library/Frameworks/Cocoa.framework"', - '(NSBundle *) main_bundle = ','test/functionalities/data-formatter/data-formatter-objc']) + '(NSBundle *) main_bundle = ','data-formatter-objc']) def nsexception_data_formatter_commands(self): self.expect('frame variable except0 except1 except2 except3', diff --git a/lldb/test/functionalities/inferior-changed/TestInferiorChanged.py b/lldb/test/functionalities/inferior-changed/TestInferiorChanged.py index b51f4ca13de..58b9ffa7fd7 100644 --- a/lldb/test/functionalities/inferior-changed/TestInferiorChanged.py +++ b/lldb/test/functionalities/inferior-changed/TestInferiorChanged.py @@ -44,8 +44,8 @@ class ChangedInferiorTestCase(TestBase): def inferior_crashing(self): """Inferior crashes upon launching; lldb should catch the event and stop.""" - exe = os.path.join(os.getcwd(), "a.out") - self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + self.exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + self.exe, CURRENT_EXECUTABLE_SET) self.runCmd("run", RUN_SUCCEEDED) @@ -67,6 +67,9 @@ class ChangedInferiorTestCase(TestBase): def inferior_not_crashing(self): """Test lldb reloads the inferior after it was changed during the session.""" self.runCmd("process kill") + # Prod the lldb-platform that we have a newly built inferior ready. + if lldb.lldbtest_remote_sandbox: + self.runCmd("file " + self.exe, CURRENT_EXECUTABLE_SET) self.runCmd("run", RUN_SUCCEEDED) self.runCmd("process status") diff --git a/lldb/test/functionalities/load_unload/TestLoadUnload.py b/lldb/test/functionalities/load_unload/TestLoadUnload.py index 6fc1124a4f7..e6015a8cebe 100644 --- a/lldb/test/functionalities/load_unload/TestLoadUnload.py +++ b/lldb/test/functionalities/load_unload/TestLoadUnload.py @@ -27,6 +27,7 @@ class LoadUnloadTestCase(TestBase): @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support @skipIfLinux # llvm.org/pr14424 - missing linux Makefiles/testcase support + @not_remote_testsuite_ready def test_modules_search_paths(self): """Test target modules list after loading a different copy of the library libd.dylib, and verifies that it works with 'target modules search-paths add'.""" @@ -81,6 +82,7 @@ class LoadUnloadTestCase(TestBase): @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support @skipIfLinux # llvm.org/pr14424 - missing linux Makefiles/testcase support + @not_remote_testsuite_ready def test_dyld_library_path(self): """Test DYLD_LIBRARY_PATH after moving libd.dylib, which defines d_function, somewhere else.""" @@ -135,6 +137,7 @@ class LoadUnloadTestCase(TestBase): substrs = [special_dir, os.path.basename(new_dylib)]) @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support + @not_remote_testsuite_ready @skipIfLinux # llvm.org/pr14424 - missing linux Makefiles/testcase support def test_lldb_process_load_and_unload_commands(self): """Test that lldb process load/unload command work correctly.""" @@ -183,6 +186,7 @@ class LoadUnloadTestCase(TestBase): self.runCmd("process continue") @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support + @not_remote_testsuite_ready @skipIfLinux # llvm.org/pr14424 - missing linux Makefiles/testcase support def test_load_unload(self): """Test breakpoint by name works correctly with dlopen'ing.""" @@ -224,6 +228,7 @@ class LoadUnloadTestCase(TestBase): substrs = [' resolved, hit count = 2']) @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support + @not_remote_testsuite_ready @skipIfLinux # llvm.org/pr14424 - missing linux Makefiles/testcase support def test_step_over_load (self): """Test stepping over code that loads a shared library works correctly.""" diff --git a/lldb/test/functionalities/process_launch/TestProcessLaunch.py b/lldb/test/functionalities/process_launch/TestProcessLaunch.py index 82ab22f452e..e7f83d1818b 100644 --- a/lldb/test/functionalities/process_launch/TestProcessLaunch.py +++ b/lldb/test/functionalities/process_launch/TestProcessLaunch.py @@ -31,6 +31,7 @@ class ProcessLaunchTestCase(TestBase): self.buildDwarf () self.process_io_test () + @not_remote_testsuite_ready def process_io_test (self): """Test that process launch I/O redirection flags work properly.""" exe = os.path.join (os.getcwd(), "a.out") @@ -127,6 +128,7 @@ class ProcessLaunchTestCase(TestBase): # rdar://problem/9056462 # The process launch flag '-w' for setting the current working directory not working? + @not_remote_testsuite_ready def my_working_dir_test (self): """Test that '-w dir' sets the working dir when running the inferior.""" exe = os.path.join (os.getcwd(), "a.out") diff --git a/lldb/test/functionalities/watchpoint/hello_watchlocation/TestWatchLocation.py b/lldb/test/functionalities/watchpoint/hello_watchlocation/TestWatchLocation.py index bba5990cc1d..61541dc44e8 100644 --- a/lldb/test/functionalities/watchpoint/hello_watchlocation/TestWatchLocation.py +++ b/lldb/test/functionalities/watchpoint/hello_watchlocation/TestWatchLocation.py @@ -90,15 +90,13 @@ class HelloWatchLocationTestCase(TestBase): # only once. The stop reason of the thread should be watchpoint. self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT, substrs = ['stopped', - 'stop reason = watchpoint %d' % expected_wp_id, - self.violating_func]) + 'stop reason = watchpoint %d' % expected_wp_id]) # Switch to the thread stopped due to watchpoint and issue some commands. self.switch_to_thread_with_stop_reason(lldb.eStopReasonWatchpoint) self.runCmd("thread backtrace") - self.runCmd("expr unsigned val = *g_char_ptr; val") - self.expect(self.res.GetOutput().splitlines()[0], exe=False, - endstr = ' = 1') + self.expect("frame info", + substrs = [self.violating_func]) # Use the '-v' option to do verbose listing of the watchpoint. # The hit count should now be 1. diff --git a/lldb/test/functionalities/watchpoint/hello_watchpoint/TestMyFirstWatchpoint.py b/lldb/test/functionalities/watchpoint/hello_watchpoint/TestMyFirstWatchpoint.py index 4a65baad5e4..4b4bdad2720 100644 --- a/lldb/test/functionalities/watchpoint/hello_watchpoint/TestMyFirstWatchpoint.py +++ b/lldb/test/functionalities/watchpoint/hello_watchpoint/TestMyFirstWatchpoint.py @@ -80,10 +80,11 @@ class HelloWatchpointTestCase(TestBase): 'stop reason = watchpoint']) self.runCmd("process continue") + # Don't expect the read of 'global' to trigger a stop exception. - # The process status should be 'exited'. - self.expect("process status", - substrs = ['exited']) + process = self.dbg.GetSelectedTarget().GetProcess() + if process.GetState() == lldb.eStateStopped: + self.assertFalse(lldbutil.get_stopped_thread(process, lldb.eStopReasonWatchpoint)) # Use the '-v' option to do verbose listing of the watchpoint. # The hit count should now be 1. diff --git a/lldb/test/lang/c/blocks/TestBlocks.py b/lldb/test/lang/c/blocks/TestBlocks.py index 5cd5bd010d6..124f5f2ed5e 100644 --- a/lldb/test/lang/c/blocks/TestBlocks.py +++ b/lldb/test/lang/c/blocks/TestBlocks.py @@ -6,7 +6,7 @@ import lldb from lldbtest import * import lldbutil -class AnonymousTestCase(TestBase): +class BlocksTestCase(TestBase): mydir = os.path.join("lang", "c", "blocks") lines = [] diff --git a/lldb/test/lldbtest.py b/lldb/test/lldbtest.py index c6fb79860a3..a696157427f 100644 --- a/lldb/test/lldbtest.py +++ b/lldb/test/lldbtest.py @@ -373,6 +373,23 @@ def dwarf_test(func): wrapper.__dwarf_test__ = True return wrapper +def not_remote_testsuite_ready(func): + """Decorate the item as a test which is not ready yet for remote testsuite.""" + if isinstance(func, type) and issubclass(func, unittest2.TestCase): + raise Exception("@not_remote_testsuite_ready can only be used to decorate a test method") + @wraps(func) + def wrapper(self, *args, **kwargs): + try: + if lldb.lldbtest_remote_sandbox: + self.skipTest("not ready for remote testsuite") + except AttributeError: + pass + return func(self, *args, **kwargs) + + # Mark this function as such to separate them from the regular tests. + wrapper.__not_ready_for_remote_testsuite_test__ = True + return wrapper + def expectedFailureGcc(bugnumber=None, compiler_version=["=", None]): if callable(bugnumber): @wraps(bugnumber) @@ -1556,6 +1573,31 @@ class TestBase(Base): if not self.dbg: raise Exception('Invalid debugger instance') + # + # Warning: MAJOR HACK AHEAD! + # If we are running testsuite remotely (by checking lldb.lldbtest_remote_sandbox), + # redefine the self.dbg.CreateTarget(filename) method to execute a "file filename" + # command, instead. See also runCmd() where it decorates the "file filename" call + # with additional functionality when running testsuite remotely. + # + if lldb.lldbtest_remote_sandbox: + def DecoratedCreateTarget(arg): + self.runCmd("file %s" % arg) + target = self.dbg.GetSelectedTarget() + # + # SBTarget.LaunchSimple() currently not working for remote platform? + # johnny @ 04/23/2012 + # + def DecoratedLaunchSimple(argv, envp, wd): + self.runCmd("run") + return target.GetProcess() + target.LaunchSimple = DecoratedLaunchSimple + + return target + self.dbg.CreateTarget = DecoratedCreateTarget + if self.TraceOn(): + print "self.dbg.Create is redefined to:\n%s" % getsource_if_available(DecoratedCreateTarget) + # We want our debugger to be synchronous. self.dbg.SetAsync(False) @@ -1645,6 +1687,38 @@ class TestBase(Base): trace = (True if traceAlways else trace) + # This is an opportunity to insert the 'platform target-install' command if we are told so + # via the settig of lldb.lldbtest_remote_sandbox. + if cmd.startswith("target create "): + cmd = cmd.replace("target create ", "file ") + if cmd.startswith("file ") and lldb.lldbtest_remote_sandbox: + with recording(self, trace) as sbuf: + the_rest = cmd.split("file ")[1] + # Split the rest of the command line. + atoms = the_rest.split() + # + # NOTE: This assumes that the options, if any, follow the file command, + # instead of follow the specified target. + # + target = atoms[-1] + # Now let's get the absolute pathname of our target. + abs_target = os.path.abspath(target) + print >> sbuf, "Found a file command, target (with absolute pathname)=%s" % abs_target + fpath, fname = os.path.split(abs_target) + parent_dir = os.path.split(fpath)[0] + platform_target_install_command = 'platform target-install %s %s' % (fpath, lldb.lldbtest_remote_sandbox) + print >> sbuf, "Insert this command to be run first: %s" % platform_target_install_command + self.ci.HandleCommand(platform_target_install_command, self.res) + # And this is the file command we want to execute, instead. + # + # Warning: SIDE EFFECT AHEAD!!! + # Populate the remote executable pathname into the lldb namespace, + # so that test cases can grab this thing out of the namespace. + # + lldb.lldbtest_remote_sandboxed_executable = abs_target.replace(parent_dir, lldb.lldbtest_remote_sandbox) + cmd = "file -P %s %s %s" % (lldb.lldbtest_remote_sandboxed_executable, the_rest.replace(target, ''), abs_target) + print >> sbuf, "And this is the replaced file command: %s" % cmd + running = (cmd.startswith("run") or cmd.startswith("process launch")) for i in range(self.maxLaunchCount if running else 1): diff --git a/lldb/test/macosx/universal/TestUniversal.py b/lldb/test/macosx/universal/TestUniversal.py index 0a5cf9c1d02..59c21e11e60 100644 --- a/lldb/test/macosx/universal/TestUniversal.py +++ b/lldb/test/macosx/universal/TestUniversal.py @@ -35,7 +35,6 @@ class UniversalTestCase(TestBase): process = target.LaunchSimple(None, None, os.getcwd()) self.assertTrue(process, PROCESS_IS_VALID) - # rdar://problem/8972204 AddressByteSize of 32-bit process should be 4, got 8 instead. @unittest2.skipUnless(sys.platform.startswith("darwin") and os.uname()[4] in ['i386', 'x86_64'], "requires Darwin & i386") def test_process_launch_for_universal(self): @@ -74,7 +73,7 @@ class UniversalTestCase(TestBase): self.runCmd("continue") # Now specify i386 as the architecture for "testit". - self.expect("file " + exe + " -a i386", CURRENT_EXECUTABLE_SET, + self.expect("file -a i386 " + exe, CURRENT_EXECUTABLE_SET, startstr = "Current executable set to ", substrs = ["testit' (i386)."]) diff --git a/lldb/test/python_api/hello_world/TestHelloWorld.py b/lldb/test/python_api/hello_world/TestHelloWorld.py index d4fabbbb25d..d1e7d89114d 100644 --- a/lldb/test/python_api/hello_world/TestHelloWorld.py +++ b/lldb/test/python_api/hello_world/TestHelloWorld.py @@ -33,6 +33,7 @@ class HelloWorldTestCase(TestBase): self.setTearDownCleanup(dictionary=self.d) self.hello_world_python() + @not_remote_testsuite_ready @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") @python_api_test @dsym_test @@ -45,6 +46,7 @@ class HelloWorldTestCase(TestBase): self.setTearDownCleanup(dictionary=self.d) self.hello_world_attach_with_id_api() + @not_remote_testsuite_ready @python_api_test @dwarf_test def test_with_dwarf_and_attach_to_process_with_id_api(self): @@ -56,6 +58,7 @@ class HelloWorldTestCase(TestBase): self.setTearDownCleanup(dictionary=self.d) self.hello_world_attach_with_id_api() + @not_remote_testsuite_ready @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") @python_api_test @dsym_test @@ -69,6 +72,7 @@ class HelloWorldTestCase(TestBase): self.hello_world_attach_with_name_api() @expectedFailureFreeBSD('llvm.org/pr16699') # attach by name not on FreeBSD yet + @not_remote_testsuite_ready @python_api_test @dwarf_test def test_with_dwarf_and_attach_to_process_with_name_api(self): diff --git a/lldb/test/python_api/process/TestProcessAPI.py b/lldb/test/python_api/process/TestProcessAPI.py index 1d8c9e16847..016fc1271cd 100644 --- a/lldb/test/python_api/process/TestProcessAPI.py +++ b/lldb/test/python_api/process/TestProcessAPI.py @@ -78,7 +78,6 @@ class ProcessAPITestCase(TestBase): def read_memory(self): """Test Python SBProcess.ReadMemory() API.""" exe = os.path.join(os.getcwd(), "a.out") - self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) @@ -160,7 +159,6 @@ class ProcessAPITestCase(TestBase): def write_memory(self): """Test Python SBProcess.WriteMemory() API.""" exe = os.path.join(os.getcwd(), "a.out") - self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) @@ -211,7 +209,6 @@ class ProcessAPITestCase(TestBase): def access_my_int(self): """Test access 'my_int' using Python SBProcess.GetByteOrder() and other APIs.""" exe = os.path.join(os.getcwd(), "a.out") - self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) @@ -300,7 +297,6 @@ class ProcessAPITestCase(TestBase): def remote_launch_should_fail(self): """Test SBProcess.RemoteLaunch() API with a process not in eStateConnected, and it should fail.""" exe = os.path.join(os.getcwd(), "a.out") - self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) diff --git a/lldb/test/python_api/target/TestTargetAPI.py b/lldb/test/python_api/target/TestTargetAPI.py index 27dd3799709..8f2a6dee01b 100644 --- a/lldb/test/python_api/target/TestTargetAPI.py +++ b/lldb/test/python_api/target/TestTargetAPI.py @@ -195,7 +195,7 @@ class TargetAPITestCase(TestBase): self.expect(desc, exe=False, substrs = ['a.out', 'Target', 'Module', 'Breakpoint']) - + @not_remote_testsuite_ready def launch_new_process_and_redirect_stdout(self): """Exercise SBTaget.Launch() API with redirected stdout.""" exe = os.path.join(os.getcwd(), "a.out") diff --git a/lldb/test/settings/TestSettings.py b/lldb/test/settings/TestSettings.py index fe75afc6b3b..b3642c4eb1e 100644 --- a/lldb/test/settings/TestSettings.py +++ b/lldb/test/settings/TestSettings.py @@ -205,6 +205,7 @@ class SettingsCommandTestCase(TestBase): self.buildDwarf() self.pass_run_args_and_env_vars() + @not_remote_testsuite_ready def pass_run_args_and_env_vars(self): """Test that run-args and env-vars are passed to the launched process.""" exe = os.path.join(os.getcwd(), "a.out") @@ -231,6 +232,7 @@ class SettingsCommandTestCase(TestBase): "argv[3] matches", "Environment variable 'MY_ENV_VAR' successfully passed."]) + @not_remote_testsuite_ready def test_pass_host_env_vars(self): """Test that the host env vars are passed to the launched process.""" self.buildDefault() @@ -262,6 +264,7 @@ class SettingsCommandTestCase(TestBase): substrs = ["The host environment variable 'MY_HOST_ENV_VAR1' successfully passed.", "The host environment variable 'MY_HOST_ENV_VAR2' successfully passed."]) + @not_remote_testsuite_ready def test_set_error_output_path(self): """Test that setting target.error/output-path for the launched process works.""" self.buildDefault() diff --git a/lldb/tools/CMakeLists.txt b/lldb/tools/CMakeLists.txt index 960ac22ea9c..b1989f81551 100644 --- a/lldb/tools/CMakeLists.txt +++ b/lldb/tools/CMakeLists.txt @@ -3,5 +3,5 @@ if (CMAKE_SYSTEM_NAME MATCHES "Darwin") endif()
if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows")
add_subdirectory(driver)
+ add_subdirectory(lldb-platform)
endif()
-
diff --git a/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj b/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj index 90500c5e722..7354f62488e 100644 --- a/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj +++ b/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj @@ -606,7 +606,7 @@ CLANG_CXX_LIBRARY = "libc++"; "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "source/debugserver-entitlements.plist"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = lldb_codesign; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; COPY_PHASE_STRIP = YES; CURRENT_PROJECT_VERSION = 310.99.0; FRAMEWORK_SEARCH_PATHS = $SDKROOT/System/Library/PrivateFrameworks; diff --git a/lldb/tools/lldb-platform/CMakeLists.txt b/lldb/tools/lldb-platform/CMakeLists.txt new file mode 100644 index 00000000000..97ccde10199 --- /dev/null +++ b/lldb/tools/lldb-platform/CMakeLists.txt @@ -0,0 +1,14 @@ +set(LLVM_NO_RTTI 1)
+
+include_directories(../../source)
+
+add_lldb_executable(lldb-platform
+ lldb-platform.cpp
+ )
+
+target_link_libraries(lldb-platform liblldb)
+
+set_target_properties(lldb-platform PROPERTIES VERSION ${LLDB_VERSION})
+
+install(TARGETS lldb-platform
+ RUNTIME DESTINATION bin)
diff --git a/lldb/tools/lldb-platform/lldb-platform.cpp b/lldb/tools/lldb-platform/lldb-platform.cpp index 112dc9ed04c..007e69c9281 100644 --- a/lldb/tools/lldb-platform/lldb-platform.cpp +++ b/lldb/tools/lldb-platform/lldb-platform.cpp @@ -37,11 +37,13 @@ using namespace lldb_private; int g_debug = 0; int g_verbose = 0; +int g_stay_alive = 0; static struct option g_long_options[] = { { "debug", no_argument, &g_debug, 1 }, { "verbose", no_argument, &g_verbose, 1 }, + { "stay-alive", no_argument, &g_stay_alive, 1 }, { "log-file", required_argument, NULL, 'l' }, { "log-flags", required_argument, NULL, 'f' }, { "listen", required_argument, NULL, 'L' }, @@ -60,16 +62,32 @@ signal_handler(int signo) case SIGPIPE: g_sigpipe_received = 1; break; + case SIGHUP: + // Use SIGINT first, if that does not work, use SIGHUP as a last resort. + // And we should not call exit() here because it results in the global destructors + // to be invoked and wreaking havoc on the threads still running. + Host::SystemLog(Host::eSystemLogWarning, "SIGHUP received, exiting lldb-platform...\n"); + abort(); + break; } } +static void +display_usage (const char *progname) +{ + fprintf(stderr, "Usage:\n %s [--log-file log-file-path] [--log-flags flags] --listen port\n", progname); + exit(0); +} + //---------------------------------------------------------------------- // main //---------------------------------------------------------------------- int main (int argc, char *argv[]) { + const char *progname = argv[0]; signal (SIGPIPE, signal_handler); + signal (SIGHUP, signal_handler); int long_option_index = 0; StreamSP log_stream_sp; Args log_args; @@ -167,8 +185,16 @@ main (int argc, char *argv[]) case 'L': listen_host_port.append (optarg); break; + + case 'h': /* fall-through is intentional */ + case '?': + display_usage(progname); + break; } } + // Print usage and exit if no listening port is specified. + if (listen_host_port.empty()) + display_usage(progname); if (log_stream_sp) { @@ -182,50 +208,63 @@ main (int argc, char *argv[]) argv += optind; - GDBRemoteCommunicationServer gdb_server (true); - if (!listen_host_port.empty()) - { - std::unique_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor()); - if (conn_ap.get()) + do { + GDBRemoteCommunicationServer gdb_server (true); + if (!listen_host_port.empty()) { - std::string connect_url ("listen://"); - connect_url.append(listen_host_port.c_str()); - - printf ("Listening for a connection on %s...\n", listen_host_port.c_str()); - if (conn_ap->Connect(connect_url.c_str(), &error) == eConnectionStatusSuccess) + std::unique_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor()); + if (conn_ap.get()) { - printf ("Connection established.\n"); - gdb_server.SetConnection (conn_ap.release()); - } - } - } - + for (int j = 0; j < listen_host_port.size(); j++) + { + char c = listen_host_port[j]; + if (c > '9' || c < '0') + printf("WARNING: passing anything but a number as argument to --listen will most probably make connecting impossible.\n"); + } + std::auto_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor()); + if (conn_ap.get()) + { + std::string connect_url ("listen://"); + connect_url.append(listen_host_port.c_str()); - if (gdb_server.IsConnected()) - { - // After we connected, we need to get an initial ack from... - if (gdb_server.HandshakeWithClient(&error)) - { - bool interrupt = false; - bool done = false; - while (!interrupt && !done) - { - if (!gdb_server.GetPacketAndSendResponse (UINT32_MAX, error, interrupt, done)) - break; + printf ("Listening for a connection on %s...\n", listen_host_port.c_str()); + if (conn_ap->Connect(connect_url.c_str(), &error) == eConnectionStatusSuccess) + { + printf ("Connection established.\n"); + gdb_server.SetConnection (conn_ap.release()); + } + } } - - if (error.Fail()) + + if (gdb_server.IsConnected()) { - fprintf(stderr, "error: %s\n", error.AsCString()); + // After we connected, we need to get an initial ack from... + if (gdb_server.HandshakeWithClient(&error)) + { + bool interrupt = false; + bool done = false; + while (!interrupt && !done) + { + if (!gdb_server.GetPacketAndSendResponse (UINT32_MAX, error, interrupt, done)) + break; + } + + if (error.Fail()) + { + fprintf(stderr, "error: %s\n", error.AsCString()); + } + } + else + { + fprintf(stderr, "error: handshake with client failed\n"); + } } } - else - { - fprintf(stderr, "error: handshake with client failed\n"); - } - } + } while (g_stay_alive); Debugger::Terminate(); + fprintf(stderr, "lldb-platform exiting...\n"); + return 0; } |