module: access-path-implementation synopsis: Implementation of debugger connections author: Paul Howard Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND // define constant $dbg-transport-ok = 1; // define constant $dbg-transport-illegal-binding = 2; // define constant $dbg-transport-could-not-find-server = 3; // define constant $dbg-transport-server-spawn-nub-failure = 4; // define constant $dbg-transport-could-not-find-nub = 5; // define constant $dbg-transport-nub-spawn-process-failure = 6; ///// // An instance of can be seen as an object that // has the capability of creating debugger nubs. define abstract class () slot connection-hostname :: ; constant slot connection-open-tethers :: = make(); slot connection-process-list :: = make(); end class; ///// // A debugger connection to a process known to be running on the same // machine as the development environment. define class () end class; ///// // A debugger connection to a process known to be running on a // different machine than the development environment. define open abstract class () constant slot connection-network-address :: , required-init-keyword: network-address:; constant slot connection-password :: , required-init-keyword: password:; slot connection-open? :: = #f; end class; ///// Make sure that is instantiable. define method make (class == , #rest keys, #key) => (connection) apply (make, , keys); end method; ///// // Signalled when attempting to make a debugger connection and // it fails. define abstract class () end class; define class () constant slot attempted-connection-network-address :: , required-init-keyword: network-address:; end class; define class () constant slot attempted-connection-password :: , required-init-keyword: wrong-password:; end class; ///// *OPEN-DEBUGGER-CONNECTIONS* // Holds all open debugger connections. define variable *open-debugger-connections* = make(); ///// DO-OPEN-DEBUGGER-CONNECTIONS // Iterates over all open debugger connections. define function do-open-debugger-connections (f :: ) => () for (connection in *open-debugger-connections*) f(connection) end for end function; ///// DO-OPEN-ACCESS-CONNECTIONS // Iterates over all open tethers within a particular debugger connection. define function do-open-access-connections (f :: , server :: ) => () for (connection in server.connection-open-tethers) f(connection) end for end function; ///// INITIALIZE (Dylan) // Sets up debugger connections. define constant $local-hostname :: = begin let hostname-length = get-local-hostname-length(); let hostname = make(, size: hostname-length); get-local-hostname(hostname-length, hostname); hostname end; define method initialize (connection :: , #key, #all-keys) => () // A local debugger connection is really nothing more than a place // holder. All we need to do is fetch the local hostname from the // local debugger nub. next-method(); connection.connection-hostname := $local-hostname; add!(*open-debugger-connections*, connection); end method; ///// DESCRIBE-DEBUGGER-CONNECTION // Produces a printable representation of a debugger connection. // TODO: This should include a description of the transport // protocol in use by the server. define method describe-debugger-connection (connection :: ) => (desc :: ) format-to-string("%s (Local machine)", connection.connection-hostname) end method; define method describe-debugger-connection (connection :: ) => (desc :: ) format-to-string("%s (%s)", connection.connection-hostname, connection.connection-network-address) end method; ///// *DEFAULT-LOCAL-DEBUGGER-CONNECTION* // While our connection philosphy is currently very simple - there is // no real nub server or debugger connection, this object serves as // a place-holder for it. define constant *default-local-debugger-connection* = make (); ///// HOST-MACHINE // A functional interface, returning the local debugger connection. define function host-machine () => (connection :: ) *default-local-debugger-connection* end function; ///// // Instances of relate directly to instances of // a debugger nub. They contain the information that is necessary to // communicate with a specific debugger nub from the access path. An // access connection can be local or remote, and (orthogonally) 32-bit // or 64-bit. define open abstract class () constant slot access-debugger-connection :: , required-init-keyword: debugger-connection:; constant slot access-connection-description :: , init-value: "No description available", init-keyword: description:; slot connection-process :: , init-keyword: process:; end class; define class () end class; ///// START-APPLICATION-ON-CONNECTION // This function is called to initialize an instance of // and calls the server function to create // the running process. If the access connection is local, then the // server returns a packaged process descriptor (a ) which is // saved in the access connection. define open generic start-application-on-connection (conn :: , command :: , arguments :: , symbol-file-directories :: , working-directory :: false-or(), library-search-paths :: , #key) => (); define method start-application-on-connection (conn :: , command :: , arguments :: , symbol-file-directories :: , working-directory :: false-or(), library-search-paths :: , #key own-shell? = #t) => () let create-shell = if (own-shell?) 1 else 0 end if; let symfile-c-strings = map(curry(as, ), symbol-file-directories); let symfile-dir-array = make(, element-count: symfile-c-strings.size); for (i :: from 0 below symfile-c-strings.size) symfile-dir-array[i] := symfile-c-strings[i]; end for; let lsp-c-strings = map(curry(as, ), library-search-paths); let lsp-dir-array = make(, element-count: lsp-c-strings.size); for (i :: from 0 below lsp-c-strings.size) lsp-dir-array[i] := lsp-c-strings[i]; end for; let (process, success) = open-local-tether(command, arguments, symbol-file-directories.size, symfile-dir-array, library-search-paths.size, lsp-dir-array, working-directory | "", create-shell); destroy(symfile-dir-array); do(destroy, symfile-c-strings); destroy(lsp-dir-array); do(destroy, lsp-c-strings); if (success == 0) signal(make()); else conn.connection-process := process; add!(conn.access-debugger-connection.connection-open-tethers, conn); end if; end method; ///// ATTACH-APPLICATION-ON-CONNECTION // This function is called to initialize an instance of // and calls the server function to attach to // the running process. If the access connection is local, then the // server returns a packaged process descriptor (a ) which is // saved in the access connection. define open generic attach-application-on-connection (conn :: , process :: , symbol-file-directories :: , system-info :: ) => (); define method attach-application-on-connection (conn :: , process :: , symbol-file-directories :: , system-info :: ) => () let symfile-c-strings = map(curry(as, ), symbol-file-directories); let symfile-dir-array = make(, element-count: symfile-c-strings.size); for (i :: from 0 below symfile-c-strings.size) symfile-dir-array[i] := symfile-c-strings[i]; end for; let (process, success) = attach-local-tether(process.nub-descriptor, symbol-file-directories.size, symfile-dir-array, system-info); destroy(symfile-dir-array); do(destroy, symfile-c-strings); if (success == 0) signal(make()); else conn.connection-process := process; add!(conn.access-debugger-connection.connection-open-tethers, conn); end if; end method; ///// CLOSE-REMOTE-DEBUGGER-CONNECTION // Declares that a remote debugger connection is not going to be used // again within the lifetime of the calling program. define method close-remote-debugger-connection (connection :: ) => () remove!(*open-debugger-connections*, connection); connection.connection-open? := #f; end method;