Example #1
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public static MiRecord ParseGdbOutputRecord(string streamOutput)
        {
            if (string.IsNullOrEmpty(streamOutput))
            {
                return(null);
            }

            //
            // Process any leading 'async-record' or 'result-record' token.
            //

            int streamIndex = 0;

            if (streamOutput.StartsWith("(gdb)"))
            {
                //
                // GDB prompt. Waiting for input.
                //

                return(new MiPromptRecord());
            }
            else if (streamOutput [streamIndex] == '~')
            {
                //
                // Console stream record. Clears leading '~" and trailing '\\n"' characters.
                //

                ++streamIndex;

                StringBuilder consoleStreamBuilder = new StringBuilder(streamOutput.Trim(new char [] { '~', '\"' }));

                //consoleStreamBuilder.Replace ("\\n", "\n");

                return(new MiStreamRecord(MiStreamRecord.StreamType.Console, consoleStreamBuilder.ToString()));
            }
            else if (streamOutput [streamIndex] == '@')
            {
                //
                // Target stream record. Clears leading '@" and trailing '\\n"' characters.
                //

                ++streamIndex;

                StringBuilder targetStreamBuilder = new StringBuilder(streamOutput.Trim(new char [] { '@', '\"' }));

                //targetStreamBuilder.Replace ("\\n", "\n");

                return(new MiStreamRecord(MiStreamRecord.StreamType.Target, targetStreamBuilder.ToString()));
            }
            else if (streamOutput [streamIndex] == '&')
            {
                //
                // Log stream record. Clears leading '&" and trailing '\\n"' characters.
                //

                ++streamIndex;

                StringBuilder logStreamBuilder = new StringBuilder(streamOutput.Trim(new char [] { '&', '\"' }));

                //logStreamBuilder.Replace ("\\n", "\n");

                return(new MiStreamRecord(MiStreamRecord.StreamType.Log, logStreamBuilder.ToString()));
            }
            else
            {
                //
                // The following record types have associated key-pair data; identify the type and build a result collection.
                //

                string recordData = streamOutput.Substring(streamIndex);

                int bufferStartPos = 0;

                int bufferCurrentPos = bufferStartPos;

                char type = '^';

                uint token = 0;

                while (bufferCurrentPos < streamOutput.Length)
                {
                    if (((bufferCurrentPos + 1) >= streamOutput.Length) || (streamOutput [bufferCurrentPos + 1] == ','))
                    {
                        string clazz = recordData.Substring(bufferStartPos, (bufferCurrentPos + 1) - bufferStartPos);

                        string data = string.Empty;

                        if (((bufferCurrentPos + 1) < streamOutput.Length) && (streamOutput [bufferCurrentPos + 1] == ','))
                        {
                            data = recordData.Substring(bufferCurrentPos + 2);
                        }

                        MiRecord resultRecord = null;

                        List <MiResultValue> values = new List <MiResultValue> ();

                        try
                        {
                            ParseAllResults(data, ref values);
                        }
                        catch (Exception e)
                        {
                            LoggingUtils.HandleException(e);
                        }
                        finally
                        {
                            switch (type)
                            {
                            case '^': resultRecord = new MiResultRecord(token, clazz, values); break;

                            case '*': resultRecord = new MiAsyncRecord(MiAsyncRecord.AsyncType.Exec, token, clazz, values); break;

                            case '+': resultRecord = new MiAsyncRecord(MiAsyncRecord.AsyncType.Status, token, clazz, values); break;

                            case '=': resultRecord = new MiAsyncRecord(MiAsyncRecord.AsyncType.Notify, token, clazz, values); break;
                            }
                        }

                        return(resultRecord);
                    }
                    else if ((recordData [bufferCurrentPos] == '^') || (recordData [bufferCurrentPos] == '*') || (recordData [bufferCurrentPos] == '+') || (recordData [bufferCurrentPos] == '='))
                    {
                        type = recordData [bufferCurrentPos];

                        string stringToken = recordData.Substring(bufferStartPos, bufferCurrentPos);

                        if (!string.IsNullOrWhiteSpace(stringToken))
                        {
                            uint.TryParse(stringToken, out token);
                        }

                        bufferStartPos = ++bufferCurrentPos;
                    }

                    ++bufferCurrentPos;
                }

                return(null);
            }
        }
Example #2
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public void RefreshProcesses(uint processIdFilter = 0)
        {
            //
            // Skip the first line, and read in tab-separated process data.
            //

            LoggingUtils.PrintFunction();

            string deviceProcessList = Shell("ps", string.Format("-t {0}", ((processIdFilter == 0) ? "" : processIdFilter.ToString())));

            if (!string.IsNullOrEmpty(deviceProcessList))
            {
                var processesOutputLines = deviceProcessList.Replace("\r", "").Split(new char [] { '\n' });

                string processesRegExPattern = @"(?<user>[^ ]+)[ ]*(?<pid>[0-9]+)[ ]*(?<ppid>[0-9]+)[ ]*(?<vsize>[0-9]+)[ ]*(?<rss>[0-9]+)[ ]*(?<wchan>[^ ]+)[ ]*(?<pc>[A-Za-z0-9]+)[ ]*(?<s>[^ ]+)[ ]*(?<name>[^\r\n]+)";

                Regex regExMatcher = new Regex(processesRegExPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);

                m_deviceProcessesByPid.Clear();

                m_devicePidsByName.Clear();

                m_devicePidsByPpid.Clear();

                for (uint i = 1; i < processesOutputLines.Length; ++i)
                {
                    if (!string.IsNullOrEmpty(processesOutputLines [i]))
                    {
                        Match regExLineMatches = regExMatcher.Match(processesOutputLines [i]);

                        string processUser = regExLineMatches.Result("${user}");

                        uint processPid = uint.Parse(regExLineMatches.Result("${pid}"));

                        uint processPpid = uint.Parse(regExLineMatches.Result("${ppid}"));

                        uint processVsize = uint.Parse(regExLineMatches.Result("${vsize}"));

                        uint processRss = uint.Parse(regExLineMatches.Result("${rss}"));

                        string processWchan = regExLineMatches.Result("${wchan}");

                        string processPc = regExLineMatches.Result("${pc}");

                        string processPcS = regExLineMatches.Result("${s}");

                        string processName = regExLineMatches.Result("${name}");

                        AndroidProcess process = new AndroidProcess(this, processName, processPid, processPpid, processUser);

                        m_deviceProcessesByPid [processPid] = process;

                        //
                        // Add new process to a fast-lookup collection organised by process name.
                        //

                        if (!m_devicePidsByName.TryGetValue(processName, out HashSet <uint> processPidsList))
                        {
                            processPidsList = new HashSet <uint> ();
                        }

                        if (!processPidsList.Contains(processPid))
                        {
                            processPidsList.Add(processPid);
                        }

                        m_devicePidsByName [processName] = processPidsList;

                        //
                        // Check whether this process is sibling of another; keep ppids-pid relationships tracked.
                        //

                        if (!m_devicePidsByPpid.TryGetValue(processPpid, out HashSet <uint> processPpidSiblingList))
                        {
                            processPpidSiblingList = new HashSet <uint> ();
                        }

                        if (!processPpidSiblingList.Contains(process.Pid))
                        {
                            processPpidSiblingList.Add(process.Pid);
                        }

                        m_devicePidsByPpid [processPpid] = processPpidSiblingList;
                    }
                }
            }
        }
Example #3
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public void Refresh()
        {
            LoggingUtils.PrintFunction();

            RefreshProcesses();
        }
Example #4
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public static void Refresh()
        {
            LoggingUtils.PrintFunction();

            lock (m_updateLockMutex)
            {
                //
                // Start an ADB instance, if required.
                //

                using (SyncRedirectProcess adbStartServer = new SyncRedirectProcess(AndroidSettings.SdkRoot + @"\platform-tools\adb.exe", "start-server"))
                {
                    adbStartServer.StartAndWaitForExit(30000);
                }

                using (SyncRedirectProcess adbDevices = new SyncRedirectProcess(AndroidSettings.SdkRoot + @"\platform-tools\adb.exe", "devices"))
                {
                    adbDevices.StartAndWaitForExit(30000);

                    //
                    // Parse 'devices' output, skipping headers and potential 'start-server' output.
                    //

                    Dictionary <string, string> currentAdbDevices = new Dictionary <string, string> ();

                    LoggingUtils.Print(string.Format("[AndroidAdb] Devices output: {0}", adbDevices.StandardOutput));

                    if (!String.IsNullOrEmpty(adbDevices.StandardOutput))
                    {
                        string [] deviceOutputLines = adbDevices.StandardOutput.Replace("\r", "").Split(new char [] { '\n' });

                        foreach (string line in deviceOutputLines)
                        {
                            if (Regex.IsMatch(line, "^[A-Za-z0-9.:\\-]+[\t][a-z]+$"))
                            {
                                string [] segments = line.Split(new char [] { '\t' });

                                string deviceName = segments [0];

                                string deviceType = segments [1];

                                currentAdbDevices.Add(deviceName, deviceType);
                            }
                        }
                    }

                    //
                    // First identify any previously tracked devices which aren't in 'devices' output.
                    //

                    HashSet <string> disconnectedDevices = new HashSet <string> ();

                    foreach (string key in m_connectedDevices.Keys)
                    {
                        string deviceName = (string)key;

                        if (!currentAdbDevices.ContainsKey(deviceName))
                        {
                            disconnectedDevices.Add(deviceName);
                        }
                    }

                    //
                    // Identify whether any devices have changed state; connected/persisted/disconnected.
                    //

                    foreach (KeyValuePair <string, string> devicePair in currentAdbDevices)
                    {
                        string deviceName = devicePair.Key;

                        string deviceType = devicePair.Value;

                        if (deviceType.Equals("offline"))
                        {
                            disconnectedDevices.Add(deviceName);
                        }
                        else if (deviceType.Equals("unauthorized"))
                        {
                            // User needs to allow USB debugging.
                        }
                        else
                        {
                            AndroidDevice connectedDevice;

                            if (m_connectedDevices.TryGetValue(deviceName, out connectedDevice))
                            {
                                //
                                // Device is pervasive. Refresh internal properties.
                                //

                                LoggingUtils.Print(string.Format("[AndroidAdb] Device pervaded: {0} - {1}", deviceName, deviceType));

                                connectedDevice.Refresh();

                                foreach (IStateListener deviceListener in m_registeredDeviceStateListeners)
                                {
                                    deviceListener.DevicePervasive(connectedDevice);
                                }
                            }
                            else
                            {
                                //
                                // Device connected.
                                //

                                LoggingUtils.Print(string.Format("[AndroidAdb] Device connected: {0} - {1}", deviceName, deviceType));

                                connectedDevice = new AndroidDevice(deviceName);

                                connectedDevice.Refresh();

                                m_connectedDevices.Add(deviceName, connectedDevice);

                                foreach (IStateListener deviceListener in m_registeredDeviceStateListeners)
                                {
                                    deviceListener.DeviceConnected(connectedDevice);
                                }
                            }
                        }
                    }

                    //
                    // Finally, handle device disconnection.
                    //

                    foreach (string deviceName in disconnectedDevices)
                    {
                        AndroidDevice disconnectedDevice;

                        if (m_connectedDevices.TryGetValue(deviceName, out disconnectedDevice))
                        {
                            LoggingUtils.Print(string.Format("[AndroidAdb] Device disconnected: {0}", deviceName));

                            m_connectedDevices.Remove(deviceName);

                            foreach (IStateListener deviceListener in m_registeredDeviceStateListeners)
                            {
                                deviceListener.DeviceDisconnected(disconnectedDevice);
                            }
                        }
                    }
                }
            }
        }
Example #5
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public string Pull(string remotePath, string localPath)
        {
            LoggingUtils.PrintFunction();

            try
            {
                //
                // Check if the remote path is a symbolic link, and adjust the target file.
                // (ADB Pull doesn't follow these links)
                //

                try
                {
                    string readlink = Shell("readlink", remotePath).Replace("\r", "").Replace("\n", "");

                    if (readlink.StartsWith("/")) // absolute path link
                    {
                        remotePath = readlink;
                    }
                    else // relative path link
                    {
                        int i = remotePath.LastIndexOf('/');

                        if (i != -1)
                        {
                            string parentPath = remotePath.Substring(0, i);

                            string file = remotePath.Substring(i + 1);

                            remotePath = parentPath + '/' + file;
                        }
                    }
                }
                catch (Exception)
                {
                    // Ignore. Not a relative link.
                }

                //
                // Pull the requested file.
                //

                int exitCode = -1;

                using SyncRedirectProcess process = AndroidAdb.AdbCommand(this, "pull", string.Format("{0} {1}", remotePath, PathUtils.QuoteIfNeeded(localPath)));

                exitCode = process.StartAndWaitForExit();

                if (exitCode != 0)
                {
                    throw new InvalidOperationException(string.Format("[pull] returned error code: {0}", exitCode));
                }

                return(process.StandardOutput);
            }
            catch (Exception e)
            {
                LoggingUtils.HandleException(e);

                throw new InvalidOperationException("[pull] failed", e);
            }
        }
Example #6
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public void ProcessExited(object sendingProcess, EventArgs args)
        {
            LoggingUtils.Print(string.Format("[GdbServer] ProcessExited"));
        }
Example #7
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public void Start()
        {
            LoggingUtils.PrintFunction();

            //
            // Check the target 'gdbserver' binary exits on target device/emulator.
            //

            string gdbServerPath = string.Empty;

            List <string> potentialGdbServerPaths = new List <string> ();

            foreach (string libraryPath in m_gdbSetup.Process.NativeLibraryAbiPaths)
            {
                potentialGdbServerPaths.Add(string.Format("{0}/gdbserver", libraryPath));
            }

            potentialGdbServerPaths.Add(string.Format("{0}/gdbserver", m_gdbSetup.Process.NativeLibraryPath));

            foreach (string path in potentialGdbServerPaths)
            {
                try
                {
                    string ls = m_gdbSetup.Process.HostDevice.Shell("ls", path);

                    if (ls.ToLowerInvariant().Contains("no such file"))
                    {
                        throw new DirectoryNotFoundException(path);
                    }

                    gdbServerPath = path;

                    break;
                }
                catch (Exception)
                {
                    // Ignore.
                }
            }

            //
            // If we can't find a bundled 'gdbserver' binary, attempt to find one in the NDK.
            //

            if (string.IsNullOrWhiteSpace(gdbServerPath))
            {
                foreach (string path in m_gdbSetup.Process.NativeLibraryAbiPaths)
                {
                    try
                    {
                        string shortAbi = path.Substring(path.LastIndexOf('/') + 1);

                        string local = Path.Combine(AndroidSettings.NdkRoot, string.Format(@"prebuilt\android-{0}\gdbserver\gdbserver", shortAbi));

                        string remote = string.Format("/data/local/tmp/gdbserver-{0}", shortAbi);

                        m_gdbSetup.Process.HostDevice.Push(local, remote);
                    }
                    catch (Exception e)
                    {
                        LoggingUtils.HandleException(e);
                    }
                }
            }

            if (string.IsNullOrWhiteSpace(gdbServerPath))
            {
                throw new InvalidOperationException(string.Format("Failed to locate required 'gdbserver' binary on device ({0}).", m_gdbSetup.Process.HostDevice.ID));
            }

            KillActiveGdbServerSessions();

            //
            // Construct a adaptive command line based on GdbSetup requirements.
            //

            StringBuilder commandLineArgumentsBuilder = new StringBuilder();

            commandLineArgumentsBuilder.AppendFormat("run-as {0} {1} ", m_gdbSetup.Process.Name, gdbServerPath);

            if (!string.IsNullOrWhiteSpace(m_gdbSetup.Socket))
            {
                commandLineArgumentsBuilder.AppendFormat("+{0} ", m_gdbSetup.Socket);
            }

            commandLineArgumentsBuilder.Append("--attach ");

            if (string.IsNullOrWhiteSpace(m_gdbSetup.Socket)) // Don't need a host if we have a bound socket?
            {
                commandLineArgumentsBuilder.AppendFormat("{0}:{1} ", m_gdbSetup.Host, m_gdbSetup.Port);
            }

            commandLineArgumentsBuilder.Append(m_gdbSetup.Process.Pid);

            //
            // Launch 'gdbserver' and wait for output to determine success.
            //

            Stopwatch waitForConnectionTimer = new Stopwatch();

            waitForConnectionTimer.Start();

            m_gdbServerAttached = new ManualResetEvent(false);

            m_gdbServerInstance = AndroidAdb.AdbCommandAsync(m_gdbSetup.Process.HostDevice, "shell", commandLineArgumentsBuilder.ToString());

            m_gdbServerInstance.Start(this);

            LoggingUtils.Print(string.Format("[GdbServer] Waiting to attach..."));

            uint timeout = 5000;

            bool responseSignaled = false;

            while ((!responseSignaled) && (waitForConnectionTimer.ElapsedMilliseconds < timeout))
            {
                responseSignaled = m_gdbServerAttached.WaitOne(0);

                if (!responseSignaled)
                {
                    Thread.Sleep(100);
                }
            }

            if (!responseSignaled)
            {
                throw new TimeoutException("Timed out waiting for GdbServer to execute.");
            }
        }
Example #8
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public void Populate(List <MiResultValue> breakpointValues)
        {
            LoggingUtils.PrintFunction();

            if (breakpointValues == null)
            {
                throw new ArgumentNullException("breakpointValues");
            }

            foreach (MiResultValue resultValue in breakpointValues)
            {
                if (resultValue.HasField("number"))
                {
                    ID = resultValue ["number"] [0].GetUnsignedInt();
                }

                if (resultValue.HasField("type"))
                {
                    Type = resultValue ["type"] [0].GetString();
                }

                if (resultValue.HasField("disp"))
                {
                    Disposition = resultValue ["disp"] [0].GetString();
                }

                if (resultValue.HasField("enabled"))
                {
                    Enabled = (resultValue ["enabled"] [0].GetString().Equals("y"));
                }

                if (resultValue.HasField("addr"))
                {
                    string addr = resultValue ["addr"] [0].GetString();

                    bool pending = addr.Equals("<PENDING>");

                    bool multiple = addr.Equals("<MULTIPLE>");

                    if (pending)
                    {
                        Address = Pending;
                    }
                    else if (multiple)
                    {
                        Address = Multiple;
                    }
                    else
                    {
                        if (addr.ToLower().StartsWith("0x"))
                        {
                            Address = ulong.Parse(addr.Substring(2), NumberStyles.HexNumber);
                        }
                        else
                        {
                            Address = ulong.Parse(addr, NumberStyles.HexNumber);
                        }
                    }
                }

                if (resultValue.HasField("func"))
                {
                    Function = resultValue ["func"] [0].GetString();
                }

                if (resultValue.HasField("filename"))
                {
                    Filename = resultValue ["filename"] [0].GetString();
                }

                if (resultValue.HasField("fullname"))
                {
                    Fullname = resultValue ["fullname"] [0].GetString();
                }

                if (resultValue.HasField("line"))
                {
                    Line = resultValue ["line"] [0].GetUnsignedInt();
                }
            }
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public void Populate(List <MiResultValue> variableValues)
        {
            LoggingUtils.PrintFunction();

            if (variableValues == null)
            {
                throw new ArgumentNullException(nameof(variableValues));
            }

            foreach (MiResultValue resultValue in variableValues)
            {
                if (resultValue.HasField("name"))
                {
                    Name = resultValue ["name"] [0].GetString();
                }

                if (resultValue.HasField("exp"))
                {
                    Expression = resultValue ["exp"] [0].GetString();
                }

                if (resultValue.HasField("type"))
                {
                    Type = resultValue ["type"] [0].GetString();
                }

                if (resultValue.HasField("new_type"))
                {
                    Type = resultValue ["new_type"] [0].GetString();
                }

                if (resultValue.HasField("thread-id"))
                {
                    ThreadId = resultValue ["thread-id"] [0].GetUnsignedInt();
                }

                if (resultValue.HasField("dynamic"))
                {
                    Dynamic = (resultValue ["dynamic"] [0].GetUnsignedInt() == 1);
                }

                if (Dynamic && (resultValue.HasField("has_more")))
                {
                    HasChildren = (resultValue ["has_more"] [0].GetUnsignedInt() != 0);
                }
                else
                {
                    if (resultValue.HasField("numchild"))
                    {
                        HasChildren = (resultValue ["numchild"] [0].GetUnsignedInt() != 0);
                    }

                    if (resultValue.HasField("new_num_children"))
                    {
                        HasChildren = (resultValue ["new_num_children"] [0].GetUnsignedInt() != 0);
                    }
                }

                if (resultValue.HasField("value"))
                {
                    Value = resultValue ["value"] [0].GetString();
                }
            }
        }
Example #10
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public string [] CacheApplicationLibraries()
        {
            //
            // Application binaries (those under /lib/ of an installed application).
            // TODO: Consider improving this. Pulling libraries ensures consistency, but takes time (ADB is a slow protocol).
            //

            LoggingUtils.PrintFunction();

            try
            {
                List <string> additionalLibraries = new List <string> ();

                foreach (string nativeLibraryAbiPath in Process.NativeLibraryAbiPaths)
                {
                    string nativeOatLibraryPath = nativeLibraryAbiPath.Replace("/lib", "/oat");

                    //
                    // On Android L, Google have broken pull permissions to 'app-lib' (and '/data/app/XXX/lib/') content so we use cp to avoid this.
                    //

                    bool pulledLibraries = false;

                    string libraryCachePath = Path.Combine(CacheSysRoot, nativeLibraryAbiPath.Substring(1));

                    Directory.CreateDirectory(libraryCachePath);

                    if (Process.HostDevice.SdkVersion >= AndroidSettings.VersionCode.LOLLIPOP)
                    {
                        string [] libraries = Process.HostDevice.Shell("ls", nativeLibraryAbiPath).Replace("\r", "").Split(new char [] { '\n' }, StringSplitOptions.RemoveEmptyEntries);

                        foreach (string file in libraries)
                        {
                            string remoteLib = nativeLibraryAbiPath + "/" + file;

                            string temporaryStorage = "/data/local/tmp/" + file;

                            try
                            {
                                Process.HostDevice.Shell("cp", string.Format("-fH {0} {1}", remoteLib, temporaryStorage));

                                Process.HostDevice.Pull(temporaryStorage, libraryCachePath);

                                Process.HostDevice.Shell("rm", temporaryStorage);

                                LoggingUtils.Print(string.Format("[GdbSetup] Pulled {0} from device/emulator.", remoteLib));

                                pulledLibraries = true;
                            }
                            catch (Exception e)
                            {
                                LoggingUtils.HandleException(string.Format("[GdbSetup] Failed pulling {0} from device/emulator.", remoteLib), e);
                            }
                        }
                    }

                    //
                    // Also on Android L, Google's new oat format is an ELF which is readable by GDB. We want to include this in the sysroot.
                    //

                    bool pulledOatLibraries = false;

                    string libraryOatCachePath = Path.Combine(CacheSysRoot, nativeOatLibraryPath.Substring(1));

                    Directory.CreateDirectory(libraryOatCachePath);

                    if (Process.HostDevice.SdkVersion >= AndroidSettings.VersionCode.LOLLIPOP)
                    {
                        string [] oatLibraries = new string []
                        {
                            // Due to permissions these have to be directly referenced; ls won't work.
                            "base.odex"
                        };

                        foreach (string file in oatLibraries)
                        {
                            string remoteLib = nativeOatLibraryPath + "/" + file;

                            string temporaryStorage = "/data/local/tmp/" + file;

                            try
                            {
                                Process.HostDevice.Shell("cp", string.Format("-fH {0} {1}", remoteLib, temporaryStorage));

                                Process.HostDevice.Pull(temporaryStorage, libraryCachePath);

                                Process.HostDevice.Shell("rm", temporaryStorage);

                                LoggingUtils.Print(string.Format("[GdbSetup] Pulled {0} from device/emulator.", remoteLib));

                                pulledOatLibraries = true;
                            }
                            catch (Exception e)
                            {
                                LoggingUtils.HandleException(string.Format("[GdbSetup] Failed pulling {0} from device/emulator.", remoteLib), e);
                            }
                        }
                    }

                    try
                    {
                        if (!pulledLibraries)
                        {
                            Process.HostDevice.Pull(nativeLibraryAbiPath, libraryCachePath);

                            LoggingUtils.Print(string.Format("[GdbSetup] Pulled {0} from device/emulator.", nativeLibraryAbiPath));

                            pulledLibraries = true;
                        }

                        if (!pulledOatLibraries)
                        {
                            Process.HostDevice.Pull(nativeOatLibraryPath, libraryOatCachePath);

                            LoggingUtils.Print(string.Format("[GdbSetup] Pulled {0} from device/emulator.", nativeOatLibraryPath));

                            pulledOatLibraries = true;
                        }
                    }
                    catch (Exception e)
                    {
                        LoggingUtils.HandleException(string.Format("[GdbSetup] Failed pulling {0} from device/emulator.", nativeLibraryAbiPath), e);
                    }

                    additionalLibraries.AddRange(Directory.GetFiles(libraryCachePath, "lib*.so", SearchOption.AllDirectories));

                    additionalLibraries.AddRange(Directory.GetFiles(libraryOatCachePath, "*.odex", SearchOption.AllDirectories));
                }

                return(additionalLibraries.ToArray());
            }
            catch (Exception e)
            {
                LoggingUtils.HandleException(e);
            }

            return(new string [] {});
        }
Example #11
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public string [] CacheSystemLibraries(bool only64bit)
        {
            //
            // Evaluate the remote libraries required for debugging on the host device.
            //

            LoggingUtils.PrintFunction();

            List <string> deviceLibraries = new List <string> ();

            string libdir = (only64bit) ? "lib64" : "lib";

            deviceLibraries.AddRange(new string []
            {
                string.Format("/system/{0}/libandroid.so", libdir),
                string.Format("/system/{0}/libandroid_runtime.so", libdir),
                //string.Format ("/system/{0}/libart.so", libdir),
                string.Format("/system/{0}/libbinder.so", libdir),
                string.Format("/system/{0}/libc.so", libdir),
                //string.Format ("/system/{0}/libdvm.so", libdir),
                string.Format("/system/{0}/libEGL.so", libdir),
                string.Format("/system/{0}/libGLESv1_CM.so", libdir),
                string.Format("/system/{0}/libGLESv2.so", libdir),
                string.Format("/system/{0}/libutils.so", libdir),
            });

            //
            // Pull the required libraries from the device.
            //

            List <string> hostBinaries = new List <string> ();

            foreach (string binary in deviceLibraries)
            {
                string cachedBinary = Path.Combine(CacheSysRoot, binary.Substring(1));

                string cachedBinaryDir = Path.GetDirectoryName(cachedBinary);

                string cachedBinaryFullPath = Path.Combine(Path.GetDirectoryName(cachedBinary), Path.GetFileName(cachedBinary));

                Directory.CreateDirectory(cachedBinaryDir);

                FileInfo cachedBinaryFileInfo = new FileInfo(cachedBinaryFullPath);

                bool usedCached = false;

                if (cachedBinaryFileInfo.Exists && (DateTime.UtcNow - cachedBinaryFileInfo.CreationTimeUtc) < TimeSpan.FromDays(1))
                {
                    LoggingUtils.Print(string.Format("[GdbSetup] Using cached {0}.", binary));

                    hostBinaries.Add(cachedBinaryFullPath);

                    usedCached = true;
                }

                if (!usedCached)
                {
                    try
                    {
                        Process.HostDevice.Pull(binary, cachedBinary);

                        hostBinaries.Add(cachedBinaryFullPath);

                        LoggingUtils.Print(string.Format("[GdbSetup] Pulled {0} from device/emulator.", binary));
                    }
                    catch (Exception e)
                    {
                        LoggingUtils.HandleException(e);
                    }
                }
            }

            return(hostBinaries.ToArray());
        }
Example #12
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public GdbSetup(AndroidProcess process, string gdbToolPath)
        {
            LoggingUtils.PrintFunction();

            Process = process;

            Host = "localhost";

            Port = 5039;

            if (!Process.HostDevice.IsOverWiFi)
            {
                Socket = "debug-socket";
            }

            string sanitisedDeviceId = Process.HostDevice.ID.Replace(':', '-');

            CacheDirectory = string.Format(@"{0}\Android++\Cache\{1}\{2}", Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), sanitisedDeviceId, Process.Name);

            Directory.CreateDirectory(CacheDirectory);

            CacheSysRoot = Path.Combine(CacheDirectory, "sysroot");

            Directory.CreateDirectory(CacheSysRoot);

            SymbolDirectories = new HashSet <string> ();

            GdbToolPath = gdbToolPath;

            GdbToolArguments = "--interpreter=mi ";

            if (!File.Exists(gdbToolPath))
            {
                throw new FileNotFoundException("Could not find requested GDB instance. Expected: " + gdbToolPath);
            }

            //
            // Spawn an initial GDB instance to evaluate the client version.
            //

            GdbToolVersionMajor = 1;

            GdbToolVersionMinor = 0;

            using (SyncRedirectProcess gdbProcess = new SyncRedirectProcess(GdbToolPath, "--version"))
            {
                gdbProcess.StartAndWaitForExit();

                string [] versionDetails = gdbProcess.StandardOutput.Replace("\r", "").Split(new char [] { '\n' });

                string versionPrefix = "GNU gdb (GDB) ";

                for (int i = 0; i < versionDetails.Length; ++i)
                {
                    if (versionDetails [i].StartsWith(versionPrefix))
                    {
                        string gdbVersion = versionDetails [i].Substring(versionPrefix.Length);;

                        string [] gdbVersionComponents = gdbVersion.Split('.');

                        if (gdbVersionComponents.Length > 0)
                        {
                            GdbToolVersionMajor = int.Parse(gdbVersionComponents [0]);
                        }

                        if (gdbVersionComponents.Length > 1)
                        {
                            GdbToolVersionMinor = int.Parse(gdbVersionComponents [1]);
                        }

                        break;
                    }
                }
            }
        }
Example #13
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public string [] CacheSystemBinaries(bool only64bit)
        {
            //
            // Evaluate remote binaries required for debugging, which must be cached on the host device.
            //

            LoggingUtils.PrintFunction();

            List <string> deviceBinaries = new List <string> ();

            if (only64bit && (Process.HostDevice.SdkVersion >= AndroidSettings.VersionCode.LOLLIPOP))
            {
                deviceBinaries.AddRange(new string []
                {
                    "/system/bin/app_process64",
                    "/system/bin/linker64",
                });
            }
            else
            {
                deviceBinaries.AddRange(new string []
                {
                    "/system/bin/app_process",
                    "/system/bin/app_process32",
                    "/system/bin/linker",
                });
            }

            //
            // Pull the required binaries from the device.
            //

            List <string> hostBinaries = new List <string> ();

            foreach (string binary in deviceBinaries)
            {
                string cachedBinary = Path.Combine(CacheSysRoot, binary.Substring(1));

                string cachedBinaryDir = Path.GetDirectoryName(cachedBinary);

                string cachedBinaryFullPath = Path.Combine(cachedBinaryDir, Path.GetFileName(cachedBinary));

                Directory.CreateDirectory(cachedBinaryDir);

                FileInfo cachedBinaryFileInfo = new FileInfo(cachedBinaryFullPath);

                bool usedCached = false;

                if (cachedBinaryFileInfo.Exists && (DateTime.UtcNow - cachedBinaryFileInfo.CreationTimeUtc) < TimeSpan.FromDays(1))
                {
                    LoggingUtils.Print(string.Format("[GdbSetup] Using cached {0}.", binary));

                    hostBinaries.Add(cachedBinaryFullPath);

                    usedCached = true;
                }

                if (!usedCached)
                {
                    try
                    {
                        Process.HostDevice.Pull(binary, cachedBinary);

                        hostBinaries.Add(cachedBinaryFullPath);

                        LoggingUtils.Print(string.Format("[GdbSetup] Pulled {0} from device/emulator.", binary));
                    }
                    catch (Exception e)
                    {
                        LoggingUtils.HandleException(e);
                    }
                }
            }

            return(hostBinaries.ToArray());
        }
Example #14
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public void Dispose()
        {
            LoggingUtils.PrintFunction();
        }