1
1
s-lang/slsh/lib/sldbsock.sl

355 строки
7.4 KiB
Plaintext

% Copyright (C) 2012-2017,2018 John E. Davis
%
% This file is part of the S-Lang Library and may be distributed under the
% terms of the GNU General Public License. See the file COPYING for
% more information.
%---------------------------------------------------------------------------
% This file implements a socket-based wrapper around sldbcore for
% debugging a separate process. Programs that wish to use this for
% debugging should something like
%
% autoload ("sldb", "sldbcore");
%
% in a startup file. Then instead of using `require` or `evalfile` to
% load a file, use sldb("name-of-file") to load the file to be
% debugged. Then in another window, run
%
% sldb --pid <pid-of-process-to-be-debugged>
%
% Do not byte-compile this file. It contains both the client and
% server code.
require ("sldbcore");
require ("socket");
% Simple line-based protocol:
private variable SLDB_SOCKET_PREFIX = "/tmp/.sldb_";
private variable OK_CMD_RECEIVED = 200;
private variable OK_CONT_XFER = 201;
private variable OK_XFER_RECEIVED = 202;
private variable ERR_UNKNOWN_CMD = 301;
private variable Debug_Mode = 0;
private define debug_output ()
{
variable args = __pop_args (_NARGS);
if (Debug_Mode)
{
variable fp = stderr;
() = fprintf (fp, __push_args (args));
() = fflush (fp);
}
}
private define make_socket_name (pid)
{
return "$SLDB_SOCKET_PREFIX$pid"$;
}
private define do_write (fp, data)
{
variable nbytes = fwrite (data, fp);
if ((bstrlen (data) != nbytes)
or (-1 == fflush (fp)))
throw WriteError;
debug_output ("** WROTE: %s\n", data);
}
private define do_fgets (fp)
{
variable buf;
if (-1 == fgets (&buf, fp))
throw ReadError;
debug_output ("** READ: %s\n", buf);
return buf;
}
private define receive_response (fp)
{
variable line = do_fgets (fp);
return atoi (line);
}
private define send_response (fp, rsp, txt)
{
rsp = sprintf ("%03d %s\n", rsp, txt);
debug_output ("Sending Response:<%s>\n", rsp);
do_write (fp, rsp);
}
private define send_cmd (fp, cmd)
{
debug_output ("Sending CMD:<%s>\n", cmd);
do_write (fp, cmd);
return receive_response (fp);
}
private define receive_data (fp)
{
variable data = "";
forever
{
variable line = do_fgets (fp);
if (line[0] == '.')
{
if (line == ".\n")
break;
line = substr (line, 2, -1);
}
data = strcat (data, line);
}
debug_output ("** Received:<%s>\n", data);
send_response (fp, OK_XFER_RECEIVED, "Ok transfer received");
return data;
}
private define send_data (fp, data)
{
debug_output ("** Sending:<%s>\n", data);
variable lines = strchop (data, '\n', 0);
variable num = length (lines);
if (data[-1] == '\n')
num--;
_for (0, num-1, 1)
{
variable i = ();
variable line = lines[i];
if (line[0] == '.')
line = strcat (".", line, "\n");
else
line = strcat (line, "\n");
do_write (fp, line);
}
do_write (fp, ".\n");
return receive_response (fp);
}
%
#ifnexists __SLDB_CLIENT__
% Server Routines
private variable Server_Socket_fd = NULL;
private variable Server_Socket_fp = NULL;
private define vmessage_method ()
{
variable args = __pop_args (_NARGS);
variable buf = sprintf (__push_args (args));
debug_output ("Entering vmessage_method to send <%s>\r\n", buf);
variable fp = Server_Socket_fp;
variable status = send_cmd (fp, "MESSAGE\n");
if (status != OK_CONT_XFER)
throw IOError, "Client returned $status to MESSAGE command";
status = send_data (fp, buf);
if (status != OK_XFER_RECEIVED)
throw IOError, "Expected OK_XFER_RECEIVED, got $status"$;
debug_output ("Leaving vmessage_method\r\n");
}
private define open_file_at_linenum (file, linenum)
{
if (path_extname (file) == ".slc")
file = path_sans_extname (file) + ".sl";
variable fp = fopen (file, "r");
if (fp == NULL)
{
vmessage_method ("Unable to open %s\n", file);
return NULL;
}
if (linenum == 1)
return fp;
foreach (fp) using ("line")
{
variable line = ();
linenum--;
if (linenum == 1)
break;
}
return fp;
}
private define list_method (file, linemin, linemax)
{
variable n = linemax - linemin + 1;
foreach (open_file_at_linenum (file, linemin))
{
variable line = ();
vmessage_method ("%d\t%s", linemin, line);
linemin++;
if (linemin > linemax)
break;
}
}
private define read_input_method (prompt, default_cmd)
{
variable fp = Server_Socket_fp;
do
{
variable status = send_cmd (fp, "INPUT\n");
if (status != OK_CONT_XFER)
throw IOError, "Expected OK_CONT_XFER, got $status"$;
status = send_data (fp, prompt);
if (status != OK_XFER_RECEIVED)
throw IOError, "Expected OK_XFER_RECEIVED, got $status"$;
variable input = receive_data (fp);
if (input == "\n")
input = default_cmd;
}
while ((input == "") or (input == NULL));
return input;
}
private define quit_method ()
{
variable fp = Server_Socket_fp;
() = send_cmd (fp, "QUIT\n");
exit (0);
}
private define exit_method ()
{
variable fp = Server_Socket_fp;
() = send_cmd (fp, "EXIT\n");
}
private define initialize_server ()
{
if (Server_Socket_fd != NULL)
return;
variable s = socket (PF_UNIX, SOCK_STREAM, 0);
variable name = make_socket_name (getpid ());
bind (s, name);
() = chmod (name, S_IRUSR|S_IWUSR);
listen (s, 1);
Server_Socket_fd = accept (s);
Server_Socket_fp = fdopen (Server_Socket_fd, "r+");
if (Server_Socket_fd == NULL)
throw OpenError, errno_string (errno);
vmessage_method ("Welcome to sldb");
}
define sldb_initialize ()
{
initialize_server ();
variable m = sldb_methods ();
m.list = &list_method;
m.vmessage = &vmessage_method;
m.read_input = &read_input_method;
m.quit = &quit_method;
m.pprint = NULL;
m.exit = &exit_method;
}
#endif % ! __SLDB_CLIENT__
#ifexists __SLDB_CLIENT__
% Client Routines
private define read_command (s)
{
variable line = strtrim (do_fgets (s));
return line;
}
private define handle_message_cmd (s)
{
send_response (s, OK_CONT_XFER, "Ok continue transfer");
variable msg = receive_data (s);
() = fputs (msg, stdout);
() = fflush (stdout);
}
private define handle_input_cmd (s)
{
send_response (s, OK_CONT_XFER, "Ok continue transfer");
variable prompt = receive_data (s);
prompt = strtrim (prompt, "\n");
forever
{
try
{
variable line = slsh_readline (prompt);
}
catch UserBreakError: continue;
if (line != NULL)
break;
}
send_data (s, line);
}
private define handle_unknown_cmd (s)
{
send_response (s, ERR_UNKNOWN_CMD, "Error Unknown Command");
}
define sldbsock_attach (pid)
{
variable name = make_socket_name (pid);
variable s = socket (PF_UNIX, SOCK_STREAM, 0);
variable e;
try (e)
{
connect (s, name);
}
catch SocketError:
{
() = fprintf (stderr, "Unable to connect to socket %s\n", name);
() = fprintf (stderr, "Is the remote process ready to be debugged?\n");
return -1;
}
variable fp = fdopen (s, "r+");
forever
{
variable cmd = read_command (fp);
if (cmd == "MESSAGE")
{
handle_message_cmd (fp);
continue;
}
if (cmd == "INPUT")
{
handle_input_cmd (fp);
continue;
}
if (cmd == "QUIT")
{
send_response (fp, OK_CMD_RECEIVED, "Ok Cmd Received");
return 0;
}
if (cmd == "EXIT")
{
send_response (fp, OK_CMD_RECEIVED, "Ok Cmd Received");
return 0;
}
handle_unknown_cmd (fp, cmd);
}
}
#endif
provide ("sldbsock");