/** Specify environment variables if needed */ @Execute public void stepSetEnvironmentVariables(RequestMonitor rm) { boolean clear = false; Properties properties = new Properties(); try { // here we need to pass the proper container context clear = fBackend.getClearEnvironment(); properties = fBackend.getEnvironmentVariables(); } catch (CoreException e) { rm.setStatus( new Status( IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Cannot get environment information", e)); //$NON-NLS-1$ rm.done(); return; } if (clear == true || properties.size() > 0) { // here we need to pass the proper container context fCommandControl.setEnvironment(properties, clear, rm); } else { rm.done(); } }
/** * This method does the necessary work to setup the input/output streams for the inferior process, * by either preparing the PTY to be used, or by simply leaving the PTY null, which indicates that * the input/output streams of the CLI should be used instead; this decision is based on the type * of session. */ @Execute public void stepInitializeInputOutput(final RequestMonitor rm) { if (fBackend.getSessionType() == SessionType.REMOTE && !fBackend.getIsAttachSession()) { // Remote non-attach sessions don't support multi-process and therefore will not // start new processes. Those sessions will only start the one process, which should // not have a console, because it's output is handled by GDB server. fPty = null; rm.done(); } else { // Every other type of session that can get to this code, is starting a new process // and requires a pty for it. try { fPty = new PTY(); // Tell GDB to use this PTY fCommandControl.queueCommand( fCommandFactory.createMIInferiorTTYSet( (IMIContainerDMContext) getContainerContext(), fPty.getSlaveName()), new ImmediateDataRequestMonitor<MIInfo>(rm) { @Override protected void handleFailure() { // We were not able to tell GDB to use the PTY // so we won't use it at all. fPty = null; rm.done(); } }); } catch (IOException e) { fPty = null; rm.done(); } } }
/** * This method indicates if we should use the -exec-continue command instead of the -exec-run * command. This method can be overridden to allow for customization. */ protected boolean useContinueCommand() { // Note that restart does not apply to remote sessions IGDBBackend backend = fTracker.getService(IGDBBackend.class); if (backend == null) { return false; } // When doing remote non-attach debugging, we use -exec-continue instead of -exec-run // For remote attach, if we get here it is that we are starting a new process // (multi-process), so we want to use -exec-run return backend.getSessionType() == SessionType.REMOTE && !backend.getIsAttachSession(); }
/** Start executing the program. */ @Execute public void stepStartExecution(final RequestMonitor rm) { if (fBackend.getSessionType() != SessionType.CORE) { // Overwrite the program name to use the binary name that was specified. // This is important for multi-process // Bug 342351 fAttributes.put(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, fBinaryName); fProcService.start( getContainerContext(), fAttributes, new DataRequestMonitor<IContainerDMContext>(ImmediateExecutor.getInstance(), rm) { @Override protected void handleSuccess() { assert getData() instanceof IMIContainerDMContext; // Set the container that we created setContainerContext( DMContexts.getAncestorOfType(getData(), IMIContainerDMContext.class)); fDataRequestMonitor.setData(getContainerContext()); // Don't call fDataRequestMonitor.done(), the sequence will // automatically do that when it completes; rm.done(); } }); } else { fDataRequestMonitor.setData(getContainerContext()); rm.done(); } }
/** Specify the arguments to the program that will be run. */ @Execute public void stepSetArguments(RequestMonitor rm) { try { String args = fBackend.getProgramArguments(); if (args != null) { String[] argArray = args.replaceAll("\n", " ").split(" "); // $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ fCommandControl.queueCommand( fCommandFactory.createMIGDBSetArgs(getContainerContext(), argArray), new DataRequestMonitor<MIInfo>(ImmediateExecutor.getInstance(), rm)); } else { rm.done(); } } catch (CoreException e) { rm.setStatus( new Status( IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Cannot get inferior arguments", e)); //$NON-NLS-1$ rm.done(); } }
/** @return true if supports fetching OS info from /proc pseudo-filesystem */ private boolean supportsProcPseudoFS() { if (Platform.getOS().equals(Platform.OS_LINUX)) return true; // for non-linux platform, support only remote (linux? ) targets if (SessionType.REMOTE == fBackend.getSessionType()) { return true; } return false; }
/** * Start tracking the breakpoints. Note that for remote debugging we should first connect to the * target. */ @Execute public void stepStartTrackingBreakpoints(RequestMonitor rm) { if (fBackend.getSessionType() != SessionType.CORE) { MIBreakpointsManager bpmService = fTracker.getService(MIBreakpointsManager.class); IBreakpointsTargetDMContext bpTargetDmc = DMContexts.getAncestorOfType(getContainerContext(), IBreakpointsTargetDMContext.class); bpmService.startTrackingBreakpoints(bpTargetDmc, rm); } else { rm.done(); } }
/** * If we are dealing with a remote debugging session, connect to the target. * * @since 4.0 */ @Execute public void stepRemoteConnection(RequestMonitor rm) { // If we are dealing with a non-attach remote session, it is now time to connect // to the remote side. Note that this is the 'target remote' case // and not the 'target extended-remote' case (remote attach session) // This step is actually global for GDB. However, we have to do it after // we have specified the executable, so we have to do it here. // It is safe to do it here because a 'target remote' does not support // multi-process so this step will not be executed more than once. if (fBackend.getSessionType() == SessionType.REMOTE && !fBackend.getIsAttachSession()) { boolean isTcpConnection = CDebugUtils.getAttribute( fAttributes, IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, false); if (isTcpConnection) { String remoteTcpHost = CDebugUtils.getAttribute( fAttributes, IGDBLaunchConfigurationConstants.ATTR_HOST, INVALID); String remoteTcpPort = CDebugUtils.getAttribute( fAttributes, IGDBLaunchConfigurationConstants.ATTR_PORT, INVALID); fCommandControl.queueCommand( fCommandFactory.createMITargetSelect( fCommandControl.getContext(), remoteTcpHost, remoteTcpPort, false), new DataRequestMonitor<MIInfo>(ImmediateExecutor.getInstance(), rm)); } else { String serialDevice = CDebugUtils.getAttribute( fAttributes, IGDBLaunchConfigurationConstants.ATTR_DEV, INVALID); fCommandControl.queueCommand( fCommandFactory.createMITargetSelect(fCommandControl.getContext(), serialDevice, false), new DataRequestMonitor<MIInfo>(ImmediateExecutor.getInstance(), rm)); } } else { rm.done(); } }
/** * If we are dealing with a postmortem session, connect to the core/trace file. * * @since 4.0 */ @Execute public void stepSpecifyCoreFile(final RequestMonitor rm) { // If we are dealing with a postmortem session, it is now time to connect // to the core/trace file. We have to do this step after // we have specified the executable, so we have to do it here. // It is safe to do it here because a postmortem session does not support // multi-process so this step will not be executed more than once. // Bug 338730 if (fBackend.getSessionType() == SessionType.CORE) { String coreFile = CDebugUtils.getAttribute( fAttributes, ICDTLaunchConfigurationConstants.ATTR_COREFILE_PATH, ""); // $NON-NLS-1$ final String coreType = CDebugUtils.getAttribute( fAttributes, IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_POST_MORTEM_TYPE, IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_TYPE_DEFAULT); if (coreFile.length() == 0) { new PromptForCoreJob( "Prompt for post mortem file", //$NON-NLS-1$ new DataRequestMonitor<String>(getExecutor(), rm) { @Override protected void handleCancel() { rm.cancel(); rm.done(); } @Override protected void handleSuccess() { String newCoreFile = getData(); if (newCoreFile == null || newCoreFile.length() == 0) { rm.setStatus( new Status( IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get post mortem file path", null)); //$NON-NLS-1$ rm.done(); } else { if (coreType.equals( IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_CORE_FILE)) { fCommandControl.queueCommand( fCommandFactory.createMITargetSelectCore( fCommandControl.getContext(), newCoreFile), new DataRequestMonitor<MIInfo>(getExecutor(), rm)); } else if (coreType.equals( IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_TRACE_FILE)) { IGDBTraceControl traceControl = fTracker.getService(IGDBTraceControl.class); if (traceControl != null) { ITraceTargetDMContext targetDmc = DMContexts.getAncestorOfType( fCommandControl.getContext(), ITraceTargetDMContext.class); traceControl.loadTraceData(targetDmc, newCoreFile, rm); } else { rm.setStatus( new Status( IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Tracing not supported", null)); rm.done(); } } else { rm.setStatus( new Status( IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Invalid post-mortem type", null)); rm.done(); } } } }) .schedule(); } else { if (coreType.equals(IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_CORE_FILE)) { fCommandControl.queueCommand( fCommandFactory.createMITargetSelectCore(fCommandControl.getContext(), coreFile), new DataRequestMonitor<MIInfo>(getExecutor(), rm)); } else if (coreType.equals( IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_TRACE_FILE)) { IGDBTraceControl traceControl = fTracker.getService(IGDBTraceControl.class); if (traceControl != null) { ITraceTargetDMContext targetDmc = DMContexts.getAncestorOfType( fCommandControl.getContext(), ITraceTargetDMContext.class); traceControl.loadTraceData(targetDmc, coreFile, rm); } else { rm.setStatus( new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Tracing not supported", null)); rm.done(); } } else { rm.setStatus( new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Invalid post-mortem type", null)); rm.done(); } } } else { rm.done(); } }
/** Before running the program, we must create its console for IO. */ @Execute public void stepCreateConsole(final RequestMonitor rm) { Process inferiorProcess; if (fPty == null) { inferiorProcess = new MIInferiorProcess(fContainerDmc, fBackend.getMIOutputStream()); } else { inferiorProcess = new MIInferiorProcess(fContainerDmc, fPty); } final Process inferior = inferiorProcess; final ILaunch launch = (ILaunch) getContainerContext().getAdapter(ILaunch.class); // This is the groupId of the new process that will be started, even in the // case of a restart. final String groupId = ((IMIContainerDMContext) getContainerContext()).getGroupId(); // For multi-process, we cannot simply use the name given by the backend service // because we may not be starting that process, but another one. // Instead, we can look in the attributes for the binary name, which we stored // there for this case, specifically. // Bug 342351 IGDBBackend backend = fTracker.getService(IGDBBackend.class); String defaultPathName = backend.getProgramPath().lastSegment(); if (defaultPathName == null) { defaultPathName = ""; // $NON-NLS-1$ } String progPathName = CDebugUtils.getAttribute( fAttributes, ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, defaultPathName); final String pathLabel = new Path(progPathName).lastSegment(); // Add the inferior to the launch. // This cannot be done on the executor or things deadlock. DebugPlugin.getDefault() .asyncExec( new Runnable() { @Override public void run() { String label = pathLabel; if (fRestart) { // For a restart, remove the old inferior IProcess[] launchProcesses = launch.getProcesses(); for (IProcess process : launchProcesses) { if (process instanceof InferiorRuntimeProcess) { String groupAttribute = process.getAttribute(IGdbDebugConstants.INFERIOR_GROUPID_ATTR); // if the groupAttribute is not set in the process we know we are dealing // with single process debugging so the one process is the one we want. // If the groupAttribute is set, then we must make sure it is the proper // inferior if (groupAttribute == null || groupAttribute.equals(MIProcesses.UNIQUE_GROUP_ID) || groupAttribute.equals(groupId)) { launch.removeProcess(process); // Use the exact same label as before label = process.getLabel(); break; } } } } // Add the inferior InferiorRuntimeProcess runtimeInferior = new InferiorRuntimeProcess(launch, inferior, label, null); runtimeInferior.setAttribute(IGdbDebugConstants.INFERIOR_GROUPID_ATTR, groupId); launch.addProcess(runtimeInferior); rm.done(); } }); }
/** * This method processes "load info" requests. The load is computed using a sampling method; two * readings of a local or remote /proc/stat file are done with a delay in between. Then the load * is computed from the two samples, for all CPUs/cores known in the system. * * <p>Because of the method used, it's possible that fast variations in CPU usage will be missed. * However longer load trends should be reflected in the results. * * <p>To avoid generating too much load in the remote case, there is a cache that will return the * already computed load, if requested multiple times in a short period. There is also a mechanism * to queue subsequent requests if one is ongoing. Upon completion of the ongoing request, any * queued request is answered with the load that was just computed. * * @since 4.2 */ @Override public void getLoadInfo(final IDMContext context, final DataRequestMonitor<ILoadInfo> rm) { if (!(context instanceof ICoreDMContext) && !(context instanceof ICPUDMContext)) { // we only support getting the load for a CPU or a core rm.done( new Status( IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Load information not supported for this context type", null)); //$NON-NLS-1$ return; } // The measurement interval should be of a minimum length to be meaningful assert (LOAD_SAMPLE_DELAY >= 100); // so the cache is useful assert (LOAD_CACHE_LIFETIME >= LOAD_SAMPLE_DELAY); // This way of computing the CPU load is only applicable to Linux if (!supportsProcPseudoFS()) { rm.done( new Status( IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Operation not supported", null)); //$NON-NLS-1$ return; } // Is a request is already ongoing? if (fLoadRequestOngoing) { // queue current new request fLoadInfoRequestCache.put(context, rm); return; } // no request ongoing, so proceed fLoadRequestOngoing = true; // caching mechanism to keep things sane, even if the views(s) // request load information very often. long currentTime = System.currentTimeMillis(); // time to fetch fresh load information? if (fLastCpuLoadRefresh + LOAD_CACHE_LIFETIME < currentTime) { fLastCpuLoadRefresh = currentTime; } else { // not time yet... re-use cached load data processLoads(context, rm, fCachedLoads); fLoadRequestOngoing = false; return; } final ProcStatParser procStatParser = new ProcStatParser(); final ICommandControlDMContext dmc = DMContexts.getAncestorOfType(context, ICommandControlDMContext.class); final String statFile = "/proc/stat"; // $NON-NLS-1$ final String localFile = sTempFolder + "proc.stat." + getSession().getId(); // $NON-NLS-1$ // Remote debugging? We will ask GDB to get us the /proc/stat file from target, twice, with a // delay between. if (fBackend.getSessionType() == SessionType.REMOTE) { fCommandControl.queueCommand( fCommandFactory.createCLIRemoteGet(dmc, statFile, localFile), new ImmediateDataRequestMonitor<MIInfo>(rm) { @Override protected void handleCompleted() { if (!isSuccess()) { fLoadRequestOngoing = false; rm.done( new Status( IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Can't get load info for CPU", null)); //$NON-NLS-1$ return; } // Success - parse first set of stat counters try { procStatParser.parseStatFile(localFile); } catch (Exception e) { rm.done( new Status( IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Can't get load info for CPU", null)); //$NON-NLS-1$ fLoadRequestOngoing = false; return; } // delete temp file new File(localFile).delete(); getExecutor() .schedule( new Runnable() { @Override public void run() { fCommandControl.queueCommand( fCommandFactory.createCLIRemoteGet(dmc, statFile, localFile), new ImmediateDataRequestMonitor<MIInfo>(rm) { @Override protected void handleCompleted() { if (!isSuccess()) { fLoadRequestOngoing = false; rm.done( new Status( IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Can't get load info for CPU", null)); //$NON-NLS-1$ return; } // Success - parse the second set of stat counters and compute // loads try { procStatParser.parseStatFile(localFile); } catch (Exception e) { rm.done( new Status( IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Can't get load info for CPU", null)); //$NON-NLS-1$ fLoadRequestOngoing = false; return; } // delete temp file new File(localFile).delete(); // Compute load fCachedLoads = procStatParser.getCpuLoad(); processLoads(context, rm, fCachedLoads); // done with request fLoadRequestOngoing = false; // process any queued request for (Entry<IDMContext, DataRequestMonitor<ILoadInfo>> e : fLoadInfoRequestCache.entrySet()) { processLoads(e.getKey(), e.getValue(), fCachedLoads); } fLoadInfoRequestCache.clear(); } }); } }, LOAD_SAMPLE_DELAY, TimeUnit.MILLISECONDS); } }); // Local debugging? Then we can read /proc/stat directly } else { // Read /proc/stat file for the first time try { procStatParser.parseStatFile(statFile); } catch (Exception e) { rm.done( new Status( IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Can't get load info for CPU", null)); //$NON-NLS-1$ fLoadRequestOngoing = false; return; } // Read /proc/stat file again after a delay getExecutor() .schedule( new Runnable() { @Override public void run() { try { procStatParser.parseStatFile(statFile); } catch (Exception e) { rm.done( new Status( IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Can't get load info for CPU", null)); //$NON-NLS-1$ fLoadRequestOngoing = false; return; } // compute load fCachedLoads = procStatParser.getCpuLoad(); processLoads(context, rm, fCachedLoads); // done with request fLoadRequestOngoing = false; // process any queued request for (Entry<IDMContext, DataRequestMonitor<ILoadInfo>> e : fLoadInfoRequestCache.entrySet()) { processLoads(e.getKey(), e.getValue(), fCachedLoads); } fLoadInfoRequestCache.clear(); } }, LOAD_SAMPLE_DELAY, TimeUnit.MILLISECONDS); } }