From c6b2f7585d51164dc32f4dd2a05855913e464c58 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Wed, 30 Sep 2009 08:07:24 +0000 Subject: [PATCH] Make gldbg interactive Separate child process management and tracing into their own classes Use %g format for floats instead of %f Add a class for GlPrint when compiling C++ code Allow a null decoder for gldecoder_decode to determine packet size --- Makefile | 15 +++- genwrap.py | 8 +-- source/commandinterpreter.cpp | 114 +++++++++++++++++++++++++++++ source/commandinterpreter.h | 38 ++++++++++ source/gldbg.cpp | 131 ++++++++++++++++++++++------------ source/gldbg.h | 22 ++++-- source/gldecoder.c | 11 +-- source/glprint.h | 13 ++++ source/process.cpp | 111 ++++++++++++++++++++++++++++ source/process.h | 45 ++++++++++++ source/tracer.cpp | 61 ++++++++++++++++ source/tracer.h | 34 +++++++++ 12 files changed, 539 insertions(+), 64 deletions(-) create mode 100644 source/commandinterpreter.cpp create mode 100644 source/commandinterpreter.h create mode 100644 source/process.cpp create mode 100644 source/process.h create mode 100644 source/tracer.cpp create mode 100644 source/tracer.h diff --git a/Makefile b/Makefile index 55aa80b..736f05a 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ CXXFLAGS = -Igensrc -ggdb PACKAGES_gldbg = mspcore mspstrings mspio mspfs CXXFLAGS_gldbg = $(shell pkg-config --cflags $(PACKAGES_gldbg)) -LIBS_gldbg = $(shell pkg-config --libs $(PACKAGES_gldbg)) +LIBS_gldbg = $(shell pkg-config --libs $(PACKAGES_gldbg)) -lreadline all: glwrap.so gldump gldbg @@ -15,7 +15,7 @@ glwrap.so: source/glwrap.o gldump: source/gldecoder.o source/gldump.o source/glprint.o $(CC) -o $@ $^ $(LIBS) $(LDFLAGS) -gldbg: source/gldbg.o source/gldecoder.o source/glprint.o +gldbg: source/gldbg.o source/gldecoder.o source/glprint.o source/commandinterpreter.o source/tracer.o source/process.o $(CXX) -o $@ $^ $(LIBS_gldbg) $(LIBS) $(LDFLAGS) source/glwrap.o: source/functions.h gensrc/functions.enum gensrc/glwrap.funcs @@ -23,7 +23,16 @@ source/gldecoder.o: source/functions.h gensrc/gldecoder.struct gensrc/gldecoder. source/gldump.o: source/gldecoder.h gensrc/gldecoder.struct source/glprint.h source/glprint.o: gensrc/glprint.funcs gensrc/gldecoder.struct -source/gldbg.o: source/gldbg.cpp source/gldbg.h source/gldecoder.h source/glprint.h +source/gldbg.o: source/gldbg.cpp source/gldbg.h source/gldecoder.h source/tracer.h + $(CXX) -c $(CXXFLAGS) $(CXXFLAGS_gldbg) -o $@ $< + +source/tracer.o: source/tracer.cpp source/gldecoder.h source/glprint.h + $(CXX) -c $(CXXFLAGS) $(CXXFLAGS_gldbg) -o $@ $< + +source/commandinterpreter.o: source/commandinterpreter.cpp source/gldbg.h + $(CXX) -c $(CXXFLAGS) $(CXXFLAGS_gldbg) -o $@ $< + +source/process.o: source/process.cpp source/process.h $(CXX) -c $(CXXFLAGS) $(CXXFLAGS_gldbg) -o $@ $< gensrc/functions.enum gensrc/gldecoder.funcs gensrc/gldecoder.struct gensrc/glwrap.funcs gensrc/glprint.funcs: gensrc/.created genwrap.py gl.spec gl.tm diff --git a/genwrap.py b/genwrap.py index da23cb3..c388f7c 100755 --- a/genwrap.py +++ b/genwrap.py @@ -64,10 +64,10 @@ fmtmap = {"GLenum":"%#x", "GLushort":"%u", "GLuint":"%u", "GLsizei":"%i", - "GLfloat":"%f", - "GLclampf":"%f", - "GLdouble":"%lf", - "GLclampd":"%lf", + "GLfloat":"%g", + "GLclampf":"%g", + "GLdouble":"%lg", + "GLclampd":"%lg", "const GLubyte *":"%s", "GLchar *":"%s", "GLcharARB *":"%s", diff --git a/source/commandinterpreter.cpp b/source/commandinterpreter.cpp new file mode 100644 index 0000000..71e8ee7 --- /dev/null +++ b/source/commandinterpreter.cpp @@ -0,0 +1,114 @@ +/* $Id$ + +This file is part of gldbg +Copyright © 2009 Mikko Rasa, Mikkosoft Productions +Distributed under the GPL +*/ + +#include +#include +#include +#include +#include "commandinterpreter.h" +#include "gldbg.h" +#include "tracer.h" + +using namespace std; +using namespace Msp; + +CommandInterpreter::CommandInterpreter(GlDbg &d): + gldbg(d) +{ + commands["run"] = &CommandInterpreter::cmd_run; + commands["continue"] = &CommandInterpreter::cmd_continue; + commands["c"] = &CommandInterpreter::cmd_continue; + commands["kill"] = &CommandInterpreter::cmd_kill; + commands["signal"] = &CommandInterpreter::cmd_signal; + commands["exit"] = &CommandInterpreter::cmd_exit; + commands["quit"] = &CommandInterpreter::cmd_exit; + commands["trace"] = &CommandInterpreter::cmd_trace; +} + +void CommandInterpreter::execute(const string &cmd) +{ + unsigned space = cmd.find(' '); + string name = cmd.substr(0, space); + CommandMap::const_iterator i = commands.lower_bound(name); + if(i==commands.end() || i->first.compare(0, name.size(), name)) + throw KeyError("Unknown command", name); + if(i->first!=name) + { + CommandMap::const_iterator j = i; + if((++j)!=commands.end() && !j->first.compare(0, name.size(), name)) + throw KeyError("Ambiguous command", name); + } + + string args; + if(space!=string::npos) + args = cmd.substr(space+1); + + (this->*(i->second))(args); +} + + +void CommandInterpreter::cmd_run(const string &) +{ + gldbg.launch(); +} + +void CommandInterpreter::cmd_continue(const string &) +{ + gldbg.get_process().resume(); +} + +void CommandInterpreter::cmd_signal(const string &args) +{ + gldbg.get_process().resume(lexical_cast(args)); +} + +void CommandInterpreter::cmd_kill(const string &) +{ + gldbg.get_process().kill(); +} + +void CommandInterpreter::cmd_exit(const string &) +{ + gldbg.quit(); +} + +void CommandInterpreter::cmd_trace(const string &args) +{ + Tracer &tracer = gldbg.get_tracer(); + if(args[0]=='>') + { + string fn = args.substr(1); + if(fn=="-") + { + tracer.set_output(IO::cout); + IO::print("Tracing to stdout\n"); + } + else + { + tracer.set_output(new IO::File(fn, IO::M_WRITE)); + IO::print("Tracing to %s\n", fn); + } + } + else + { + if(args=="on") + { + tracer.enable(); + IO::print("Tracing enabled\n"); + } + else if(args=="off") + { + tracer.disable(); + IO::print("Tracing disabled\n"); + } + else if(args=="end") + { + tracer.set_output(0); + IO::print("Tracing terminated\n"); + } + } +} diff --git a/source/commandinterpreter.h b/source/commandinterpreter.h new file mode 100644 index 0000000..8ce59d0 --- /dev/null +++ b/source/commandinterpreter.h @@ -0,0 +1,38 @@ +/* $Id$ + +This file is part of gldbg +Copyright © 2009 Mikko Rasa, Mikkosoft Productions +Distributed under the GPL +*/ + +#ifndef COMMANDINTERPRETER_H_ +#define COMMANDINTERPRETER_H_ + +#include +#include + +class GlDbg; + +class CommandInterpreter +{ +private: + typedef void (CommandInterpreter::*CommandFunc)(const std::string &); + typedef std::map CommandMap; + + GlDbg &gldbg; + CommandMap commands; + +public: + CommandInterpreter(GlDbg &); + void execute(const std::string &); + +private: + void cmd_run(const std::string &); + void cmd_continue(const std::string &); + void cmd_signal(const std::string &); + void cmd_kill(const std::string &); + void cmd_exit(const std::string &); + void cmd_trace(const std::string &); +}; + +#endif diff --git a/source/gldbg.cpp b/source/gldbg.cpp index f06496d..5a4ad19 100644 --- a/source/gldbg.cpp +++ b/source/gldbg.cpp @@ -6,11 +6,11 @@ Distributed under the GPL */ #include -#include -#include +#include +#include #include #include -#include +#include #include #include #include @@ -24,37 +24,94 @@ using namespace Msp; Application::RegApp GlDbg::reg; GlDbg::GlDbg(int argc, char **argv): - pid(0), - sock_fd(-1), - glprint(glprint_new(0, 16384)) + cmd_interp(*this), + process(vector(argv+1, argv+argc)), + flushing(false), + got_sigchld(false) { - libdir = FS::get_sys_lib_dir(argv[0], "gldbg"); - args.assign(argv+1, argv+argc); + FS::Path libdir = FS::get_sys_lib_dir(argv[0], "gldbg"); + process.setenv("LD_PRELOAD", (libdir/"glwrap.so").str().c_str()); } int GlDbg::main() { catch_signal(SIGINT); - launch(); + catch_signal(SIGCHLD); + set_loop_mode(TICK_BUSY); Application::main(); } +void GlDbg::launch() +{ + if(process.get_state()!=Process::INACTIVE) + throw InvalidState("Program is already running"); + + int fds[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, fds); + sock_fd = fds[0]; + + int flags = fcntl(sock_fd, F_GETFD); + fcntl(sock_fd, F_SETFD, flags|FD_CLOEXEC); + + process.setenv("GLWRAP_FD", lexical_cast(fds[1])); + process.launch(); + close(fds[1]); +} + +void GlDbg::quit() +{ + if(process.get_state()!=Process::INACTIVE) + throw InvalidState("Program is still running"); + exit(0); +} + void GlDbg::tick() { - int status; - int ret = waitpid(pid, &status, WNOHANG); - if(ret==pid) + if(got_sigchld) { - if(WIFEXITED(status) || WIFSIGNALED(status)) + got_sigchld = false; + int ret = process.check(); + if(ret==0x100) + IO::print("Target process exited normally\n"); + else if((ret>>8)==1) + IO::print("Target process exited with status %d\n", ret&0xFF); + else if((ret>>8)==2) + IO::print("Target process terminated with signal %d\n", ret&0xFF); + else if((ret>>8)==3) { - IO::print("Target process exited\n"); - exit(0); + IO::print("Target process stopped by signal %d\n", ret&0xFF); + flushing = true; + } + } + + Process::State pstate = process.get_state(); + if((pstate!=Process::INACTIVE && pstate!=Process::STOPPED) || flushing) + read_stream(); + else + { + char *line = readline("gldbg> "); + if(line) + { + try + { + cmd_interp.execute(line); + } + catch(const Exception &e) + { + IO::print("%s\n", e.what()); + } + free(line); } + else if(pstate==Process::INACTIVE) + exit(0); } +} +void GlDbg::read_stream() +{ pollfd pfd = { sock_fd, POLLIN, 0 }; - ret = poll(&pfd, 1, -1); + int ret = poll(&pfd, 1, (flushing ? 0 : -1)); if(ret>0) { char rbuf[1024]; @@ -64,11 +121,13 @@ void GlDbg::tick() buffer.append(rbuf, ret); while(buffer.size()>buf_offset) { - int ret = gldecoder_decode(glprint, buffer.data()+buf_offset, buffer.size()-buf_offset); - if(ret<0) + const char *data = buffer.data()+buf_offset; + unsigned len = buffer.size()-buf_offset; + int size = gldecoder_decode(0, data, len); + if(size<0) break; - buf_offset += ret; - IO::print("%s\n", glprint_get_buffer(glprint)); + tracer.decode(data, len); + buf_offset += size; } if(buf_offset>8192) { @@ -77,34 +136,12 @@ void GlDbg::tick() } } } + else if(flushing) + flushing = false; } -void GlDbg::launch() +void GlDbg::sighandler(int sig) { - int fds[2]; - socketpair(AF_UNIX, SOCK_STREAM, 0, fds); - sock_fd = fds[0]; - - pid = fork(); - if(pid==0) - { - string fd_str = lexical_cast(fds[1]); - setenv("LD_PRELOAD", (libdir/"glwrap.so").str().c_str(), true); - setenv("GLWRAP_FD", fd_str.c_str(), true); - close(fds[0]); - std::vector argv(args.size()+1); - for(unsigned i=0; i0) - { - close(fds[1]); - } - else - { - throw SystemError("Could not launch process", errno); - } + if(sig==SIGCHLD) + got_sigchld = true; } diff --git a/source/gldbg.h b/source/gldbg.h index fce7d7f..5f75164 100644 --- a/source/gldbg.h +++ b/source/gldbg.h @@ -12,27 +12,37 @@ Distributed under the GPL #include #include #include -#include "gldecoder.h" +#include "commandinterpreter.h" +#include "process.h" +#include "tracer.h" class GlDbg: public Msp::Application { private: - Msp::FS::Path libdir; - int pid; - std::vector args; + CommandInterpreter cmd_interp; + Process process; int sock_fd; std::string buffer; unsigned buf_offset; - GlDecoder *glprint; + bool flushing; + Tracer tracer; + bool got_sigchld; static RegApp reg; public: GlDbg(int, char **); int main(); + Tracer &get_tracer() { return tracer; } + Process &get_process() { return process; } + void launch(); + void quit(); private: void tick(); - void launch(); + void check_child(); + void read_stream(); + long ptrace(int, void *, void *); + virtual void sighandler(int); }; #endif diff --git a/source/gldecoder.c b/source/gldecoder.c index 85d089d..e11d13c 100644 --- a/source/gldecoder.c +++ b/source/gldecoder.c @@ -46,10 +46,13 @@ int gldecoder_decode(GlDecoder *dec, const char *data, unsigned len) if(len +#include +#include +#include +#include +#include +#include "process.h" + +using namespace std; +using namespace Msp; + +Process::Process(const vector &a): + args(a), + pid(-1), + state(INACTIVE) +{ } + +void Process::setenv(const string &key, const string &value) +{ + env[key] = value; +} + +void Process::launch() +{ + if(state!=INACTIVE) + throw InvalidState("Program is already running"); + + pid = fork(); + if(pid==0) + { + for(map::const_iterator i=env.begin(); i!=env.end(); ++i) + ::setenv(i->first.c_str(), i->second.c_str(), true); + std::vector argv(args.size()+1); + for(unsigned i=0; i0) + state = STARTING; + else + throw SystemError("Could not launch process", errno); +} + +int Process::check() +{ + int status; + int ret = waitpid(pid, &status, WNOHANG); + if(ret==pid) + { + if(WIFEXITED(status)) + { + int code = WEXITSTATUS(status); + state = INACTIVE; + return 0x100|code; + } + else if(WIFSIGNALED(status)) + { + state = INACTIVE; + return 0x200|WTERMSIG(status); + } + else if(WIFSTOPPED(status)) + { + int sig = WSTOPSIG(status); + if(sig==SIGTRAP && state==STARTING) + { + ptrace(PTRACE_CONT, 0, 0); + state = RUNNING; + } + else + { + state = STOPPED; + return 0x300|sig; + } + } + } + + return 0; +} + +void Process::resume(int sig) +{ + if(state!=STOPPED) + throw InvalidState("Program is not stopped"); + ptrace(PTRACE_CONT, 0, (void *)sig); + state = RUNNING; +} + +void Process::kill() +{ + if(state==INACTIVE) + throw InvalidState("Program is not running"); + ptrace(PTRACE_KILL, 0, 0); +} + +long Process::ptrace(int req, void *addr, void *data) +{ + int ret = ::ptrace((__ptrace_request)req, pid, addr, data); + if(ret==-1 && ((req!=PTRACE_PEEKTEXT && req!=PTRACE_PEEKDATA && req!=PTRACE_PEEKUSER) || errno)) + throw SystemError("ptrace error", errno); + return ret; +} diff --git a/source/process.h b/source/process.h new file mode 100644 index 0000000..847b845 --- /dev/null +++ b/source/process.h @@ -0,0 +1,45 @@ +/* $Id$ + +This file is part of gldbg +Copyright © 2009 Mikko Rasa, Mikkosoft Productions +Distributed under the GPL +*/ + +#ifndef PROCESS_H_ +#define PROCESS_H_ + +#include +#include +#include + +class Process +{ +public: + enum State + { + INACTIVE, + STARTING, + RUNNING, + STOPPED + }; + +private: + std::vector args; + std::map env; + int pid; + State state; + +public: + Process(const std::vector &); + void setenv(const std::string &, const std::string &); + void launch(); + int check(); + void stop(); + void resume(int = 0); + void kill(); + State get_state() const { return state; } +private: + long ptrace(int, void *, void *); +}; + +#endif diff --git a/source/tracer.cpp b/source/tracer.cpp new file mode 100644 index 0000000..7ce00fe --- /dev/null +++ b/source/tracer.cpp @@ -0,0 +1,61 @@ +/* $Id$ + +This file is part of gldbg +Copyright © 2009 Mikko Rasa, Mikkosoft Productions +Distributed under the GPL +*/ + +#include +#include "glprint.h" +#include "tracer.h" + +using namespace Msp; + +Tracer::Tracer(): + glprint(new GlPrint(0, 16384)), + out(0), + delete_out(true), + enabled(true) +{ } + +Tracer::~Tracer() +{ + if(delete_out) + delete out; +} + +void Tracer::set_output(IO::Base *o) +{ + if(delete_out) + delete out; + out = o; + delete_out = true; +} + +void Tracer::set_output(IO::Base &o) +{ + set_output(&o); + delete_out = false; +} + +void Tracer::enable() +{ + if(!out) + throw InvalidState("Output is not set"); + enabled = true; +} + +void Tracer::disable() +{ + enabled = false; +} + +int Tracer::decode(const char *data, unsigned len) +{ + if(!enabled || !out) + return 0; + int ret = glprint->decode(data, len); + if(ret>=0) + IO::print(*out, "%s\n", glprint->get_buffer()); + return ret; +} diff --git a/source/tracer.h b/source/tracer.h new file mode 100644 index 0000000..51adb39 --- /dev/null +++ b/source/tracer.h @@ -0,0 +1,34 @@ +/* $Id$ + +This file is part of gldbg +Copyright © 2009 Mikko Rasa, Mikkosoft Productions +Distributed under the GPL +*/ + +#ifndef TRACER_H_ +#define TRACER_H_ + +#include + +struct GlPrint; + +class Tracer +{ +private: + GlPrint *glprint; + Msp::IO::Base *out; + bool delete_out; + bool enabled; + +public: + Tracer(); + ~Tracer(); + + void set_output(Msp::IO::Base *); + void set_output(Msp::IO::Base &); + void enable(); + void disable(); + int decode(const char *, unsigned); +}; + +#endif -- 2.43.0