binutils-gdb/gdb/energize.c

1524 lines
34 KiB
C
Raw Normal View History

/* Energize (formerly known as Cadillac) interface routines.
Copyright 1991, 1992 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "defs.h"
#include "symtab.h"
#include "inferior.h"
#include "command.h"
#include "bfd.h"
#include "symfile.h"
#include "objfiles.h"
#include <sys/types.h>
#include <sys/time.h>
#include <sys/param.h>
#include "energize/connection.h"
#include "energize/genericreq.h"
#include "energize/debuggerreq.h"
#include "energize/debuggerconn.h"
#include "energize/ttyconn.h"
#include <varargs.h>
#include <sys/stat.h>
#ifdef USG
#include <sys/file.h>
#endif
#include <fcntl.h>
#include <sys/filio.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/errno.h>
#include <termios.h>
#include <string.h>
/* Non-zero means that we're doing the energize interface. */
int energize = 0;
/* Connection block for debugger<=>kernel communications. */
static Connection *conn = 0;
/* fd for our socket to the kernel. */
static int kerfd;
/* The kernel's ID for this instance of the program. */
static int program_id;
static int instance_id;
/* The fd for the pty associated with the inferior. */
static int inferior_pty = -1;
static int inferior_tty = -1;
static int has_run = 0;
extern int pgrp_inferior;
extern char *source_path;
char **pprompt; /* Pointer to pointer to prompt */
/* Tell energize_command_line_input() where to get its text from */
static int doing_breakcommands_message = 0;
/* Stash command text here */
static char *command_line_text = 0;
static int command_line_length = 0;
/* Flags returned by wait_for_events() */
#define KERNEL_EVENT 1
#define PTY_EVENT 2
/* This routine redirects the output of fputs_filtered to the kernel so that
the user can see what's going on in his debugger window. */
void
energize_fputs(ptr)
const char *ptr;
{
if (conn)
CVWriteTranscriptInfo (conn, instance_id, (char *)ptr);
else
fputs (ptr, stdout);
}
void
energize_query(query, args)
char *query;
va_list args;
{
char buf[100];
if (!energize)
return;
vsprintf(buf, query, args);
CVWriteQueryInfo(conn,
instance_id,
CQueryConfirm,
qno_unknown,
buf,
""); /* transcript */
}
void
energize_acknowledge_query(ack)
char *ack;
{
CVWriteQueryInfo(conn,
instance_id,
CQueryAcknowleged,
0,
ack,
""); /* transcript */
}
/* Copy all data from the pty to the kernel. */
static void
pty_to_kernel()
{
CTtyRequest *req;
char buf[1024];
int cc;
while (1)
{
cc = read(inferior_pty, buf, sizeof(buf));
if (cc == 0
|| (cc < 0
&& errno == EWOULDBLOCK))
break;
if (cc < 0)
{
close(inferior_pty);
inferior_pty = -1;
perror("pty read error");
break;
}
req = CWriteTtyRequest(conn, TextIORType);
CWriteVstringLen(conn, buf, cc);
CWriteLength(conn);
}
CWriteRequestBuffer(conn);
}
/* Copy data from the kernel to the pty. */
static void
kernel_to_pty(data, len)
char *data;
int len;
{
int cc;
cc = write(inferior_pty, data, len);
if (cc != len)
{
if (cc < 0)
{
close(inferior_pty);
inferior_pty = -1;
perror("pty write error");
return;
}
printf("Couldn't write all the data to the pty, wanted %d, got %d\n",
len, cc);
}
}
static char *
full_filename(symtab)
struct symtab *symtab;
{
int pathlen;
char *filename;
if (!symtab)
return NULL;
if (symtab->fullname)
return savestring(symtab->fullname, strlen(symtab->fullname));
if (symtab->dirname)
pathlen = strlen(symtab->dirname);
else
pathlen = 0;
if (symtab->filename)
pathlen += strlen(symtab->filename);
filename = xmalloc(pathlen+1);
if (symtab->dirname)
strcpy(filename, symtab->dirname);
else
*filename = '\000';
if (symtab->filename)
strcat(filename, symtab->filename);
return filename;
}
/* Tell the energize kernel how high the stack is so that frame numbers (which
are relative to the current stack height make sense.
Calculate the number of frames on the stack, and the number of subroutine
invocations that haven't changed since the last call to this routine. The
second number is calculated by comparing the PCs of the current stack frames
to the PCs of the previous set of stack frames. The screw here is that a
subroutine may call several different procedures, which means that the PC
in its frame changes, even though you are still in the same subroutine. We
resolve this by converting the frames PC into the PC at the start of the
function (for efficiency, this is done only if the simple comparison test
fails). */
struct pclist
{
CORE_ADDR pc;
struct pclist *next;
};
/* Non-zero means that Energize kernel already knows how high the stack is. */
static int stack_info_valid = 0;
static void
send_stack_info()
{
struct pclist *pclist = 0, *pli, *opli;
static struct pclist *old_pclist;
FRAME frame;
int height, similar;
if (stack_info_valid)
return;
height = 0;
similar = 0;
/* First, calculate the stack height, and build the new pclist */
for (frame = get_current_frame();
frame != 0;
frame = get_prev_frame(frame))
{
(height)++;
pli = (struct pclist *)xmalloc(sizeof(struct pclist));
pli->pc = frame->pc;
pli->next = pclist;
pclist = pli;
}
/* Now, figure out how much of the stack hasn't changed */
for (pli = pclist, opli = old_pclist;
pli != 0 && opli != 0;
pli = pli->next, opli = opli->next, (similar)++)
{
if ((pli->pc != opli->pc)
&& (get_pc_function_start(pli->pc)
!= get_pc_function_start(opli->pc)))
break;
}
/* Free up all elements of the old pclist */
opli = old_pclist;
while (opli)
{
pli = opli->next;
free (opli);
opli = pli;
}
old_pclist = pclist; /* Install the new pclist */
CVWriteStackSizeInfo(conn,
instance_id,
height, /* Frame depth */
CInnerFrameIs0,
similar, /* Frame diff */
"" /* Transcript */
);
stack_info_valid = 1;
}
/* Tell the kernel where we are in the program, and what the stack looks like.
*/
static void
send_status()
{
static int linecount = 48;
struct symtab_and_line sal;
struct symbol *symbol;
char *funcname, *filename;
static int sent_prog_inst = 0;
if (!has_run)
return;
if (inferior_pid == 0) /* target has died */
{
CVWriteProgramTerminatedInfo(conn,
instance_id,
""
);
return;
}
sal = find_pc_line(stop_pc, 0);
symbol = find_pc_function(stop_pc);
funcname = symbol ? symbol->name : "";
filename = full_filename(sal.symtab);
if (!sent_prog_inst)
{
sent_prog_inst = 1;
CVWriteProgramInstanceInfo(conn,
program_id,
instance_id,
"", /* hostname */
"", /* arglist */
""
);
}
send_stack_info();
CVWriteStackFrameInfo(conn,
instance_id,
sal.line,
CFileLinePos,
0, /* XXX - frame # */
funcname,
filename,
"" /* XXX ? transcript */
);
CVWriteProgramStoppedInfo(conn,
instance_id,
0, /* XXX - breakpoint # or signal # */
CDebuggerCommand,
funcname,
"" /* XXX ? transcript */
);
if (filename)
free(filename);
}
/* Call this to output annotated function names. Names will be demangled if
necessary. arg_mode contains flags that are passed on to cplus_demangle. */
void
energize_annotate_function(funcname, arg_mode, level)
char *funcname;
int arg_mode;
int level;
{
extern int demangle;
char *demangled_name = NULL;
if (funcname == NULL)
return;
if (demangle)
{
demangled_name = cplus_demangle(funcname, arg_mode);
if (demangled_name)
funcname = demangled_name;
}
send_stack_info();
if (level < 0) level = 0;
CVWriteBackTraceEntryInfo(conn,
instance_id,
level, /* frameNo */
funcname);
if (demangled_name)
free(demangled_name);
}
/* Call this just prior to printing out the name & value of a variable. This
tells the kernel where to annotate the output. */
/* The args are:
expression - A text handle on what GDB can use to reference this value.
This can be a symbol name, or a convenience var, etc...
symbol - Used to determine the scope of the data. May be NULL.
type - Determines if we have a pointer ref, and the print name of the type.
Used in ShowValue message.
valaddr - The address in target memory of the data.
field - The field name of the struct or union element being referenced.
*/
static char cum_expr[200]; /* Cumulative expression */
static char *expr_stack[100] = {cum_expr}; /* Pointers to end of expressions */
static char **last_expr = expr_stack; /* Current expr stack pointer */
void
energize_start_variable_annotation(expression, symbol, type, valaddr, field)
char *expression;
struct symbol *symbol;
struct type *type;
CORE_ADDR valaddr;
char *field;
{
int ref_type;
int stor_cl;
enum type_code type_code;
enum address_class sym_class;
char *type_cast;
if (!energize)
return;
send_stack_info();
strcpy(*last_expr++, expression);
*last_expr = *(last_expr-1) + strlen(expression);
switch (TYPE_CODE(type))
{
case TYPE_CODE_ARRAY:
case TYPE_CODE_STRUCT:
case TYPE_CODE_UNION:
case TYPE_CODE_ENUM:
case TYPE_CODE_INT:
case TYPE_CODE_FLT:
ref_type = CValueValueRef;
break;
case TYPE_CODE_PTR:
ref_type = CValuePointerRef;
break;
default:
ref_type = CValueUndefRef;
break;
}
/* Make sure that pointer points at something we understand */
if (ref_type == CValuePointerRef)
switch (TYPE_CODE(TYPE_TARGET_TYPE(type)))
{
case TYPE_CODE_PTR:
case TYPE_CODE_ARRAY:
case TYPE_CODE_STRUCT:
case TYPE_CODE_UNION:
case TYPE_CODE_ENUM:
case TYPE_CODE_INT:
case TYPE_CODE_FLT:
break;
default:
ref_type = CValueUndefRef;
break;
}
if (symbol)
{
sym_class = SYMBOL_CLASS(symbol);
switch (sym_class)
{
case LOC_CONST:
case LOC_CONST_BYTES:
stor_cl = CValueStorStaticConst;
break;
case LOC_STATIC:
stor_cl = CValueStorStaticVar;
break;
case LOC_REGISTER:
case LOC_REGPARM:
stor_cl = CValueStorRegister;
break;
case LOC_ARG:
case LOC_REF_ARG:
case LOC_LOCAL:
case LOC_LOCAL_ARG:
stor_cl = CValueStorLocalVar;
break;
default:
stor_cl = CValueStorUndef;
break;
}
}
else
stor_cl = CValueStorUndef;
type_cast = TYPE_NAME(type);
CVWriteValueBeginInfo(conn,
instance_id,
valaddr,
ref_type,
stor_cl,
0, /* XXX - frameno */
cum_expr,
field,
type_cast,
""); /* transcript */
}
void
energize_end_variable_annotation()
{
if (!energize)
return;
last_expr--; /* Pop the expr stack */
**last_expr = '\000'; /* Cut off the last part of the expr */
CVWriteValueEndInfo(conn,
instance_id,
""); /* transcript */
}
/* Tell the kernel that the target is now running. */
static void
go_busy()
{
CVWriteProgramBusyInfo(conn,
instance_id,
""); /* XXX ? transcript */
CWriteRequestBuffer(conn); /* Must take place synchronusly! */
stack_info_valid = 0;
}
void
energize_symbol_file(objfile)
struct objfile *objfile;
{
if (!energize)
return;
CVWriteSymbolTableInfo(conn,
objfile->name,
""); /* Transcript */
}
/* execute_command_1(echo, queue, cmd, args) - echo - non-zero means echo the
command. queue - non-zero means don't execute it now, just save it away for
later. cmd - string containing printf control sequences. args - list of
arguments needed by those control sequences.
*/
/* Linked list of queued up commands */
static struct command_line *queued_commands = 0;
static struct command_line *last_queued_command = 0;
/* Call this routine to save a command for later. The command string is
copied into freshly malloc'ed memory. */
static void
queue_command(cmd)
char *cmd;
{
char *buf;
struct command_line *cl;
unsigned long s;
s = (strlen(cmd) + 1) + 7 & ~(unsigned long)7;
buf = (char *)xmalloc(s + sizeof(struct command_line));
cl = (struct command_line *)(buf + s);
cl->next = 0;
cl->line = buf;
strncpy(cl->line, cmd, s);
if (queued_commands)
last_queued_command->next = cl;
else
queued_commands = cl;
last_queued_command = cl;
}
/* Call this procedure to take a command off of the command queue. It returns
a pointer to a buf which the caller is responsible for freeing. NULL is
returned if there are no commands queued. */
static char *
dequeue_command()
{
struct command_line *cl;
char *cmd;
cl = queued_commands;
if (!cl)
return NULL;
queued_commands = cl->next;
return cl->line;
}
static void
execute_command_1(va_alist)
va_dcl
{
char buf[100]; /* XXX - make buf dynamic! */
int echo;
int queue;
char *cmd;
va_list args;
va_start(args);
echo = va_arg(args, int);
queue = va_arg(args, int);
cmd = va_arg(args, char *);
vsprintf(buf, cmd, args);
if (queue)
queue_command(buf);
else
{
if (echo)
printf_filtered("%s\n", buf);
execute_command(buf, 1);
}
va_end(args);
}
#ifdef KERNEL_RECORD
FILE *kerout;
static int
kernel_record(fd, ptr, num)
int fd, num;
char *ptr;
{
fwrite(ptr, num, 1, kerout);
fflush(kerout);
return write(fd, ptr, num);
}
#endif
void
energize_condition_breakpoint(b)
struct breakpoint *b;
{
if (energize)
CVWriteBreakConditionInfo(conn,
instance_id,
b->number,
b->cond_string ? b->cond_string : "",
"" /* transcript */
);
}
void
energize_commands_breakpoint(b)
struct breakpoint *b;
{
struct command_line *l;
if (!energize)
return;
CVWriteBreakCommandBegInfo(conn,
instance_id,
b->number,
""); /* transcript */
for (l = b->commands; l; l = l->next)
CVWriteBreakCommandEntryInfo(conn,
instance_id,
l->line,
""); /* transcript */
CVWriteBreakCommandEndInfo(conn,
instance_id,
""); /* transcript */
}
static void
breakpoint_notify(b, action)
struct breakpoint *b;
int action;
{
struct symbol *sym;
char *funcname = "";
char *filename;
char *included_in_filename = "";
if (!energize)
return;
if (b->type != bp_breakpoint)
return;
filename = full_filename(b->symtab);
sym = find_pc_function(b->address);
if (sym)
funcname = SYMBOL_NAME(sym);
CVWriteBreakpointInfo (conn,
instance_id,
b->number,
b->line_number,
CFileLinePos,
CBreakOnInstrAccess,
action,
b->ignore_count,
funcname,
filename ? filename : "",
"", /* included_in_filename */
"" /* transcript */
);
if (b->commands)
energize_commands_breakpoint(b);
energize_condition_breakpoint(b);
if (filename)
free(filename);
}
void
energize_create_breakpoint(b)
struct breakpoint *b;
{
breakpoint_notify(b, CEnableBreakpoint);
}
void
energize_delete_breakpoint(b)
struct breakpoint *b;
{
breakpoint_notify(b, CDeleteBreakpoint);
}
void
energize_enable_breakpoint(b)
struct breakpoint *b;
{
breakpoint_notify(b, CEnableBreakpoint);
}
void
energize_disable_breakpoint(b)
struct breakpoint *b;
{
breakpoint_notify(b, CDisableBreakpoint);
}
void
energize_ignore_breakpoint(b)
struct breakpoint *b;
{
breakpoint_notify(b, CBreakAttrUnchanged);
}
/* Open up a pty and its associated tty. Return the fd of the tty. */
static void
getpty()
{
int n, ptyfd, ttyfd;
static char dev[30];
struct stat statbuf;
struct termios termios;
#define HIGHPTY (('z' - 'p') * 16 - 1)
for (n = 0; n <= HIGHPTY; n++)
{
sprintf(dev, "/dev/pty%c%x", n/16 + 'p', n%16);
if (stat(dev, &statbuf))
break;
ptyfd = open(dev, O_RDWR);
if (ptyfd < 0)
continue;
sprintf(dev, "/dev/tty%c%x", n/16 + 'p', n%16);
ttyfd = open(dev, O_RDWR);
if (ttyfd < 0) {close(ptyfd); continue;}
/* Setup pty for non-blocking I/O. Also make it give us a SIGIO when
there's data available. */
n = fcntl(ptyfd, F_GETFL, 0);
fcntl(ptyfd, F_SETFL, n|FNDELAY|FASYNC);
fcntl(ptyfd, F_SETOWN, getpid());
tcgetattr(ttyfd, &termios);
termios.c_oflag &= ~OPOST; /* No post-processing */
tcsetattr(ttyfd, TCSANOW, &termios);
inferior_pty = ptyfd;
inferior_tty = ttyfd;
return;
}
error ("getpty: can't get a pty\n");
}
/* Examine a protocol packet from the driver. */
static void
kernel_dispatch(queue)
int queue; /* Non-zero means we should just queue up
commands. */
{
register CHeader *head;
head = (CHeader *)CPeekNextRequest (conn);
if (head == NULL)
{
fprintf (stderr, "EOF on kernel read!\n");
exit (1);
}
if (head->reqType < LastTtyRequestRType)
{
CTtyRequest* req = CReadTtyRequest (conn);
switch (req->head.reqType)
{
case AcceptConnectionRType:
/* Tell the rest of the world that energize is now set up */
CSkipRequest (conn);
break;
case RefuseConnectionRType:
fprintf (stderr, "Debugger connection refused\n");
exit (1);
case KillProgramRType:
exit (0);
case TextIORType:
{
char *p;
ReqLen len;
p = CGetVstring(conn, &len);
kernel_to_pty(p, len);
}
break;
default:
fprintf(stderr, "Unknown Tty request type = %d\n",
req->head.reqType);
break;
}
}
else
{
CVDebuggerRequest *req = CVReadDebuggerRequest (conn);
if (!req)
{
fprintf (stderr, "CVReadDebuggerRequest returned NULL, type = %d\n",
head->reqType);
exit(1);
}
switch (req->head.request->reqType)
{
case OpenProgramInstanceRType:
{
char *arglist, buf[100]; /* XXX - Make buf dynamic! */
int arglen;
/* XXX - should notice when program_id changes */
arglist = req->openProgramInstance.progArglist.text;
arglen = req->openProgramInstance.progArglist.byteLen;
execute_command_1(1, queue, "break main");
execute_command_1(1, queue, "enable delete $bpnum");
if (arglist)
{
execute_command_1(1, queue, "set args %.*s", arglen, arglist);
}
execute_command_1(1, queue, "run");
}
break;
case SearchPathRType:
directory_command(req->searchPath.searchPath.text, 0);
break;
case QuitDebuggerRType:
execute_command_1(1, queue, "quit");
break;
case RunRType:
if (req->run.request->useArglist == CNewArglist)
{
execute_command_1(1, queue, "set args %.*s",
req->run.progArglist.byteLen,
req->run.progArglist.text);
}
execute_command_1(1, queue, "run");
break;
case ContinueRType:
execute_command_1(1, queue, "continue");
break;
case StepRType:
execute_command_1(1, queue, "step %d", req->step.request->stepCount);
break;
case NextRType:
execute_command_1(1, queue, "next %d", req->next.request->nextCount);
break;
case ChangeStackFrameRType:
switch (req->changeStackFrame.request->frameMovement)
{
case CToCurrentStackFrame:
execute_command_1(1, queue, "frame %d",
req->changeStackFrame.request->frameNo);
break;
case CToInnerStackFrame:
execute_command_1(1, queue, "down %d",
req->changeStackFrame.request->frameNo);
break;
case CToOuterStackFrame:
execute_command_1(1, queue, "up %d",
req->changeStackFrame.request->frameNo);
break;
case CToAbsoluteStackFrame:
execute_command_1(1, queue, "frame %d",
req->changeStackFrame.request->frameNo);
break;
}
break;
case BackTraceRType:
/* XXX - deal with limit??? */
execute_command_1(1, queue, "backtrace");
break;
case FinishRType:
execute_command_1(1, queue, "finish");
break;
case TerminateProgramRType:
execute_command_1(1, queue, "kill");
break;
case NewBreakpointRType:
{
char *tail;
int skipped;
tail = strrchr(req->newBreakpoint.fileName.text, '/');
if (!tail)
tail = req->newBreakpoint.fileName.text;
else
tail++;
skipped = tail - req->newBreakpoint.fileName.text;
execute_command_1(1, queue, "break %.*s:%d",
req->newBreakpoint.fileName.byteLen - skipped,
tail,
req->newBreakpoint.request->fileLinePos);
}
break;
case StopRType:
killpg(pgrp_inferior, SIGINT);
break;
case UserInputRType:
{
char *text;
long len;
/* XXX - should really break command up into seperate lines
and spoon-feed it to execute_command */
text = req->userInput.userInput.text;
len = req->userInput.userInput.byteLen;
if (text[len-1] == '\n') text[len-1] = '\000';
while (*text == ' ' || *text == '\t') text++;
if (strcmp(text, "]*[") == 0) /* XXX - What does this mean??? */
break;
if (*text != '\000')
execute_command_1(0, queue, "%s", text);
else
print_prompt(); /* User just typed a blank line */
}
break;
case QueryResponseRType:
{
char *resp;
if (req->queryResponse.request->response)
resp = "y";
else
resp = "n";
execute_command_1(1, 1, resp);
printf_filtered("%s\n", resp);
}
break;
case ChangeBreakpointRType:
switch (req->changeBreakpoint.request->breakpointAttr)
{
case CBreakAttrUnchanged:
execute_command_1(1, queue, "ignore %d %d",
req->changeBreakpoint.request->breakpointId,
req->changeBreakpoint.request->ignoreCount);
break;
case CEnableBreakpoint:
execute_command_1(1, queue, "enable %d",
req->changeBreakpoint.request->breakpointId);
break;
case CDisableBreakpoint:
execute_command_1(1, queue, "disable %d",
req->changeBreakpoint.request->breakpointId);
break;
case CDeleteBreakpoint:
execute_command_1(1, queue, "delete %d",
req->changeBreakpoint.request->breakpointId);
break;
case CEnableDisableBreakpoint:
execute_command_1(1, queue, "enable once %d",
req->changeBreakpoint.request->breakpointId);
break;
case CEnableDeleteBreakpoint:
execute_command_1(1, queue, "enable delete %d",
req->changeBreakpoint.request->breakpointId);
break;
default:
printf_filtered("ChangeBreakpointRType: unknown breakpointAttr\n");
printf_filtered(" breakpointAttr = %d\n",
req->changeBreakpoint.request->breakpointAttr);
printf_filtered(" breakpointId = %d\n",
req->changeBreakpoint.request->breakpointId);
printf_filtered(" breakpointType = %d\n",
req->changeBreakpoint.request->breakpointType);
printf_filtered(" ignoreCount = %d\n",
req->changeBreakpoint.request->ignoreCount);
break;
}
break;
case BreakConditionRType:
execute_command_1(1, queue, "condition %d %.*s",
req->breakCondition.request->breakpointId,
req->breakCondition.condition.byteLen,
req->breakCondition.condition.text);
break;
case BreakCommandsRType:
/* Put pointers to where energize_command_line_input() can find
them. */
doing_breakcommands_message = 1;
command_line_length = req->breakCommands.commands.byteLen;
command_line_text = req->breakCommands.commands.text;
execute_command_1(1, queue, "commands %d",
req->breakCommands.request->breakpointId);
command_line_text = (char *)NULL;
command_line_length = 0;
doing_breakcommands_message = 0;
break;
case ShowValueRType:
{
char expr[100], *p = expr;
expr[0] = 0;
if (req->showValue.request->ref_type == CValuePointerRef)
strcat(expr, "* ");
if (req->showValue.type_cast.byteLen)
{
strcat(expr, "(");
strncat(expr, req->showValue.type_cast.text,
req->showValue.type_cast.byteLen);
strcat(expr, ") ");
}
if (req->showValue.field.byteLen)
strcat(expr, "(");
strncat(expr, req->showValue.expression.text,
req->showValue.expression.byteLen);
if (req->showValue.field.byteLen)
{
strcat(expr, ")");
strncat(expr, req->showValue.field.text,
req->showValue.field.byteLen);
}
execute_command_1(1, queue, "print %s", expr);
}
break;
case SetValueRType:
{
char expr[100], *p = expr;
expr[0] = 0;
if (req->setValue.request->ref_type == CValuePointerRef)
strcat(expr, "* ");
#if 0
if (req->setValue.type_cast.byteLen)
{
strcat(expr, "(");
strncat(expr, req->setValue.type_cast.text,
req->setValue.type_cast.byteLen);
strcat(expr, ") ");
}
#endif
if (req->setValue.field.byteLen)
strcat(expr, "(");
strncat(expr, req->setValue.expression.text,
req->setValue.expression.byteLen);
if (req->setValue.field.byteLen)
{
strcat(expr, ")");
strncat(expr, req->setValue.field.text,
req->setValue.field.byteLen);
}
execute_command_1(1, queue, "print %s = (%s) %s", expr,
req->setValue.type_cast.text,
req->setValue.value.text);
}
break;
case DynamicLoadRType:
switch (req->dynamicLoad.request->action)
{
case CDynamicLoadUpdateSymtab:
printf_filtered("CDynamicLoadUpdateSymtab, filename=%s\n",
req->dynamicLoad.filenames.text);
break;
default:
printf_filtered("DynamicLoadRType: unknown action=%d, filename=%s\n",
req->dynamicLoad.request->action,
req->dynamicLoad.filenames.text);
break;
}
break;
default:
fprintf(stderr, "Unknown Debugger request type = %d\n",
req->head.request->reqType);
break;
}
free (req); /* Should probably call CVFreeDebuggerRequest() here, but
can't do so if interrupt level has mucked with req->
request. CVFreeDebuggerRequest() only ends up calling
free() anyway! */
}
}
/* Return a bitmask indicating if the kernel or the pty did something
interesting. Set poll to non-zero if you don't want to wait. */
static int
wait_for_events(poll)
int poll;
{
fd_set readfds;
int numfds;
int eventmask = 0;
static struct timeval tv = {0};
/* Output all pending requests. */
CWriteRequestBuffer(conn);
FD_ZERO(&readfds);
/* Wait till there's some activity from the kernel or the pty. */
do
{
FD_SET(kerfd, &readfds);
if (inferior_pty > 0)
FD_SET(inferior_pty, &readfds);
if (poll)
numfds = select(sizeof(readfds)*8, &readfds, 0, 0, &tv);
else
numfds = select(sizeof(readfds)*8, &readfds, 0, 0, 0);
}
while (numfds <= 0 && !poll);
if (FD_ISSET(inferior_pty, &readfds))
eventmask |= PTY_EVENT;
if (FD_ISSET(kerfd, &readfds))
eventmask |= KERNEL_EVENT;
return eventmask;
}
/* This is called from read_command_lines() to provide the text for breakpoint
commands, which is supplied in a BreakCommands message. Each call to this
routine supplies a single line of text, with the newline removed. */
/* This routine may be invoked in two different contexts. In the first, it
is being called as a result of the BreakCommands message. In this case,
all of the command text is immediately available. In the second case, it is
called as a result of the user typing the 'command' command. The command
text then needs to be glommed out of UserInput messages (and possibly other
messages as well). The most 'straighforward' way of doing this is to
basically simulate the main loop, but just accumulate the command text
instead of sending it to execute_command(). */
char *
energize_command_line_input(prompt, repeat)
char *prompt;
int repeat;
{
char *p;
if (!energize)
return command_line_input(prompt, repeat);
if (doing_breakcommands_message)
{
if (command_line_length <= 0)
return (char *)NULL;
p = command_line_text;
while (command_line_length-- > 0)
{
if (*command_line_text == '\n')
{
*command_line_text = '\000';
command_line_text++;
break;
}
command_line_text++;
}
printf_filtered("%s\n", p);
return p;
}
else
{
/* We come here when the user has typed the 'command' or 'define' command
to the GDB window. We are basically deep inside of the 'command'
command processing routine right now, and will be called to get a new
line of input. We expect that kernel_dispatch will queue up only one
command at a time. */
int eventmask;
static char buf[100];
eventmask = wait_for_events(0);
if (eventmask & PTY_EVENT)
pty_to_kernel();
if (eventmask & KERNEL_EVENT)
kernel_dispatch(1); /* Queue up commands */
/* Note that command has been echoed by the time we get here */
p = dequeue_command();
if (p)
{
strncpy(buf, p, sizeof(buf));
free(p);
return buf;
}
else
return NULL;
}
}
/* Establish contact with the kernel. */
void
energize_initialize(energize_id, execarg)
char *energize_id;
char *execarg;
{
CTtyRequest *req;
char *ctmp;
extern long strtol(char *str, char **ptr, int base);
char pathname[MAXPATHLEN];
int n;
if (!energize_id)
return;
if (!execarg) execarg = "";
printf("\ngdb-debugger pid=%d\n", getpid()); /* XXX - debugging only */
/* First establish the connection with the kernel. */
kerfd = COpenClientSocket(NULL);
if (kerfd < 0) {
printf("COpenClientSocket() failed\n");
exit(1);
}
/* Setup for I/O interrupts when appropriate. */
n = fcntl(kerfd, F_GETFL, 0);
fcntl(kerfd, F_SETFL, n|FASYNC);
fcntl(kerfd, F_SETOWN, getpid());
/* Setup connection buffering. */
CSetSocketBufferSize (kerfd, 12000);
/* Generate a new connection control block. */
conn = NewConnection (0, kerfd, kerfd);
if (!conn) {
printf("NewConnection() failed\n");
exit(1);
}
#ifdef KERNEL_RECORD
kerout = fopen("kernel.output", "+w");
CReadWriteHooks(conn, conn->previewMethod, conn->readMethod, kernel_record);
#endif
/* Tell the kernel that we are the "debugger". */
req = CWriteTtyRequest (conn, QueryConnectionRType);
req->generic.queryconnection.major = 0;
req->generic.queryconnection.minor = 0;
req->generic.queryconnection.cadillacId1=strtol(energize_id, &ctmp, 16);
req->generic.queryconnection.cadillacId2 = strtol(++ctmp, NULL, 16);
req->generic.queryconnection.nProtocols = 1;
CWriteProtocol (conn, 0, 0, "debugger");
CWriteLength (conn);
/* Tell the kernel that we are actually running. */
/* KROCK ALERT!!! The kernel doesn't really care about the arguments to
the program at all! It only cares that argument 7 be the name of the
target program. So, we just fill in the rest of the slots with
padding. I hope the kernel never cares about this! */
req = CWriteTtyRequest (conn, RunningProgramRType);
req->runningprogram.argc = 8;
getwd (pathname);
CWriteVstring0 (conn, pathname);
CWriteVstring0 (conn, "0");
CWriteVstring0 (conn, "1");
CWriteVstring0 (conn, "2");
CWriteVstring0 (conn, "3");
CWriteVstring0 (conn, "4");
CWriteVstring0 (conn, "5");
CWriteVstring0 (conn, "6");
CWriteVstring0 (conn, execarg);
CWriteLength (conn);
/* Tell the kernel our PID and all that */
program_id = 1;
CVWriteDebugProgramInfo(conn,
getpid(),
program_id,
execarg,
"");
/* Tell the rest of the world that Energize is now set up. */
energize = 1;
setsid(); /* Drop controlling tty, become pgrp master */
getpty(); /* Setup the pty */
dup2(inferior_tty, 0); /* Attach all GDB I/O to the pty */
dup2(inferior_tty, 1);
dup2(inferior_tty, 2);
}
/* This is called from execute_command, and provides a wrapper around
various command routines in a place where both protocol messages and
user input both flow through.
*/
void
energize_call_command(cmdblk, arg, from_tty)
struct cmd_list_element *cmdblk;
char *arg;
int from_tty;
{
if (!energize)
{
(*cmdblk->function.cfunc) (arg, from_tty);
return;
}
if (cmdblk->class == class_run)
{
go_busy();
has_run = 1;
(*cmdblk->function.cfunc)(arg, from_tty);
send_status();
}
else
(*cmdblk->function.cfunc)(arg, from_tty);
print_prompt();
}
void
energize_new_process()
{
instance_id = inferior_pid;
}
static void
iosig(signo)
int signo;
{
while (1)
{
int eventmask;
eventmask = wait_for_events(1);
if (eventmask == 0)
return;
if (eventmask & PTY_EVENT)
pty_to_kernel();
if (eventmask & KERNEL_EVENT)
kernel_dispatch(1);
}
}
int
energize_wait(status)
int *status;
{
int pid;
if (!energize)
return wait(status);
signal(SIGIO, iosig);
pid = wait(status);
signal(SIGIO, SIG_DFL);
return pid;
}
static void
null_routine(arg)
int arg;
{
}
/* All requests from the Energize kernel eventually end up here. */
void
energize_main_loop()
{
CTtyRequest *req;
struct cleanup *old_chain;
doing_breakcommands_message = 0;
/* We will come thru here any time there is an error, so send status if
necessary. */
send_status();
print_prompt();
/* The actual event loop! */
while (1)
{
int eventmask;
char *cmd;
old_chain = make_cleanup(null_routine, 0);
/* First, empty out the command queue, then check for new requests. */
while (cmd = dequeue_command())
{
execute_command_1(1, 0, cmd);
free(cmd);
}
eventmask = wait_for_events(0);
if (eventmask & PTY_EVENT)
pty_to_kernel();
if (eventmask & KERNEL_EVENT)
kernel_dispatch(0);
bpstat_do_actions(&stop_bpstat);
do_cleanups(old_chain);
}
}