//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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(); } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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); } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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; } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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); } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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 (); }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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()); }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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); } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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 (); }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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 [] {}; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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); } }