Esempio n. 1
0
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    public int LaunchSuspended (string pszServer, IDebugPort2 port, string exe, string args, string dir, string env, string options, enum_LAUNCH_FLAGS launchFlags, uint hStdInput, uint hStdOutput, uint hStdError, IDebugEventCallback2 ad7Callback, out IDebugProcess2 process)
    {
      // 
      // Normally, VS launches a program using the IDebugPortEx2::LaunchSuspended method, and the attaches the debugger to the suspended program.
      // However, there are circumstances in which the DebugEngine may need to launch a program or other dependencies (e.g. tools or interpreters) in which case this method is used.
      // IDebugEngineLaunch2::ResumeProcess method is called to start the process after the program has been launched in a suspended state.
      // 

      LoggingUtils.PrintFunction ();

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

        if (string.IsNullOrEmpty (exe))
        {
          throw new ArgumentNullException ("exe");
        }

        if (!File.Exists (exe))
        {
          throw new FileNotFoundException ("Failed to find target application: " + exe);
        }

        m_sdmCallback = new DebugEngineCallback (this, ad7Callback);

        DebuggeePort debuggeePort = port as DebuggeePort;

        DebuggeeProcess debugProcess = null;

        // 
        // Evaluate options; including current debugger target application.
        // 

        if (m_launchConfiguration == null)
        {
          throw new InvalidOperationException ("No launch configuration found.");
        }

        m_launchConfiguration.FromString (options);

        string packageName = m_launchConfiguration ["PackageName"];

        string launchActivity = m_launchConfiguration ["LaunchActivity"];

        bool debugMode = m_launchConfiguration ["DebugMode"].Equals ("true");

        bool openGlTrace = m_launchConfiguration ["OpenGlTrace"].Equals ("true");

        bool appIsRunning = false;

        // 
        // Cache any LaunchSuspended specific parameters.
        // 

        m_launchConfiguration ["LaunchSuspendedExe"] = exe;

        m_launchConfiguration ["LaunchSuspendedDir"] = dir;

        m_launchConfiguration ["LaunchSuspendedEnv"] = env;

        // 
        // Prevent blocking the main VS thread when launching a suspended application.
        // 

        Broadcast (new DebugEngineEvent.DebuggerConnectionEvent (DebugEngineEvent.DebuggerConnectionEvent.EventType.ShowDialog, string.Empty), null, null);

        ManualResetEvent launchSuspendedMutex = new ManualResetEvent (false);

        Thread asyncLaunchSuspendedThread = new Thread (delegate ()
        {
          try
          {
            // 
            // Launch application on device in a 'suspended' state.
            // 

            Broadcast (new DebugEngineEvent.DebuggerConnectionEvent (DebugEngineEvent.DebuggerConnectionEvent.EventType.LogStatus, string.Format ("Starting '{0}'...", packageName)), null, null);

            if (!appIsRunning)
            {
              StringBuilder launchArgumentsBuilder = new StringBuilder ();

              launchArgumentsBuilder.Append ("start ");

              if (debugMode)
              {
                launchArgumentsBuilder.Append ("-D "); // debug
              }
              else
              {
                launchArgumentsBuilder.Append ("-W "); // wait
              }

              launchArgumentsBuilder.Append ("-S "); // force stop the target app before starting the activity

              if (openGlTrace)
              {
                launchArgumentsBuilder.Append ("--opengl-trace ");
              }

              launchArgumentsBuilder.Append (packageName + "/" + launchActivity);

              Broadcast (new DebugEngineEvent.DebuggerConnectionEvent (DebugEngineEvent.DebuggerConnectionEvent.EventType.LogStatus, string.Format ("[adb:shell:am] {0}", launchArgumentsBuilder)), null, null);

              string launchResponse = debuggeePort.PortDevice.Shell ("am", launchArgumentsBuilder.ToString ());

              if (string.IsNullOrEmpty (launchResponse) || launchResponse.Contains ("Error:"))
              {
                throw new InvalidOperationException ("Launch intent failed:\n" + launchResponse);
              }
            }

            // 
            // Query whether the target application is already running. (Double-check)
            // 

            int launchAttempt = 1;

            int maxLaunchAttempts = 20;

            while (!appIsRunning)
            {
              Broadcast(new DebugEngineEvent.DebuggerConnectionEvent(DebugEngineEvent.DebuggerConnectionEvent.EventType.LogStatus, string.Format("Waiting for '{0}' to launch (Attempt {1} of {2})...", packageName, launchAttempt, maxLaunchAttempts)), null, null);

              LoggingUtils.RequireOk (debuggeePort.RefreshProcesses ());

              // 
              // Validate that the process is running and was spawned by one of the zygote processes.
              // 

              uint [] zygotePids = debuggeePort.PortDevice.GetPidsFromName ("zygote");

              uint [] zygote64Pids = debuggeePort.PortDevice.GetPidsFromName ("zygote64");

              uint [] packagePids = debuggeePort.PortDevice.GetPidsFromName (packageName);

              for (int i = packagePids.Length - 1; i >= 0; --i)
              {
                uint pid = packagePids [i];

                AndroidProcess packageProcess = debuggeePort.PortDevice.GetProcessFromPid (pid);

                bool spawnedByZygote = false;

                if ((zygotePids.Length > 0) && (packageProcess.ParentPid == zygotePids [0]))
                {
                  spawnedByZygote = true;
                }
                else if ((zygote64Pids.Length > 0) && (packageProcess.ParentPid == zygote64Pids [0]))
                {
                  spawnedByZygote = true;
                }

                if (spawnedByZygote)
                {
                  debugProcess = debuggeePort.GetProcessForPid (pid);

                  appIsRunning = (debugProcess != null);

                  break;
                }
              }

              if (!appIsRunning)
              {
                if (++launchAttempt > maxLaunchAttempts)
                {
                  throw new TimeoutException (string.Format ("'{0}' failed to launch. Please ensure device is unlocked.", packageName));
                }

                Application.DoEvents ();

                Thread.Sleep (100);
              }
            }

            launchSuspendedMutex.Set ();
          }
          catch (Exception e)
          {
            LoggingUtils.HandleException (e);

            string error = string.Format ("[Exception] {0}\n{1}", e.Message, e.StackTrace);

            Broadcast (ad7Callback, new DebugEngineEvent.Error (error, true), null, null);

            launchSuspendedMutex.Set ();
          }
        });

        asyncLaunchSuspendedThread.Start ();

        while (!launchSuspendedMutex.WaitOne (0))
        {
          Application.DoEvents ();

          Thread.Sleep (100);
        }

        // 
        // Attach to launched process.
        // 

        if (debugProcess == null)
        {
          throw new InvalidOperationException (string.Format ("'{0}' failed to launch. Could not continue.", packageName));
        }

        process = debugProcess;

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

        process = null;

        try
        {
          string error = string.Format ("[Exception] {0}\n{1}", e.Message, e.StackTrace);

          Broadcast (ad7Callback, new DebugEngineEvent.Error (error, true), null, null);
        }
        catch
        {
          LoggingUtils.HandleException (e);
        }

        return Constants.E_FAIL;
      }
    }
Esempio n. 2
0
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    #region IDebugEngine2 Members

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

    public int Attach (IDebugProgram2 [] rgpPrograms, IDebugProgramNode2 [] rgpProgramNodes, uint celtPrograms, IDebugEventCallback2 ad7Callback, enum_ATTACH_REASON dwReason)
    {
      // 
      // Attach the debug engine to a program.
      // 

      LoggingUtils.PrintFunction ();

      m_sdmCallback = new DebugEngineCallback (this, ad7Callback);

      m_cLangCallback = new CLangDebuggerCallback (this);

      m_javaLangCallback = new JavaLangDebuggerCallback (this);

      try
      {
        if ((rgpPrograms == null) || (rgpPrograms.Length == 0))
        {
          throw new ApplicationException ("Attach failed. No target process specified.");
        }

        if (celtPrograms > 1)
        {
          throw new ApplicationException ("Attach failed. Can not debug multiple target processes concurrently.");
        }

        if (Program != null)
        {
          throw new ApplicationException ("Attach failed. Already attached to " + Program.DebugProcess.NativeProcess.Name);
        }

        AndroidAdb.Refresh ();

        Program = rgpPrograms [0] as DebuggeeProgram;

        Program.AttachedEngine = this;

        Program.DebugProcess.NativeProcess.RefreshPackageInfo ();

        Broadcast (new DebugEngineEvent.DebuggerConnectionEvent (DebugEngineEvent.DebuggerConnectionEvent.EventType.LogStatus, string.Format ("Starting GDB client...")), null, null);

        NativeDebugger = new CLangDebugger (this, m_launchConfiguration, Program);

        Broadcast (new DebugEngineEvent.DebuggerConnectionEvent (DebugEngineEvent.DebuggerConnectionEvent.EventType.LogStatus, string.Format ("Starting JDB client...")), null, null);

        JavaDebugger = new JavaLangDebugger (this, Program);

        ThreadPool.QueueUserWorkItem (delegate (object obj)
        {
          // 
          // When this method is called, the DE needs to send these events in sequence:
          // 1. IDebugEngineCreate2
          // 2. IDebugProgramCreateEvent2
          // 3. IDebugLoadCompleteEvent2
          // 4. (if enum_ATTACH_REASON.ATTACH_REASON_LAUNCH), IDebugEntryPointEvent2
          // 

          try
          {
            Broadcast (new DebugEngineEvent.EngineCreate (this), Program, null);

            // 
            // Run a couple of tests which prevent the run-as tool from functioning properly:
            // 
            // 1) Test if this device/emulator is susceptible to a (usually 4.3 specific) run-as permissions bug.
            //      https://code.google.com/p/android/issues/detail?id=58373
            // 2) Test if the installed package is not declared 'debuggable'.
            // 

            AndroidDevice debuggingDevice = Program.DebugProcess.NativeProcess.HostDevice;

            string runasPackageFileList = debuggingDevice.Shell (string.Format ("run-as {0}", Program.DebugProcess.NativeProcess.Name), "ls -l");

            if (runasPackageFileList.Contains (string.Format ("run-as: Package '{0}' is unknown", Program.DebugProcess.NativeProcess.Name)))
            {
              throw new InvalidOperationException ("Can not debug native code on this device/emulator.\nMore info: https://code.google.com/p/android/issues/detail?id=58373");
            }
            else if (runasPackageFileList.Contains (string.Format ("run-as: Package '{0}' is not debuggable", Program.DebugProcess.NativeProcess.Name)))
            {
              throw new InvalidOperationException (string.Format ("Package '{0}' is not debuggable.\nPlease ensure you're trying to connect to a 'Debug' application.\nAlternatively, completely uninstall the current app and try again.", Program.DebugProcess.NativeProcess.Name));
            }

            Broadcast (new DebugEngineEvent.DebuggerConnectionEvent (DebugEngineEvent.DebuggerConnectionEvent.EventType.LogStatus, string.Format ("Attaching to '{0}'...", Program.DebugProcess.NativeProcess.Name)), null, null);

            LoggingUtils.RequireOk (Program.Attach (m_sdmCallback), "Failed to attach to target application.");

            CLangDebuggeeThread currentThread = null;

            NativeDebugger.RunInterruptOperation (delegate (CLangDebugger debugger)
            {
              debugger.NativeProgram.RefreshAllThreads ();

              currentThread = debugger.NativeProgram.GetThread (debugger.NativeProgram.CurrentThreadId);

              if (currentThread == null)
              {
                // Lack of current thread is usually a good indication that connection/attaching failed.
                throw new InvalidOperationException (string.Format ("Failed to retrieve program's main thread (tid: {0}).", debugger.NativeProgram.CurrentThreadId));
              }
            });

            Broadcast (new DebugEngineEvent.ProgramCreate (), Program, null);

            Broadcast (new DebugEngineEvent.LoadComplete (), Program, currentThread);

            if (dwReason == enum_ATTACH_REASON.ATTACH_REASON_LAUNCH)
            {
              Broadcast (new DebugEngineEvent.EntryPoint (), Program, currentThread);
            }

            Broadcast (new DebugEngineEvent.AttachComplete (), Program, null);

            Broadcast (new DebugEngineEvent.DebuggerLogcatEvent (debuggingDevice), Program, null);

            Broadcast (new DebugEngineEvent.DebuggerConnectionEvent (DebugEngineEvent.DebuggerConnectionEvent.EventType.LogStatus, string.Format ("Attached successfully to '{0}'.", Program.DebugProcess.NativeProcess.Name)), null, null);

            Broadcast (new DebugEngineEvent.DebuggerConnectionEvent (DebugEngineEvent.DebuggerConnectionEvent.EventType.CloseDialog, string.Empty), null, null);
          }
          catch (Exception e)
          {
            LoggingUtils.HandleException (e);

            Broadcast (ad7Callback, new DebugEngineEvent.Error (e.Message, true), Program, null);

            Detach (Program);
          }
        });

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

        Broadcast (ad7Callback, new DebugEngineEvent.Error (e.Message, true), Program, null);

        Detach (Program);

        return Constants.E_FAIL;
      }
    }