source/enums.c \
source/arraysize.c \
source/tmpalloc.c \
- source/packet.c
+ source/packet.c \
+ source/functions.c
SOURCES_glwrap := source/glwrap.c \
source/glwrap_funcs.c \
source/arraysize.c \
source/texturestate.cpp \
source/bufferstate.cpp
TEMPLATES := source/functions.enum.t \
+ source/functions.table.t \
source/gldecoder.funcs.t \
source/gldecoder.struct.t \
source/glprint.funcs.t \
#include <dlfcn.h>
#include "arraysize.h"
+#include "breakpoint.h"
#include "functions.h"
#include "glwrap.h"
#include "opengl.h"
#include <dlfcn.h>
#include "arraysize.h"
+#include "breakpoint.h"
#include "functions.h"
#include "glwrap.h"
#include "opengl.h"
--- /dev/null
+/* $Id$
+
+This file is part of gldbg
+Copyright © 2010 Mikko Rasa, Mikkosoft Productions
+Distributed under the GPL
+*/
+
+#ifndef BREAKPOINT_H_
+#define BREAKPOINT_H_
+
+enum
+{
+ BREAK_CALL = 1,
+ BREAK_RETURN = 2
+};
+
+#endif
#include <msp/io/print.h>
#include <msp/strings/lexicalcast.h>
#include <msp/strings/utils.h>
+#include "breakpoint.h"
#include "commandinterpreter.h"
#include "enums.h"
+#include "functions.h"
#include "gldbg.h"
#include "tracer.h"
register_command("run", this, &CommandInterpreter::cmd_run)
.set_help("Starts the program");
+ register_command("break", this, &CommandInterpreter::cmd_break)
+ .set_help("Sets a breakpoint",
+ "break FUNC\n"
+ " Makes program execution stop at FUNC\n");
+ register_command("unbreak", this, &CommandInterpreter::cmd_unbreak)
+ .set_help("Removes a breakpoint",
+ "unbreak FUNC\n"
+ " Makes program execution no longer stop at FUNC\n");
+ register_command("next", this, &CommandInterpreter::cmd_next)
+ .set_help("Resumes program execution until the next function call");
+ commands["step"] = new CommandAlias(commands["next"]);
+ register_command("finish", this, &CommandInterpreter::cmd_finish)
+ .set_help("Resumes program execution until the next function return");
register_command("continue", this, &CommandInterpreter::cmd_continue)
.set_help("Resumes program execution");
register_command("kill", this, &CommandInterpreter::cmd_kill)
gldbg.launch();
}
+void CommandInterpreter::cmd_break(const string &args)
+{
+ unsigned short func = get_function(args.c_str());
+ if(!func)
+ throw InvalidParameterValue("Unknown function");
+
+ gldbg.set_breakpoint(func, BREAK_CALL);
+}
+
+void CommandInterpreter::cmd_unbreak(const string &args)
+{
+ unsigned short func = get_function(args.c_str());
+ if(!func)
+ throw InvalidParameterValue("Unknown function");
+
+ gldbg.clear_breakpoint(func, BREAK_CALL);
+}
+
+void CommandInterpreter::cmd_next(const string &)
+{
+ gldbg.set_breakpoint(0, BREAK_CALL);
+ gldbg.get_process().resume();
+}
+
+void CommandInterpreter::cmd_finish(const string &)
+{
+ gldbg.set_breakpoint(0, BREAK_RETURN);
+ gldbg.get_process().resume();
+}
+
void CommandInterpreter::cmd_continue(const string &)
{
IO::print("Continuing.\n");
private:
void cmd_help(const std::string &);
void cmd_run(const std::string &);
+ void cmd_break(const std::string &);
+ void cmd_unbreak(const std::string &);
+ void cmd_next(const std::string &);
+ void cmd_finish(const std::string &);
void cmd_continue(const std::string &);
void cmd_signal(const std::string &);
void cmd_kill(const std::string &);
--- /dev/null
+/* $Id$
+
+This file is part of gldbg
+Copyright © 2010 Mikko Rasa, Mikkosoft Productions
+Distributed under the GPL
+*/
+
+#include <string.h>
+#include "functions.h"
+
+#include "gensrc/functions.table"
+
+const char *get_function_name(unsigned short func)
+{
+ if(func<N_FUNCS)
+ return func_names[func];
+
+ return NULL;
+}
+
+unsigned short get_function(const char *name)
+{
+ unsigned i;
+ for(i=1; i<N_FUNCS; ++i)
+ if(!strcmp(func_names[i], name))
+ return i;
+
+ return 0;
+}
# $Id$
-:enum Function
+:enum
:{
: FUNC_NONE,
wl(' FUNC_%s,', func.name.upper())
-: FUNC_GLDERROR = 0x8000
+: N_FUNCS,
+: FUNC_GLDERROR = 0x8000,
+: FUNC_GLDBREAK,
+: FUNC_GLDHOLD
:};
/* $Id$
This file is part of gldbg
-Copyright © 2009 Mikko Rasa, Mikkosoft Productions
+Copyright © 2009-2010 Mikko Rasa, Mikkosoft Productions
Distributed under the GPL
*/
#ifndef FUNCTIONS_H_
#define FUNCTIONS_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include "gensrc/functions.enum"
+const char *get_function_name(unsigned short);
+unsigned short get_function(const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif
--- /dev/null
+# $Id$
+:static const char *func_names[N_FUNCS] =
+:{
+: NULL,
+wl(' "%s",', func.name)
+:};
#include <msp/fs/dir.h>
#include <msp/io/print.h>
#include <msp/strings/lexicalcast.h>
+#include "functions.h"
#include "gldbg.h"
#include "gldecoder.h"
+#include "packet.h"
#include "tool.h"
using namespace std;
fcntl(sock_fd, F_SETFD, flags|FD_CLOEXEC);
process.setenv("GLWRAP_FD", lexical_cast(fds[1]));
+ process.setenv("GLWRAP_CTRL_FD", lexical_cast(fds[1]));
process.launch();
close(fds[1]);
}
-
+
+void GlDbg::set_breakpoint(unsigned short func, char flag)
+{
+ GlPacket *pkt = packet_begin(FUNC_GLDBREAK);
+ packet_write_short(pkt, func);
+ packet_write_char(pkt, flag);
+ packet_write_char(pkt, 0);
+ packet_send(pkt, sock_fd);
+}
+
+void GlDbg::clear_breakpoint(unsigned short func, char flag)
+{
+ GlPacket *pkt = packet_begin(FUNC_GLDBREAK);
+ packet_write_short(pkt, func);
+ packet_write_char(pkt, 0);
+ packet_write_char(pkt, flag);
+ packet_send(pkt, sock_fd);
+}
+
void GlDbg::quit(bool force)
{
if(!force && process.get_state()!=Process::INACTIVE)
{
const char *data = buffer.data()+buf_offset;
unsigned len = buffer.size()-buf_offset;
- int size = gldecoder_decode(0, data, len);
- if(size<0)
+ GlPacket *pkt = packet_receive_str(data, &len);
+ if(!pkt)
break;
+
+ unsigned short func;
+ packet_read_short(pkt, (short *)&func);
+ if(func==FUNC_GLDBREAK)
+ {
+ packet_read_short(pkt, (short *)&func);
+ IO::print("Breakpoint: %s\n", get_function_name(func));
+ }
+
for(list<Tool *>::iterator i=tools.begin(); i!=tools.end(); ++i)
- (*i)->decode(data, size);
- buf_offset += size;
+ (*i)->decode(data, len);
+ buf_offset += len;
}
if(buf_offset>8192)
{
CommandInterpreter &get_command_interpreter() { return cmd_interp; }
Process &get_process() { return process; }
void launch();
+ void set_breakpoint(unsigned short, char);
+ void clear_breakpoint(unsigned short, char);
void quit(bool);
private:
void tick();
return len;
}
+typedef const void *pointer;
+typedef const char *string;
+
#include "gensrc/gldecoder.funcs"
static void decode_gldError(GlDecoder *dec, GlPacket *pkt)
#include <errno.h>
#include <dlfcn.h>
#include <fcntl.h>
+#include <signal.h>
+#include "breakpoint.h"
+#include "functions.h"
+#include "packet.h"
+
+static unsigned char breakpoints[N_FUNCS] = { };
+static char break_any = 0;
+static char hold = 0;
static const char *get_lib_names(void)
{
return fd;
}
+
+int get_in_fd(void)
+{
+ static int fd = -1;
+
+ if(fd<0)
+ {
+ const char *var = getenv("GLWRAP_CTRL_FD");
+ if(var)
+ fd = strtol(var, NULL, 0);
+ else
+ fd = 0;
+ }
+
+ return fd;
+}
+
+static void receive_gldBreak(GlPacket *pkt)
+{
+ unsigned short func;
+ unsigned char flags_set;
+ unsigned char flags_clr;
+
+ packet_read_short(pkt, (short *)&func);
+ packet_read_char(pkt, (char *)&flags_set);
+ packet_read_char(pkt, (char *)&flags_clr);
+
+ if(func)
+ {
+ breakpoints[func] &= ~flags_clr;
+ breakpoints[func] |= flags_set;
+ }
+ else
+ break_any = flags_set;
+}
+
+static void receive_gldHold(GlPacket *pkt __attribute__((unused)))
+{
+ hold = 1;
+}
+
+static void receive(void)
+{
+ GlPacket *pkt;
+
+ while((pkt = packet_receive(get_in_fd())))
+ {
+ unsigned short func;
+
+ packet_read_short(pkt, (short *)&func);
+ switch(func)
+ {
+ case FUNC_GLDBREAK: receive_gldBreak(pkt); break;
+ case FUNC_GLDHOLD: receive_gldHold(pkt); break;
+ default:;
+ }
+ }
+}
+
+void tracepoint(unsigned short func, int flag)
+{
+ receive();
+
+ if((breakpoints[func]|break_any)&flag)
+ {
+ GlPacket *pkt;
+
+ pkt = packet_begin(FUNC_GLDBREAK);
+ packet_write_short(pkt, func);
+ packet_send(pkt, get_out_fd());
+
+ break_any = 0;
+
+ while(1)
+ {
+ hold = 0;
+ raise(SIGTRAP);
+ receive();
+ if(!hold)
+ break;
+ }
+ }
+}
wl(' %s ret;', ret.ctype)
wl(' if(!orig)')
wl(' orig = glsym("%s");', func.name)
+wl(' tracepoint(FUNC_%s, BREAK_CALL);', func.name.upper())
wl(' pkt = packet_begin(FUNC_%s);', func.name.upper())
head_sent = False
for p in params:
wl(' orig(%s);', ", ".join([p.name for p in params]))
if not func.name.startswith("glX"):
wl(' check_error();')
-#wl(' receive_packet();')
+wl(' tracepoint(FUNC_%s, BREAK_RETURN);', func.name.upper())
if ret.ctype!='void':
wl(' return ret;')
wl('}')
void *glsym(const char *);
int get_out_fd(void);
+int get_in_fd(void);
+void tracepoint(unsigned short, int);
#endif
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
+#include <unistd.h>
#include "packet.h"
#include "tmpalloc.h"
};
// XXX Should make this stuff truly re-entrant
-static char *out_buffer = 0;
-static struct iovec *iovecs = 0;
+static char *out_buffer = NULL;
+static struct iovec *iovecs = NULL;
static GlPacket packet;
-/*static char *in_buffer = 0;
-static unsigned in_length;*/
+static char *in_buffer = NULL;
+static unsigned in_length;
static void next_vec(GlPacket *pkt)
{
return pkt;
}
+GlPacket *packet_receive(int fd)
+{
+ int ret;
+ fd_set fds;
+ struct timeval tv = { 0, 0 };
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ ret = select(fd+1, &fds, NULL, NULL, &tv);
+ if(ret>0)
+ {
+ if(!in_buffer)
+ in_buffer = (char *)malloc(1024);
+
+ ret = read(fd, in_buffer+in_length, 1024-in_length);
+ if(ret>0)
+ {
+ GlPacket *pkt;
+ unsigned pkt_length;
+
+ in_length += ret;
+
+ pkt_length = in_length;
+ pkt = packet_receive_str(in_buffer, &pkt_length);
+ if(pkt)
+ {
+ in_length -= pkt_length;
+ memmove(in_buffer, in_buffer+pkt_length, in_length);
+ }
+
+ return pkt;
+ }
+ }
+
+ return NULL;
+}
+
static void next_chunk(GlPacket *pkt)
{
GlInPacket *in = &pkt->in;
read_raw(pkt, (char *)v, sizeof(double), 1);
}
-void packet_read_pointer(GlPacket *pkt, pointer *v)
+void packet_read_pointer(GlPacket *pkt, const void **v)
{
- read_raw(pkt, (char *)v, sizeof(pointer), 1);
+ read_raw(pkt, (char *)v, sizeof(const void *), 1);
}
-void packet_read_data(GlPacket *pkt, pointer *v)
+void packet_read_data(GlPacket *pkt, const void **v)
{
GlInPacket *in = &pkt->in;
int vlen;
in->length -= vlen;
}
-void packet_read_string(GlPacket *pkt, string *v)
+void packet_read_string(GlPacket *pkt, const char **v)
{
- packet_read_data(pkt, (pointer *)v);
+ packet_read_data(pkt, (const void **)v);
}
-void packet_read_string_array(GlPacket *pkt, string **v)
+void packet_read_string_array(GlPacket *pkt, const char ***v)
{
int count;
int i;
packet_read_int(pkt, &count);
- *v = (string *)tmpalloc(count*sizeof(string));
+ *v = (const char **)tmpalloc(count*sizeof(const char *));
for(i=0; i<count; ++i)
packet_read_string(pkt, *v+i);
}
#ifndef PACKET_H_
#define PACKET_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct GlPacket;
typedef struct GlPacket GlPacket;
void packet_write_string_array(GlPacket *, const char **, unsigned);
GlPacket *packet_receive_str(const char *, unsigned *);
+GlPacket *packet_receive(int);
void packet_read_char(GlPacket *, char *);
void packet_read_short(GlPacket *, short *);
void packet_read_long_long(GlPacket *, long long *);
void packet_read_float(GlPacket *, float *);
void packet_read_double(GlPacket *, double *);
-typedef const void *pointer;
-void packet_read_pointer(GlPacket *, pointer *);
-void packet_read_data(GlPacket *, pointer *);
-typedef const char *string;
-void packet_read_string(GlPacket *, string *);
-void packet_read_string_array(GlPacket *, string **);
+void packet_read_pointer(GlPacket *, const void **);
+void packet_read_data(GlPacket *, const void **);
+void packet_read_string(GlPacket *, const char **);
+void packet_read_string_array(GlPacket *, const char ***);
+
+#ifdef __cplusplus
+}
+#endif
#endif