Skip to main content
aboutsummaryrefslogblamecommitdiffstats
blob: 885b8b18fd7ce4446b91d9ab3c4afbb980a8ea65 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                                                                                
                                                                




                                                                        


                                                                                 
                                                
 
                               
                           
                           


                            
                         
                            
                      
                              
                     
                     
 
                                              




                                                   
                                                   
                                                           
                                                                  
                                                                              




                                            
                                               

                                                                   
                                           
                                                         
                                              
                                            
                                              
                                                             
                                                
                                               
                                            
                                       
 

                                       
                                     
 
                                                
 
                                                  


                                                     
                                                                                                     



                                                                   

     








                                                                    






















                                                                  
                                                                                                
                                                    

                                                                                                  


                             


                                                    
                                  

                                 
                                        



                                                                          
 

                             

                                        
                                   
                                                           
                                         
                                           
                                   
                                  
                                                                                    
 

                                                                                                                           
                                                                                              

                                                                                     
                                                                                 
 

                                                

                                       

                                                   
                                                                                              
 
                                                                                      
                                                  

                                                
             
         

                                                                    
         
      
 


                                                                                                  



                                                     

         
 




                                                                                         

                                                                             
                                                                            

     
                                                 
                                                                           
 

                                                                  
                                  
                                                     
                                  
                             
                                                   
                                                    




                     


                                                          
                              
                         
                                               
                                                                     


                 






                                                                












                                                                                                          

                     
             
 
                                                         




                                                                             


                                                                                      
                                      
                                 
                                                       
                                                       
                                                               


                                                                                    


                                                                                                                     










                                                                                         


                         
                                    
                                  
                             
                                                   

                                                                                           


                     





                                            
                 









                                                     
                                                                                          
                                  
                 
              

         
                                           
     
 







                                                           
                                                                       
                                                            






                                                                      
 
                                                     


                   
                                                                                                           




                                                                           

                                                                                                                                 

















                                                                                            
                                                                  





                                                                                 

                                                                         
                                                                     
                                                     
                                                                              
















                                                                                                                    






                                                                           

                                                                                                                                 


                                                                                           












                                                                           
                                                                                            









                                                                                              
                                                                              

                                                                                                 
                                                                                    
                                                             

                                                             
                                                                                     
                                                            






















                                                                                         



                                               

                                                                  




                                                                          
                                                                          




                                                                                         






                                                                
                                                                          

                                                                                         
                                                                            
                                                     





                                             

                                 
                                                                                          
                                                        










                                                                       
                                                                                          

                                                                         






                                                                                                   







                                                                                                                          





























                                                                  
 
                                                                                                     













                                                                                                    
 



                                                               
 










                                                                                                    
 








                                                                                                       
 












                                                                                                
 




                                                                      
 















                                                                                            
 













                                                                                                                     




































                                                                                                                       























                                                                                                                   
                                                                                                                                 

                                                                                                           










                                                                                                                         
                                                                            










                                                                                                                

                                                                       





                                                                 

                                                                

                                                                                      





                                                                                                      
                                                                                                            




                                                                                                         

                 

                                                         


                                  




                                                                                                                               
                             














                                                                                                       




                                                                                

















                                                                                                                         
                         

                      
             


         
                                          
                                             




                                                                                             
                                                                                            
                                    
                                             











                                         
 



                                                               
                                                                                                          


                                                                                                  
                                                                                                                                 
                 
                                                      
                                                                                                                    
                 
                                    
                                                                                                                             
                 
                                           
                                         
                 


                                                   






                                       
 
                                              
                                          
                              
                                                              







                                                                       
 

                                                             
     
 




                                                                                                    
 
                                       
                  






                                                                               
         

                      
 



                                                        




                                                                             



                                                      
                                                             

                                           
                               

     
                                                                

                                           
                               

     
                                                                            

                                                          





                                                                                 
                                                          










                                                                                                                                              
                     


                                             

                     


                                                                          

     
                             
                                  

                       
 


                                                          
 
                                                                                              
                                           



                                                                                                  
                                                      


                                                                                            
                                                                  

                                                       
                                                                                                                   
                                     


             
 


                                   
 
                                        









                                                                                        
     
 



                                                                

                                  
                                                       









                                                                        
                                               


                                                                           







                                                                
     





                                           




                                        




                                                            

                                    

     

                                     
     
 
                                                    
             
                                    
                                   
                                   
                               
                 
                    
         



                                                                                

                                  
         
     
 
                                   
                     

     

                                   

     
                                                   
     
 


                                   
 


                                 
 



                                                           




                                                    




                                                                                              
                                                   

                                           

                                                                                         
                                     


                                        
                                             

                          
                                                         
                                         
             
                                                        

                                                                               
                                       
                                         
                                                                        
 
                                               
                         
                                                        




                                             
                 
 

                                                        
 



                                                              
 
               
                                                                



                              
         
     
 

                                                                                                                      






                                                   





                                                                  
                                                      
                                               
                                                                                  
                               
                                                             



                                                                         
                                                           



                                                

                                                                                          



                                                        





                                                                                          

























                                                                                



                                                              



                                                          





                                                                    
                                                    
                                           


                                                                                           








                                                                 
                        
     
 





                                                                                          
                                                                  
                                           
                                                                                       

     




                                                                     

                                                       

                                                
                                  
                                                                                 
                        
     
 



                                                            
                                                 
                                           
                                        
                                             
     
 




                                                
                                                  
                                           





                                                                  




                                                                                            



                                                       



                                                                               

                                                          
     



                                           
 
/*******************************************************************************
 * Copyright (c) 2007, 2011 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *******************************************************************************/
package org.eclipse.tm.internal.tcf.debug.model;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.Launch;
import org.eclipse.tm.internal.tcf.debug.Activator;
import org.eclipse.tm.internal.tcf.debug.actions.TCFAction;
import org.eclipse.tm.internal.tcf.debug.launch.TCFLaunchDelegate;
import org.eclipse.tm.internal.tcf.debug.launch.TCFLaunchDelegate.PathMapRule;
import org.eclipse.tm.tcf.protocol.IChannel;
import org.eclipse.tm.tcf.protocol.IPeer;
import org.eclipse.tm.tcf.protocol.IService;
import org.eclipse.tm.tcf.protocol.IToken;
import org.eclipse.tm.tcf.protocol.Protocol;
import org.eclipse.tm.tcf.services.IFileSystem;
import org.eclipse.tm.tcf.services.IFileSystem.FileSystemException;
import org.eclipse.tm.tcf.services.IFileSystem.IFileHandle;
import org.eclipse.tm.tcf.services.IMemory;
import org.eclipse.tm.tcf.services.IMemory.MemoryContext;
import org.eclipse.tm.tcf.services.IMemoryMap;
import org.eclipse.tm.tcf.services.IPathMap;
import org.eclipse.tm.tcf.services.IProcesses;
import org.eclipse.tm.tcf.services.IProcesses.ProcessContext;
import org.eclipse.tm.tcf.services.IProcessesV1;
import org.eclipse.tm.tcf.services.IRunControl;
import org.eclipse.tm.tcf.services.IStreams;
import org.eclipse.tm.tcf.util.TCFTask;

public class TCFLaunch extends Launch {

    public interface LaunchListener {

        public void onCreated(TCFLaunch launch);

        public void onConnected(TCFLaunch launch);

        public void onDisconnected(TCFLaunch launch);

        public void onProcessOutput(TCFLaunch launch, String process_id, int stream_id, byte[] data);

        public void onProcessStreamError(
                TCFLaunch launch, String process_id, int stream_id,
                Exception error, int lost_size);
    }

    public interface ActionsListener {

        public void onContextActionStart(TCFAction action);

        public void onContextActionResult(String id, String result);

        public void onContextActionDone(TCFAction action);
    }

    private abstract class LaunchStep implements Runnable {

        LaunchStep() {
            launch_steps.add(this);
        }

        abstract void start() throws Exception;

        void done() {
            if (channel.getState() != IChannel.STATE_OPEN) return;
            try {
                launch_steps.removeFirst().start();
            }
            catch (Throwable x) {
                channel.terminate(x);
            }
        }

        public void run() {
            done();
        }
    }

    private static final Collection<LaunchListener> listeners = new ArrayList<LaunchListener>();
    private static LaunchListener[] listeners_array;

    private final Collection<ActionsListener> action_listeners = new ArrayList<ActionsListener>();

    private IChannel channel;
    private Throwable error;
    private TCFBreakpointsStatus breakpoints_status;
    private String mode;
    private boolean connecting;
    private boolean disconnecting;
    private boolean disconnected;
    private boolean shutdown;
    private boolean last_context_exited;
    private long actions_interval;

    private final HashSet<Object> pending_clients = new HashSet<Object>();
    private long pending_clients_timestamp;

    private String peer_name;

    private Runnable update_memory_maps;

    private ProcessContext process;
    private Collection<Map<String,Object>> process_signals;
    private IToken process_start_command;
    private String process_input_stream_id;
    private boolean process_exited;
    private int process_exit_code;
    private final HashMap<String,String> process_env = new HashMap<String,String>();

    private final HashMap<String,TCFAction> active_actions = new HashMap<String,TCFAction>();
    private final HashMap<String,LinkedList<TCFAction>> context_action_queue = new HashMap<String,LinkedList<TCFAction>>();
    private final HashMap<String,Long> context_action_timestamps = new HashMap<String,Long>();
    private final HashMap<String,String> stream_ids = new HashMap<String,String>();
    private final LinkedList<LaunchStep> launch_steps = new LinkedList<LaunchStep>();
    private final LinkedList<String> redirection_path = new LinkedList<String>();

    private ArrayList<PathMapRule> filepath_map;

    private Set<String> context_filter;

    private boolean supports_memory_map_preloading;

    private final IStreams.StreamsListener streams_listener = new IStreams.StreamsListener() {

        public void created(String stream_type, String stream_id, String context_id) {
            stream_ids.put(stream_id, context_id);
            if (process_start_command == null) {
                disconnectStream(stream_id);
            }
        }

        public void disposed(String stream_type, String stream_id) {
        }
    };

    private final IProcesses.ProcessesListener prs_listener = new IProcesses.ProcessesListener() {

        public void exited(String process_id, int exit_code) {
            if (process_id.equals(process.getID())) {
                process_exit_code = exit_code;
                process_exited = true;
            }
        }
    };

    private static LaunchListener[] getListeners() {
        if (listeners_array != null) return listeners_array;
        return listeners_array = listeners.toArray(new LaunchListener[listeners.size()]);
    }

    public TCFLaunch(ILaunchConfiguration launchConfiguration, String mode) {
        super(launchConfiguration, mode, null);
        for (LaunchListener l : getListeners()) l.onCreated(TCFLaunch.this);
    }

    private void onConnected() throws Exception {
        // The method is called when TCF channel is successfully connected.

        final ILaunchConfiguration cfg = getLaunchConfiguration();
        if (cfg != null) {
            // Send file path map:
            if (getService(IPathMap.class) != null) {
                new LaunchStep() {
                    @Override
                    void start() throws Exception {
                        downloadPathMaps(cfg, this);
                    }
                };
            }
        }

        if (redirection_path.size() > 0) {
            // Connected to intermediate peer (value-add).
            // Redirect to next peer:
            new LaunchStep() {
                @Override
                void start() throws Exception {
                    channel.redirect(redirection_path.removeFirst());
                }
            };
        }
        else {
            final IStreams streams = getService(IStreams.class);
            if (streams != null) {
                // Subscribe Streams service:
                new LaunchStep() {
                    @Override
                    void start() {
                        final Set<IToken> cmds = new HashSet<IToken>();
                        String[] nms = { IProcesses.NAME, IProcessesV1.NAME };
                        for (String s : nms) {
                            if (channel.getRemoteService(s) == null) continue;
                            cmds.add(streams.subscribe(s, streams_listener, new IStreams.DoneSubscribe() {
                                public void doneSubscribe(IToken token, Exception error) {
                                    cmds.remove(token);
                                    if (error != null) channel.terminate(error);
                                    if (cmds.size() == 0) done();
                                }
                            }));
                        }
                        if (cmds.size() == 0) done();
                    }
                };
            }

            if (mode.equals(ILaunchManager.DEBUG_MODE)) {
                String attach_to_context = getAttribute("attach_to_context");
                if (attach_to_context != null) {
                    context_filter = new HashSet<String>();
                    context_filter.add(attach_to_context);
                }
                final IMemoryMap mem_map = channel.getRemoteService(IMemoryMap.class);
                if (mem_map != null) {
                    // Send manual memory map items:
                    new LaunchStep() {
                        @Override
                        void start() throws Exception {
                            final Runnable done = this;
                            // Check if preloading is supported
                            mem_map.set("\001", null, new IMemoryMap.DoneSet() {
                                public void doneSet(IToken token, Exception error) {
                                    try {
                                        supports_memory_map_preloading = error == null;
                                        if (!supports_memory_map_preloading) {
                                            // Older agents (up to ver. 0.4) don't support preloading of memory maps.
                                            updateMemoryMapsOnProcessCreation(cfg, done);
                                        }
                                        else {
                                            downloadMemoryMaps(cfg, done);
                                        }
                                    }
                                    catch (Exception x) {
                                        channel.terminate(x);
                                    }
                                }
                            });
                        }
                    };
                }
                // Send breakpoints:
                new LaunchStep() {
                    @Override
                    void start() throws Exception {
                        breakpoints_status = new TCFBreakpointsStatus(TCFLaunch.this);
                        Activator.getBreakpointsModel().downloadBreakpoints(channel, this);
                    }
                };
            }

            // Call client launch sequence:
            new LaunchStep() {
                @Override
                void start() {
                    runLaunchSequence(this);
                }
            };

            if (cfg != null) startRemoteProcess(cfg);

            // Final launch step.
            // Notify clients:
            new LaunchStep() {
                @Override
                void start() {
                    connecting = false;
                    for (LaunchListener l : getListeners()) l.onConnected(TCFLaunch.this);
                    fireChanged();
                }
            };
        }

        launch_steps.removeFirst().start();
    }

    private void onDisconnected(Throwable error) {
        // The method is called when TCF channel is closed.
        assert !disconnected;
        assert !shutdown;
        this.error = error;
        breakpoints_status = null;
        connecting = false;
        disconnected = true;
        for (LaunchListener l : getListeners()) l.onDisconnected(this);
        if (DebugPlugin.getDefault() != null) fireChanged();
        runShutdownSequence(new Runnable() {
            public void run() {
                shutdown = true;
                if (DebugPlugin.getDefault() != null) fireTerminate();
            }
        });
    }

    protected void runLaunchSequence(Runnable done) {
        done.run();
    }

    private void downloadMemoryMaps(final ILaunchConfiguration cfg, final Runnable done) throws Exception {
        final IMemoryMap mmap = channel.getRemoteService(IMemoryMap.class);
        if (mmap == null) {
            done.run();
            return;
        }
        final HashMap<String,ArrayList<IMemoryMap.MemoryRegion>> maps = new HashMap<String,ArrayList<IMemoryMap.MemoryRegion>>();
        TCFLaunchDelegate.getMemMapsAttribute(maps, cfg);
        final HashSet<IToken> cmds = new HashSet<IToken>(); // Pending commands
        final Runnable done_all = new Runnable() {
            boolean launch_done;
            public void run() {
                if (launch_done) return;
                done.run();
                launch_done = true;
            }
        };
        final IMemoryMap.DoneSet done_set_mmap = new IMemoryMap.DoneSet() {
            public void doneSet(IToken token, Exception error) {
                assert cmds.contains(token);
                cmds.remove(token);
                if (error != null) Activator.log("Cannot update context memory map", error);
                if (cmds.isEmpty()) done_all.run();
            }
        };
        for (String id : maps.keySet()) {
            ArrayList<IMemoryMap.MemoryRegion> map = maps.get(id);
            TCFMemoryRegion[] arr = map.toArray(new TCFMemoryRegion[map.size()]);
            cmds.add(mmap.set(id, arr, done_set_mmap));
        }
        update_memory_maps = new Runnable() {
            public void run() {
                try {
                    Set<String> set = new HashSet<String>(maps.keySet());
                    maps.clear();
                    TCFLaunchDelegate.getMemMapsAttribute(maps, cfg);
                    for (String id : maps.keySet()) {
                        ArrayList<IMemoryMap.MemoryRegion> map = maps.get(id);
                        TCFMemoryRegion[] arr = map.toArray(new TCFMemoryRegion[map.size()]);
                        cmds.add(mmap.set(id, arr, done_set_mmap));
                    }
                    for (String id : set) {
                        if (maps.get(id) != null) continue;
                        cmds.add(mmap.set(id, null, done_set_mmap));
                    }
                }
                catch (Throwable x) {
                    channel.terminate(x);
                }
            }
        };
        if (cmds.isEmpty()) done_all.run();
    }

    private void updateMemoryMapsOnProcessCreation(ILaunchConfiguration cfg, final Runnable done) throws Exception {
        final IMemory mem = channel.getRemoteService(IMemory.class);
        final IMemoryMap mmap = channel.getRemoteService(IMemoryMap.class);
        if (mem == null || mmap == null) {
            done.run();
            return;
        }
        final HashSet<String> deleted_maps = new HashSet<String>();
        final HashMap<String,ArrayList<IMemoryMap.MemoryRegion>> maps = new HashMap<String,ArrayList<IMemoryMap.MemoryRegion>>();
        TCFLaunchDelegate.getMemMapsAttribute(maps, cfg);
        final HashSet<String> mems = new HashSet<String>(); // Already processed memory IDs
        final HashSet<IToken> cmds = new HashSet<IToken>(); // Pending commands
        final HashMap<String,String> mem2map = new HashMap<String,String>();
        final Runnable done_all = new Runnable() {
            boolean launch_done;
            public void run() {
                mems.clear();
                deleted_maps.clear();
                if (launch_done) return;
                done.run();
                launch_done = true;
            }
        };
        final IMemoryMap.DoneSet done_set_mmap = new IMemoryMap.DoneSet() {
            public void doneSet(IToken token, Exception error) {
                cmds.remove(token);
                if (error != null) Activator.log("Cannot update context memory map", error);
                if (cmds.isEmpty()) done_all.run();
            }
        };
        final IMemory.DoneGetContext done_get_context = new IMemory.DoneGetContext() {
            public void doneGetContext(IToken token, Exception error, MemoryContext context) {
                cmds.remove(token);
                if (context != null && mems.add(context.getID())) {
                    String id = context.getName();
                    if (id == null) id = context.getID();
                    if (id != null) {
                        ArrayList<IMemoryMap.MemoryRegion> map = maps.get(id);
                        if (map != null) {
                            TCFMemoryRegion[] arr = map.toArray(new TCFMemoryRegion[map.size()]);
                            cmds.add(mmap.set(context.getID(), arr, done_set_mmap));
                            mem2map.put(context.getID(), id);
                        }
                        else if (deleted_maps.contains(id)) {
                            cmds.add(mmap.set(context.getID(), null, done_set_mmap));
                            mem2map.remove(context.getID());
                        }
                    }
                }
                if (cmds.isEmpty()) done_all.run();
            }
        };
        final IMemory.DoneGetChildren done_get_children = new IMemory.DoneGetChildren() {
            public void doneGetChildren(IToken token, Exception error, String[] ids) {
                cmds.remove(token);
                if (ids != null) {
                    for (String id : ids) {
                        cmds.add(mem.getChildren(id, this));
                        cmds.add(mem.getContext(id, done_get_context));
                    }
                }
                if (cmds.isEmpty()) done_all.run();
            }
        };
        cmds.add(mem.getChildren(null, done_get_children));
        mem.addListener(new IMemory.MemoryListener() {
            public void memoryChanged(String context_id, Number[] addr, long[] size) {
            }
            public void contextRemoved(String[] context_ids) {
                for (String id : context_ids) {
                    mems.remove(id);
                    mem2map.remove(id);
                }
            }
            public void contextChanged(MemoryContext[] contexts) {
                for (MemoryContext context : contexts) {
                    String id = context.getName();
                    if (id == null) id = context.getID();
                    if (id == null) continue;
                    if (id.equals(mem2map.get(context.getID()))) continue;
                    ArrayList<IMemoryMap.MemoryRegion> map = maps.get(id);
                    if (map == null) continue;
                    TCFMemoryRegion[] arr = map.toArray(new TCFMemoryRegion[map.size()]);
                    cmds.add(mmap.set(context.getID(), arr, done_set_mmap));
                    mem2map.put(context.getID(), id);
                }
            }
            public void contextAdded(MemoryContext[] contexts) {
                for (MemoryContext context : contexts) {
                    if (!mems.add(context.getID())) continue;
                    String id = context.getName();
                    if (id == null) id = context.getID();
                    if (id == null) continue;
                    ArrayList<IMemoryMap.MemoryRegion> map = maps.get(id);
                    if (map == null) continue;
                    TCFMemoryRegion[] arr = map.toArray(new TCFMemoryRegion[map.size()]);
                    cmds.add(mmap.set(context.getID(), arr, done_set_mmap));
                    mem2map.put(context.getID(), id);
                }
            }
        });
        update_memory_maps = new Runnable() {
            public void run() {
                try {
                    maps.clear();
                    mems.clear();
                    TCFLaunchDelegate.getMemMapsAttribute(maps, getLaunchConfiguration());
                    for (String id : mem2map.values()) {
                        if (maps.get(id) == null) deleted_maps.add(id);
                    }
                    cmds.add(mem.getChildren(null, done_get_children));
                }
                catch (Throwable x) {
                    channel.terminate(x);
                }
            }
        };
    }

    private void readPathMapConfiguration(ILaunchConfiguration cfg) throws CoreException {
        String s = cfg.getAttribute(TCFLaunchDelegate.ATTR_PATH_MAP, "");
        filepath_map = TCFLaunchDelegate.parsePathMapAttribute(s);
        s = cfg.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO, "");
        filepath_map.addAll(TCFLaunchDelegate.parseSourceLocatorMemento(s));
    }

    private void downloadPathMaps(ILaunchConfiguration cfg, final Runnable done) throws Exception {
        readPathMapConfiguration(cfg);
        final IPathMap path_map_service = getService(IPathMap.class);
        path_map_service.set(filepath_map.toArray(new IPathMap.PathMapRule[filepath_map.size()]), new IPathMap.DoneSet() {
            public void doneSet(IToken token, Exception error) {
                if (error != null) channel.terminate(error);
                else done.run();
            }
        });
    }

    private String[] toArgsArray(String file, String cmd) {
        // Create arguments list from a command line.
        int i = 0;
        int l = cmd.length();
        List<String> arr = new ArrayList<String>();
        arr.add(file);
        for (;;) {
            while (i < l && cmd.charAt(i) == ' ') i++;
            if (i >= l) break;
            String s = null;
            if (cmd.charAt(i) == '"') {
                i++;
                StringBuffer bf = new StringBuffer();
                while (i < l) {
                    char ch = cmd.charAt(i++);
                    if (ch == '"') break;
                    if (ch == '\\' && i < l) ch = cmd.charAt(i++);
                    bf.append(ch);
                }
                s = bf.toString();
            }
            else {
                int i0 = i;
                while (i < l && cmd.charAt(i) != ' ') i++;
                s = cmd.substring(i0, i);
            }
            arr.add(s);
        }
        return arr.toArray(new String[arr.size()]);
    }

    private void copyFileToRemoteTarget(String local_file, String remote_file, final Runnable done) {
        if (local_file == null) {
            channel.terminate(new Exception("Program does not exist"));
            return;
        }
        final IFileSystem fs = channel.getRemoteService(IFileSystem.class);
        if (fs == null) {
            channel.terminate(new Exception(
                    "Cannot download program file: target does not provide File System service"));
            return;
        }
        try {
            final InputStream inp = new FileInputStream(local_file);
            int flags = IFileSystem.TCF_O_WRITE | IFileSystem.TCF_O_CREAT | IFileSystem.TCF_O_TRUNC;
            fs.open(remote_file, flags, null, new IFileSystem.DoneOpen() {

                IFileHandle handle;
                long offset = 0;
                final Set<IToken> cmds = new HashSet<IToken>();
                final byte[] buf = new byte[0x1000];

                public void doneOpen(IToken token, FileSystemException error, IFileHandle handle) {
                    this.handle = handle;
                    if (error != null) {
                        TCFLaunch.this.error = new Exception("Cannot download program file", error);
                        fireChanged();
                        done.run();
                    }
                    else {
                        write_next();
                    }
                }

                private void write_next() {
                    try {
                        while (cmds.size() < 8) {
                            int rd = inp.read(buf);
                            if (rd < 0) {
                                close();
                                break;
                            }
                            cmds.add(fs.write(handle, offset, buf, 0, rd, new IFileSystem.DoneWrite() {

                                public void doneWrite(IToken token, FileSystemException error) {
                                    cmds.remove(token);
                                    if (error != null) channel.terminate(error);
                                    else write_next();
                                }
                            }));
                            offset += rd;
                        }
                    }
                    catch (Throwable x) {
                        channel.terminate(x);
                    }
                }

                private void close() {
                    if (cmds.size() > 0) return;
                    try {
                        inp.close();
                        fs.close(handle, new IFileSystem.DoneClose() {

                            public void doneClose(IToken token, FileSystemException error) {
                                if (error != null) channel.terminate(error);
                                else done.run();
                            }
                        });
                    }
                    catch (Throwable x) {
                        channel.terminate(x);
                    }
                }
            });
        }
        catch (Throwable x) {
            channel.terminate(x);
        }
    }

    @SuppressWarnings("unchecked")
    private void startRemoteProcess(final ILaunchConfiguration cfg) throws Exception {
        final String project = cfg.getAttribute(TCFLaunchDelegate.ATTR_PROJECT_NAME, "");
        final String local_file = cfg.getAttribute(TCFLaunchDelegate.ATTR_LOCAL_PROGRAM_FILE, "");
        final String remote_file = cfg.getAttribute(TCFLaunchDelegate.ATTR_REMOTE_PROGRAM_FILE, "");
        if (local_file.length() != 0 && remote_file.length() != 0) {
            // Download executable file
            new LaunchStep() {
                @Override
                void start() throws Exception {
                    copyFileToRemoteTarget(TCFLaunchDelegate.getProgramPath(project, local_file), remote_file, this);
                }
            };
        }
        final String attach_to_process = getAttribute("attach_to_process");
        if (attach_to_process != null) {
            final IProcesses ps = channel.getRemoteService(IProcesses.class);
            if (ps == null) throw new Exception("Target does not provide Processes service");
            // Attach the process
            new LaunchStep() {
                @Override
                void start() {
                    IProcesses.DoneGetContext done = new IProcesses.DoneGetContext() {
                        public void doneGetContext(IToken token, final Exception error, final ProcessContext process) {
                            if (error != null) {
                                channel.terminate(error);
                            }
                            else {
                                process.attach(new IProcesses.DoneCommand() {
                                    public void doneCommand(IToken token, final Exception error) {
                                        if (error != null) {
                                            channel.terminate(error);
                                        }
                                        else {
                                            context_filter = new HashSet<String>();
                                            context_filter.add(process.getID());
                                            TCFLaunch.this.process = process;
                                            ps.addListener(prs_listener);
                                            connectProcessStreams();
                                            done();
                                        }
                                    }
                                });
                            }
                        }
                    };
                    ps.getContext(attach_to_process, done);
                }
            };
        }
        else if (local_file.length() != 0 || remote_file.length() != 0) {
            final IProcesses ps = channel.getRemoteService(IProcesses.class);
            if (ps == null) throw new Exception("Target does not provide Processes service");
            final boolean append = cfg.getAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true);
            if (append) {
                // Get system environment variables
                new LaunchStep() {
                    @Override
                    void start() throws Exception {
                        ps.getEnvironment(new IProcesses.DoneGetEnvironment() {
                            public void doneGetEnvironment(IToken token, Exception error, Map<String,String> env) {
                                if (error != null) {
                                    channel.terminate(error);
                                }
                                else {
                                    if (env != null) process_env.putAll(env);
                                    done();
                                }
                            }
                        });
                    }
                };
            }
            final String dir = cfg.getAttribute(TCFLaunchDelegate.ATTR_WORKING_DIRECTORY, "");
            final String args = cfg.getAttribute(TCFLaunchDelegate.ATTR_PROGRAM_ARGUMENTS, "");
            final Map<String,String> env = cfg.getAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, (Map<String,String>)null);
            final boolean attach_children = cfg.getAttribute(TCFLaunchDelegate.ATTR_ATTACH_CHILDREN, true);
            final boolean use_terminal = cfg.getAttribute(TCFLaunchDelegate.ATTR_USE_TERMINAL, true);
            // Start the process
            new LaunchStep() {
                @Override
                void start() {
                    if (env != null) process_env.putAll(env);
                    String file = remote_file;
                    if (file == null || file.length() == 0) file = TCFLaunchDelegate.getProgramPath(project, local_file);
                    if (file == null || file.length() == 0) {
                        channel.terminate(new Exception("Program file does not exist"));
                        return;
                    }
                    IProcesses.DoneStart done = new IProcesses.DoneStart() {
                        public void doneStart(IToken token, final Exception error, ProcessContext process) {
                            process_start_command = null;
                            if (error != null) {
                                for (String id : new HashSet<String>(stream_ids.keySet())) disconnectStream(id);
                                Protocol.sync(new Runnable() {
                                    public void run() {
                                        channel.terminate(error);
                                    }
                                });
                            }
                            else {
                                context_filter = new HashSet<String>();
                                context_filter.add(process.getID());
                                TCFLaunch.this.process = process;
                                ps.addListener(prs_listener);
                                connectProcessStreams();
                                done();
                            }
                        }
                    };
                    String[] args_arr = toArgsArray(file, args);
                    IProcessesV1 ps_v1 = channel.getRemoteService(IProcessesV1.class);
                    if (ps_v1 != null) {
                        Map<String,Object> params = new HashMap<String,Object>();
                        if (mode.equals(ILaunchManager.DEBUG_MODE)) {
                            params.put(IProcessesV1.START_ATTACH, true);
                            if (attach_children) params.put(IProcessesV1.START_ATTACH_CHILDREN, true);
                        }
                        if (use_terminal) params.put(IProcessesV1.START_USE_TERMINAL, true);
                        process_start_command = ps_v1.start(dir, file, args_arr, process_env, params, done);
                    }
                    else {
                        boolean attach = mode.equals(ILaunchManager.DEBUG_MODE);
                        process_start_command = ps.start(dir, file, args_arr, process_env, attach, done);
                    }
                }
            };
            if (mode.equals(ILaunchManager.DEBUG_MODE)) {
                // Get process signal list
                new LaunchStep() {
                    @Override
                    void start() {
                        ps.getSignalList(process.getID(), new IProcesses.DoneGetSignalList() {
                            public void doneGetSignalList(IToken token, Exception error, Collection<Map<String,Object>> list) {
                                if (error != null) Activator.log("Can't get process signal list", error);
                                process_signals = list;
                                done();
                            }
                        });
                    }
                };
                // Set process signal masks
                String dont_stop = cfg.getAttribute(TCFLaunchDelegate.ATTR_SIGNALS_DONT_STOP, "");
                String dont_pass = cfg.getAttribute(TCFLaunchDelegate.ATTR_SIGNALS_DONT_PASS, "");
                final int no_stop = dont_stop.length() > 0 ? Integer.parseInt(dont_stop, 16) : 0;
                final int no_pass = dont_pass.length() > 0 ? Integer.parseInt(dont_pass, 16) : 0;
                if (no_stop != 0 || no_pass != 0) {
                    new LaunchStep() {
                        @Override
                        void start() {
                            final HashSet<IToken> cmds = new HashSet<IToken>();
                            final IProcesses.DoneCommand done_set_mask = new IProcesses.DoneCommand() {
                                public void doneCommand(IToken token, Exception error) {
                                    cmds.remove(token);
                                    if (error != null) channel.terminate(error);
                                    else if (cmds.size() == 0) done();
                                }
                            };
                            cmds.add(ps.setSignalMask(process.getID(), no_stop, no_pass, done_set_mask));
                            final IRunControl rc = channel.getRemoteService(IRunControl.class);
                            if (rc != null) {
                                final IRunControl.DoneGetChildren done_get_children = new IRunControl.DoneGetChildren() {
                                    public void doneGetChildren(IToken token, Exception error, String[] context_ids) {
                                        if (context_ids != null) {
                                            for (String id : context_ids) {
                                                cmds.add(ps.setSignalMask(id, no_stop, no_pass, done_set_mask));
                                                cmds.add(rc.getChildren(id, this));
                                            }
                                        }
                                        cmds.remove(token);
                                        if (error != null) channel.terminate(error);
                                        else if (cmds.size() == 0) done();
                                    }
                                };
                                cmds.add(rc.getChildren(process.getID(), done_get_children));
                            }
                        }
                    };
                }
            }
        }
    }

    private void connectProcessStreams() {
        assert process_start_command == null;
        final IStreams streams = getService(IStreams.class);
        if (streams == null) return;
        final String inp_id = (String)process.getProperties().get(IProcesses.PROP_STDIN_ID);
        final String out_id = (String)process.getProperties().get(IProcesses.PROP_STDOUT_ID);
        final String err_id = (String)process.getProperties().get(IProcesses.PROP_STDERR_ID);
        for (final String id : stream_ids.keySet().toArray(new String[stream_ids.size()])) {
            if (id.equals(inp_id)) {
                process_input_stream_id = id;
            }
            else if (id.equals(out_id)) {
                connectStream(id, 0);
            }
            else if (id.equals(err_id)) {
                connectStream(id, 1);
            }
            else {
                disconnectStream(id);
            }
        }
    }

    private void connectStream(final String id, final int no) {
        final String peocess_id = process.getID();
        final IStreams streams = getService(IStreams.class);
        IStreams.DoneRead done = new IStreams.DoneRead() {
            public void doneRead(IToken token, Exception error, int lost_size, byte[] data, boolean eos) {
                if (stream_ids.get(id) == null) return;
                if (lost_size > 0) {
                    Exception x = new IOException("Process output data lost due buffer overflow");
                    for (LaunchListener l : getListeners()) l.onProcessStreamError(TCFLaunch.this, peocess_id, no, x, lost_size);
                }
                if (data != null && data.length > 0) {
                    for (LaunchListener l : getListeners()) l.onProcessOutput(TCFLaunch.this, peocess_id, no, data);
                }
                if (error != null) {
                    for (LaunchListener l : getListeners()) l.onProcessStreamError(TCFLaunch.this, peocess_id, no, error, 0);
                }
                if (eos || error != null) {
                    disconnectStream(id);
                }
                else {
                    streams.read(id, 0x1000, this);
                }
            }
        };
        streams.read(id, 0x1000, done);
        streams.read(id, 0x1000, done);
        streams.read(id, 0x1000, done);
        streams.read(id, 0x1000, done);
    }

    private void disconnectStream(String id) {
        assert stream_ids.get(id) != null;
        stream_ids.remove(id);
        if (channel.getState() != IChannel.STATE_OPEN) return;
        IStreams streams = getService(IStreams.class);
        streams.disconnect(id, new IStreams.DoneDisconnect() {
            public void doneDisconnect(IToken token, Exception error) {
                if (channel.getState() != IChannel.STATE_OPEN) return;
                if (error != null) channel.terminate(error);
            }
        });
    }

    protected void runShutdownSequence(final Runnable done) {
        done.run();
    }

    /*--------------------------------------------------------------------------------------------*/

    public Throwable getError() {
        return error;
    }

    public void setError(Throwable x) {
        error = x;
        if (x != null) {
            if (channel != null && channel.getState() == IChannel.STATE_OPEN) {
                channel.terminate(x);
            }
            else if (!connecting) {
                disconnected = true;
            }
        }
        fireChanged();
    }

    public TCFBreakpointsStatus getBreakpointsStatus() {
        return breakpoints_status;
    }

    /**
     * Check if the agent supports setting of user defined memory map entries
     * for a context that does not exits yet.
     * @return true if memory map preloading is supported.
     */
    public boolean isMemoryMapPreloadingSupported()  {
        return supports_memory_map_preloading;
    }

    public static void addListener(LaunchListener listener) {
        assert Protocol.isDispatchThread();
        listeners.add(listener);
        listeners_array = null;
    }

    public static void removeListener(LaunchListener listener) {
        assert Protocol.isDispatchThread();
        listeners.remove(listener);
        listeners_array = null;
    }

    public void launchConfigurationChanged(final ILaunchConfiguration cfg) {
        super.launchConfigurationChanged(cfg);
        if (!cfg.equals(getLaunchConfiguration())) return;
        if (channel != null && channel.getState() == IChannel.STATE_OPEN) {
            new TCFTask<Boolean>(channel) {
                public void run() {
                    try {
                        if (update_memory_maps != null) update_memory_maps.run();
                        if (filepath_map != null) {
                            readPathMapConfiguration(cfg);
                            final IPathMap path_map_service = getService(IPathMap.class);
                            path_map_service.set(filepath_map.toArray(new IPathMap.PathMapRule[filepath_map.size()]), new IPathMap.DoneSet() {
                                public void doneSet(IToken token, Exception error) {
                                    if (error != null) channel.terminate(error);
                                    done(false);
                                }
                            });
                        }
                        else {
                            done(true);
                        }
                    }
                    catch (Throwable x) {
                        channel.terminate(x);
                        done(false);
                    }
                }
            }.getE();
            // TODO: update signal masks when launch configuration changes
        }
    }

    /** Thread safe method */
    public IChannel getChannel() {
        return channel;
    }

    public IProcesses.ProcessContext getProcessContext() {
        return process;
    }

    public void writeProcessInputStream(byte[] buf, int pos, final int len) throws Exception {
        assert Protocol.isDispatchThread();
        final String id = process_input_stream_id;
        if (channel.getState() != IChannel.STATE_OPEN) throw new IOException("Connection closed");
        if (process == null) throw new IOException("No target process");
        final String prs = process.getID();
        IStreams streams = getService(IStreams.class);
        if (streams == null) throw new IOException("Streams service not available");
        if (stream_ids.get(id) == null) throw new IOException("Input stream not available");
        streams.write(id, buf, pos, len, new IStreams.DoneWrite() {
            public void doneWrite(IToken token, Exception error) {
                if (error == null) return;
                if (stream_ids.get(id) == null) return;
                for (LaunchListener l : getListeners()) l.onProcessStreamError(TCFLaunch.this, prs, 0, error, len);
                disconnectStream(id);
            }
        });
    }

    public boolean isConnecting() {
        return connecting;
    }

    public void onLastContextRemoved() {
        ILaunchConfiguration cfg = getLaunchConfiguration();
        try {
            if (cfg.getAttribute(TCFLaunchDelegate.ATTR_DISCONNECT_ON_CTX_EXIT, true)) {
                last_context_exited = true;
                closeChannel();
            }
        }
        catch (Throwable e) {
            Activator.log("Cannot access launch configuration", e);
        }
    }

    public void closeChannel() {
        assert Protocol.isDispatchThread();
        if (channel == null) return;
        if (channel.getState() == IChannel.STATE_CLOSED) return;
        if (disconnecting) return;
        disconnecting = true;
        final Set<IToken> cmds = new HashSet<IToken>();
        if (process != null && !process_exited) {
            cmds.add(process.terminate(new IProcesses.DoneCommand() {
                public void doneCommand(IToken token, Exception error) {
                    cmds.remove(token);
                    if (error != null) channel.terminate(error);
                    else if (cmds.isEmpty()) channel.close();
                }
            }));
        }
        IStreams streams = getService(IStreams.class);
        for (String id : stream_ids.keySet()) {
            cmds.add(streams.disconnect(id, new IStreams.DoneDisconnect() {
                public void doneDisconnect(IToken token, Exception error) {
                    cmds.remove(token);
                    if (error != null) channel.terminate(error);
                    else if (cmds.isEmpty()) channel.close();
                }
            }));
        }
        stream_ids.clear();
        process_input_stream_id = null;
        if (cmds.isEmpty()) channel.close();
    }

    public IPeer getPeer() {
        assert Protocol.isDispatchThread();
        return channel.getRemotePeer();
    }

    public String getPeerName() {
        // Safe to call from any thread.
        return peer_name;
    }

    public <V extends IService> V getService(Class<V> cls) {
        assert Protocol.isDispatchThread();
        return channel.getRemoteService(cls);
    }

    public boolean canDisconnect() {
        return !disconnected;
    }

    public boolean isDisconnected() {
        return disconnected;
    }

    public void disconnect() throws DebugException {
        try {
            new TCFTask<Boolean>() {
                public void run() {
                    closeChannel();
                    done(true);
                }
            }.get();
        }
        catch (IllegalStateException x) {
            // Don't report this exception - it means Eclipse is being shut down
            disconnected = true;
        }
        catch (Exception x) {
            throw new TCFError(x);
        }
    }

    public boolean canTerminate() {
        return false;
    }

    public boolean isTerminated() {
        return disconnected;
    }

    public void terminate() throws DebugException {
    }

    public boolean isExited() {
        return last_context_exited;
    }

    public int getExitCode() {
        return process_exit_code;
    }

    public Collection<Map<String,Object>> getSignalList() {
        return process_signals;
    }

    public ArrayList<PathMapRule> getFilePathMap() {
        assert Protocol.isDispatchThread();
        return filepath_map;
    }

    /**
     * Activate TCF launch: open communication channel and perform all necessary launch steps.
     * @param mode - on of launch mode constants defined in ILaunchManager.
     * @param id - TCF peer ID.
     */
    public void launchTCF(String mode, String id) {
        assert Protocol.isDispatchThread();
        this.mode = mode;
        try {
            if (id == null || id.length() == 0) throw new IOException("Invalid peer ID");
            redirection_path.clear();
            for (;;) {
                int i = id.indexOf('/');
                if (i <= 0) {
                    redirection_path.add(id);
                    break;
                }
                redirection_path.add(id.substring(0, i));
                id = id.substring(i + 1);
            }
            String id0 = redirection_path.removeFirst();
            IPeer peer = Protocol.getLocator().getPeers().get(id0);
            if (peer == null) throw new Exception("Cannot locate peer " + id0);
            peer_name = peer.getName();
            channel = peer.openChannel();
            channel.addChannelListener(new IChannel.IChannelListener() {

                public void onChannelOpened() {
                    try {
                        peer_name = getPeer().getName();
                        onConnected();
                    }
                    catch (Throwable x) {
                        channel.terminate(x);
                    }
                }

                public void congestionLevel(int level) {
                }

                public void onChannelClosed(Throwable error) {
                    channel.removeChannelListener(this);
                    onDisconnected(error);
                }

            });
            assert channel.getState() == IChannel.STATE_OPENING;
            connecting = true;
        }
        catch (Throwable e) {
            onDisconnected(e);
        }
    }

    /****************************************************************************************************************/

    private long getActionTimeStamp(String id) {
        Long l = context_action_timestamps.get(id);
        if (l == null) return 0;
        return l.longValue();
    }

    private void startAction(final String id) {
        if (active_actions.get(id) != null) return;
        LinkedList<TCFAction> list = context_action_queue.get(id);
        if (list == null || list.size() == 0) return;
        final TCFAction action = list.removeFirst();
        if (list.size() == 0) context_action_queue.remove(id);
        active_actions.put(id, action);
        final long timestamp = getActionTimeStamp(id);
        long time = System.currentTimeMillis();
        Protocol.invokeLater(timestamp + actions_interval - time, new Runnable() {
            public void run() {
                if (active_actions.get(id) != action) return;
                long time = System.currentTimeMillis();
                synchronized (pending_clients) {
                    if (pending_clients.size() > 0) {
                        if (time - timestamp < actions_interval + 1000) {
                            Protocol.invokeLater(20, this);
                            return;
                        }
                        pending_clients.clear();
                    }
                    else if (time < pending_clients_timestamp + 10) {
                        Protocol.invokeLater(pending_clients_timestamp + 10 - time, this);
                        return;
                    }
                }
                context_action_timestamps.put(id, time);
                for (ActionsListener l : action_listeners) l.onContextActionStart(action);
                action.run();
            }
        });
    }

    /**
     * Add an object to the set of pending clients.
     * Actions execution will be delayed until the set is empty,
     * but not longer then 1 second.
     * @param client
     */
    public void addPendingClient(Object client) {
        synchronized (pending_clients) {
            pending_clients.add(client);
            pending_clients_timestamp = System.currentTimeMillis();
        }
    }

    /**
     * Remove an object from the set of pending clients.
     * Actions execution resumes when the set becomes empty.
     * @param client
     */
    public void removePendingClient(Object client) {
        synchronized (pending_clients) {
            if (pending_clients.remove(client) && pending_clients.size() == 0) {
                pending_clients_timestamp = System.currentTimeMillis();
            }
        }
    }

    /**
     * Set minimum interval between context actions execution.
     * @param interval - minimum interval in milliseconds.
     */
    public void setContextActionsInterval(long interval) {
        actions_interval = interval;
    }

    /**
     * Add a context action to actions queue.
     * Examples of context actions are resume/suspend/step commands,
     * which were requested by a user.
     * @param action
     */
    public void addContextAction(TCFAction action) {
        assert Protocol.isDispatchThread();
        String id = action.getContextID();
        LinkedList<TCFAction> list = context_action_queue.get(id);
        if (list == null) context_action_queue.put(id, list = new LinkedList<TCFAction>());
        int priority = action.getPriority();
        for (ListIterator<TCFAction> i = list.listIterator();;) {
            if (i.hasNext()) {
                if (priority <= i.next().getPriority()) continue;
                i.previous();
            }
            i.add(action);
            break;
        }
        startAction(id);
    }

    /**
     * Set action result for given context ID.
     * Action results are usually presented to a user same way as context suspend reasons.
     * @param id - debug context ID.
     * @param result - a string to be shown to user.
     */
    public void setContextActionResult(String id, String result) {
        assert Protocol.isDispatchThread();
        for (ActionsListener l : action_listeners) l.onContextActionResult(id, result);
    }

    /**
     * Remove an action from the queue.
     * The method should be called when the action execution is done.
     * @param action
     */
    public void removeContextAction(TCFAction action) {
        assert Protocol.isDispatchThread();
        String id = action.getContextID();
        assert active_actions.get(id) == action;
        active_actions.remove(id);
        for (ActionsListener l : action_listeners) l.onContextActionDone(action);
        startAction(id);
    }

    /**
     * Remove all actions from the queue of a debug context.
     * @param id - debug context ID.
     */
    public void removeContextActions(String id) {
        assert Protocol.isDispatchThread();
        context_action_queue.remove(id);
        context_action_timestamps.remove(id);
    }

    /**
     * Get action queue size of a debug context.
     * @param id - debug context ID.
     * @return count of pending actions.
     */
    public int getContextActionsCount(String id) {
        assert Protocol.isDispatchThread();
        LinkedList<TCFAction> list = context_action_queue.get(id);
        int n = list == null ? 0 : list.size();
        if (active_actions.get(id) != null) n++;
        return n;
    }

    /**
     * Add a listener that will be notified when an action execution is started or finished,
     * or when an action result is posted.
     * @param l - action listener.
     */
    public void addActionsListener(ActionsListener l) {
        action_listeners.add(l);
    }

    /**
     * Remove an action listener that was registered with addActionsListener().
     * @param l - action listener.
     */
    public void removeActionsListener(ActionsListener l) {
        action_listeners.remove(l);
    }

    public Set<String> getContextFilter() {
        return context_filter;
    }
}

Back to the top