////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        protected void ProcessExited(object sendingProcess, EventArgs args)
        {
            try
            {
                try
                {
                    m_exitCode = m_process.ExitCode;
                }
                catch (InvalidOperationException)
                {
                    // Ignore: 'No process is associated with this object'.
                }

                if (m_exitMutex != null)
                {
                    m_exitMutex.Set();
                }

                m_lastOutputTimestamp = Environment.TickCount;
            }
            catch (Exception e)
            {
                LoggingUtils.HandleException(e);
            }
            finally
            {
                LoggingUtils.Print(string.Format("[SyncRedirectProcess] {0} exited ({1}) in {2} ms", StartInfo.FileName, m_exitCode, Environment.TickCount - m_startTicks));

                Dispose();
            }
        }
Exemple #2
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

            try
            {
                int exitCode = -1;

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

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

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

                throw new InvalidOperationException("[push] failed", e);
            }
        }
Exemple #3
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public string Shell(string command, string arguments, int timeout = 30000)
        {
            LoggingUtils.PrintFunction();

            try
            {
                int exitCode = -1;

                using (SyncRedirectProcess process = AndroidAdb.AdbCommand(this, "shell", string.Format("{0} {1}", command, arguments)))
                {
                    exitCode = process.StartAndWaitForExit(timeout);

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

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

                throw new InvalidOperationException(string.Format("[shell:{0}] failed", command), e);
            }
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public int StartAndWaitForExit(int idleTimeout)
        {
            LoggingUtils.PrintFunction();

            try
            {
                if (idleTimeout <= 0)
                {
                    throw new ArgumentException("idleTimeout must be positive and non-zero.");
                }

                Start();

                int timeoutFromCurrentTick = (idleTimeout + m_lastOutputTimestamp) - Environment.TickCount;

                bool responseSignaled = false;

                while ((!responseSignaled) && (timeoutFromCurrentTick > 0))
                {
                    if (m_exitMutex != null)
                    {
                        responseSignaled = m_exitMutex.WaitOne(0);
                    }
                    else
                    {
                        responseSignaled = true;
                    }

                    if (!responseSignaled)
                    {
                        timeoutFromCurrentTick = (idleTimeout + m_lastOutputTimestamp) - Environment.TickCount;

                        Thread.Sleep(100);
                    }
                }

                if (!responseSignaled)
                {
                    throw new TimeoutException("Timed out waiting for synchronous redirect process.");
                }

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

                throw;
            }
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

            try
            {
                if (m_gdbServerInstance != null)
                {
                    m_gdbServerInstance.Kill();
                }
            }
            catch (Exception e)
            {
                LoggingUtils.HandleException(e);
            }
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

            try
            {
                if ((m_process != null) && (!m_process.HasExited))
                {
                    m_process.Kill();
                }
            }
            catch (Exception e)
            {
                LoggingUtils.HandleException(e);
            }
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        protected void ProcessStderr(object sendingProcess, DataReceivedEventArgs args)
        {
            LoggingUtils.Print(string.Format("[AsyncRedirectProcess] ProcessStderr: {0}", args.Data));

            try
            {
                m_lastOutputTimestamp = Environment.TickCount;

                if (m_listener != null)
                {
                    m_listener.ProcessStderr(sendingProcess, args);
                }
            }
            catch (Exception e)
            {
                LoggingUtils.HandleException(e);
            }
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public void ProcessStderr(object sendingProcess, DataReceivedEventArgs args)
        {
            try
            {
                m_timeSinceLastOperation.Restart();

                if (!string.IsNullOrWhiteSpace(args.Data))
                {
                    LoggingUtils.Print(string.Format("[JdbClient] ProcessStderr: {0}", args.Data));

                    OnAsyncStderr(new string [] { args.Data });
                }
            }
            catch (Exception e)
            {
                LoggingUtils.HandleException(e);
            }
        }
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

            try
            {
                m_jdbSetup.ClearPortForwarding();

                m_jdbSetup.SetupPortForwarding();

                m_jdbClientInstance.Start(this);

                m_timeSinceLastOperation.Start();

                /*uint timeout = 15000;
                 *
                 * bool responseSignaled = false;
                 *
                 * while ((!responseSignaled) && (m_timeSinceLastOperation.ElapsedMilliseconds < timeout))
                 * {
                 * responseSignaled = m_sessionStarted.WaitOne (0);
                 *
                 * if (!responseSignaled)
                 * {
                 *  Thread.Sleep (100);
                 * }
                 * }
                 *
                 * if (!responseSignaled)
                 * {
                 * throw new TimeoutException ("Timed out waiting for JDB client to execute/attach");
                 * }*/
            }
            catch (Exception e)
            {
                LoggingUtils.HandleException(e);

                throw;
            }
        }
Exemple #10
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public void ProcessExited(object sendingProcess, EventArgs args)
        {
            try
            {
                m_timeSinceLastOperation.Restart();

                LoggingUtils.Print(string.Format("[JdbClient] ProcessExited"));

                m_jdbClientInstance = null;

                //
                // If we're waiting on a synchronous command, signal a finish to process termination.
                //

                foreach (KeyValuePair <string, ManualResetEvent> syncKeyPair in m_syncCommandLocks)
                {
                    syncKeyPair.Value.Set();
                }
            }
            catch (Exception e)
            {
                LoggingUtils.HandleException(e);
            }
        }
Exemple #11
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public ICollection <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
            {
                HashSet <string> deviceBinaries = new HashSet <string> ();

                foreach (string path in Process.NativeLibraryAbiPaths)
                {
                    string ls = string.Empty;

                    try
                    {
                        ls = Process.HostDevice.Shell("ls", path);

                        if (ls.ToLowerInvariant().Contains("no such file"))
                        {
                            throw new DirectoryNotFoundException(path);
                        }
                    }
                    catch (Exception)
                    {
                    }
                    finally
                    {
                        var libraries = ls.Replace("\r", "").Split(new char [] { '\n' }, StringSplitOptions.RemoveEmptyEntries);

                        foreach (string file in libraries)
                        {
                            string lib = path + '/' + file;

                            deviceBinaries.Add(lib);
                        }
                    }
                }

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

                List <string> applicationLibraries = new List <string> (deviceBinaries.Count);

                foreach (string binary in deviceBinaries)
                {
                    string cachePath = Path.Combine(CacheSysRoot, binary.Substring(1).Replace('/', '\\'));

                    Directory.CreateDirectory(Path.GetDirectoryName(cachePath));

                    try
                    {
                        if (Process.HostDevice.SdkVersion >= AndroidSettings.VersionCode.LOLLIPOP)
                        {
                            string temporaryStorage = "/data/local/tmp/" + Path.GetFileName(cachePath);

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

                            Process.HostDevice.Pull(temporaryStorage, cachePath);

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

                            LoggingUtils.Print(string.Format("[GdbSetup] Pulled {0} from device/emulator.", binary));
                        }
                        else
                        {
                            Process.HostDevice.Pull(binary, cachePath);
                        }

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

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

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

            return(new List <string>());
        }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    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 ();
    }
Exemple #13
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public ICollection <string> CacheSystemLibraries()
        {
            //
            // Evaluate the remote libraries required for debugging on the host device.
            //

            LoggingUtils.PrintFunction();

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

            systemLibraries.AddRange(new string []
            {
                "/system/lib/libandroid.so",
                "/system/lib/libandroid_runtime.so",
                "/system/lib/libbinder.so",
                "/system/lib/libc.so",
                "/system/lib/libEGL.so",
                "/system/lib/libGLESv1_CM.so",
                "/system/lib/libGLESv2.so",
                "/system/lib/libutils.so",
            });

            try
            {
                string dir = "/system/lib64";

                string ls = Process.HostDevice.Shell("ls", dir);

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

                systemLibraries.AddRange(new string []
                {
                    "/system/lib64/libandroid.so",
                    "/system/lib64/libandroid_runtime.so",
                    "/system/lib64/libbinder.so",
                    "/system/lib64/libc.so",
                    "/system/lib64/libEGL.so",
                    "/system/lib64/libGLESv1_CM.so",
                    "/system/lib64/libGLESv2.so",
                    "/system/lib64/libutils.so",
                });
            }
            catch (Exception)
            {
                // Ignore. No lib64 directory?
            }

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

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

            foreach (string binary in systemLibraries)
            {
                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());
        }
Exemple #14
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);
            }
        }
Exemple #15
0
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public void ProcessStdout(object sendingProcess, DataReceivedEventArgs args)
        {
            if (!string.IsNullOrEmpty(args.Data))
            {
                LoggingUtils.Print(string.Format("[JdbClient] ProcessStdout: {0}", args.Data));

                try
                {
                    m_timeSinceLastOperation.Restart();

                    if (args.Data.Equals("Initializing jdb ..."))
                    {
                        m_sessionStarted.Set();
                    }

                    //
                    // Distribute result records to registered delegate callbacks.
                    //

                    OnAsyncStdout(new string [] { args.Data });

                    //
                    // Collate output for any ongoing async commands.
                    //

                    lock (m_asyncCommandData)
                    {
                        foreach (KeyValuePair <uint, AsyncCommandData> asyncCommand in m_asyncCommandData)
                        {
                            if (!asyncCommand.Value.Command.StartsWith("-"))
                            {
                                asyncCommand.Value.OutputLines.Add(args.Data);
                            }
                        }
                    }

                    //
                    // Call the corresponding registered delegate for the token response.
                    //

                    uint token = m_sessionCommandToken;

                    AsyncCommandData callbackCommandData = null;

                    lock (m_asyncCommandData)
                    {
                        if (m_asyncCommandData.TryGetValue(token, out callbackCommandData))
                        {
                            m_asyncCommandData.Remove(token);
                        }
                    }

                    //
                    // Spawn any registered callback handlers on a dedicated thread, as not to block JDB output.
                    //

                    if ((callbackCommandData != null) && (callbackCommandData.OutputDelegate != null))
                    {
                        ThreadPool.QueueUserWorkItem(delegate(object state)
                        {
                            try
                            {
                                callbackCommandData.OutputDelegate(callbackCommandData.OutputLines.ToArray());
                            }
                            catch (Exception e)
                            {
                                LoggingUtils.HandleException(e);
                            }
                        });
                    }
                }
                catch (Exception e)
                {
                    LoggingUtils.HandleException(e);
                }
            }
        }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    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 ();
    }
Exemple #17
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.");
            }
        }
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    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 [] {};
    }
Exemple #19
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);
            }
        }