/* $Id$
This file is part of gldbg
-Copyright © 2009 Mikko Rasa, Mikkosoft Productions
+Copyright © 2009-2011 Mikko Rasa, Mikkosoft Productions
Distributed under the GPL
*/
+#include <stdexcept>
+#include <cstdlib>
#include <signal.h>
#include <readline/readline.h>
-#include <msp/core/except.h>
-#include <msp/io/file.h>
-#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"
using namespace std;
-using namespace Msp;
CommandInterpreter::CommandInterpreter(GlDbg &d):
gldbg(d)
{
- commands["help"] = Command(&CommandInterpreter::cmd_help,
- "Provides help on commands",
- "help\n"
- " Displays a list of commands\n\n"
- "help COMMAND\n"
- " Gives detailed information on a command\n");
- commands["exit"] = Command(&CommandInterpreter::cmd_exit,
- "Ends the debugging session");
- commands["quit"] = Command(&commands["exit"]);
+ register_command("help", this, &CommandInterpreter::cmd_help)
+ .set_help("Provides help on commands",
+ "help\n"
+ " Displays a list of commands\n\n"
+ "help COMMAND\n"
+ " Gives detailed information on a command\n");
+ register_command("exit", this, &CommandInterpreter::cmd_exit)
+ .set_help("Ends the debugging session");
+ commands["quit"] = new CommandAlias(commands["exit"]);
+
+ 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)
+ .set_help("Terminates the program immediately");
+ register_command("signal", this, &CommandInterpreter::cmd_signal)
+ .set_help("Resumes execution with a signal",
+ "signal NUM\n"
+ "signal NAME\n"
+ " Sends the signal identified by NUM or NAME to the program and resumes\n"
+ " execution. Currently recognized signal names are HUP, INT, TERM, SEGV\n"
+ " and TERM.\n");
- commands["run"] = Command(&CommandInterpreter::cmd_run,
- "Starts the program");
- commands["continue"] = Command(&CommandInterpreter::cmd_continue,
- "Resumes program execution");
- commands["kill"] = Command(&CommandInterpreter::cmd_kill,
- "Terminates the program immediately");
- commands["signal"] = Command(&CommandInterpreter::cmd_signal,
- "Resumes execution with a signal",
- "signal NUM\n"
- "signal NAME\n"
- " Sends the signal identified by NUM or NAME to the program and resumes\n"
- " execution. Currently recognized signal names are HUP, INT, TERM, SEGV\n"
- " and TERM.\n");
-
- commands["trace"] = Command(&CommandInterpreter::cmd_trace,
- "Traces GL function calls",
- "trace\n"
- " Send trace output to stdout.\n\n"
- "trace FILE\n"
- " Send trace output to FILE (- for stdout).\n\n"
- "trace {off|on}\n"
- " Temporarily suspend or resume trace without closing the file.\n\n"
- "trace end\n"
- " Terminate trace, closing the file.\n");
-
- commands["profile"] = Command(&CommandInterpreter::cmd_profile,
- "Profiles GL usage and performance",
- "profile {on|off}\n"
- " Enables or disables profiling\n");
-
- commands["state"] = Command(&CommandInterpreter::cmd_state,
- "Inspects general GL state",
- "state vertex\n"
- " Print current vertex attributes\n\n"
- "state bind\n"
- " Show current bindings\n");
-
- commands["texture"] = Command(&CommandInterpreter::cmd_texture,
- "Inspect texture state",
- "texture\n"
- " Lists texture objects\n\n"
- "texture ID\n"
- " Print information about a texture object\n");
-
- commands["buffer"] = Command(&CommandInterpreter::cmd_buffer,
- "Inspect buffer object state",
- "buffer\n"
- " Lists buffer objects\n\n"
- "buffer ID\n"
- " Print information about a buffer object\n");
}
void CommandInterpreter::execute(const string &cmd)
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);
+ throw runtime_error("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);
+ throw runtime_error("Ambiguous command "+name);
}
string args;
if(space!=string::npos)
args = cmd.substr(space+1);
- (this->*(i->second.func))(args);
+ i->second->execute(args);
}
void CommandInterpreter::cmd_help(const string &args)
{
if(args.empty())
{
- for(map<string, Command>::const_iterator i=commands.begin(); i!=commands.end(); ++i)
- if(!i->second.alias_for)
- IO::print("%-10s : %s\n", i->first, i->second.description);
+ for(CommandMap::const_iterator i=commands.begin(); i!=commands.end(); ++i)
+ if(!dynamic_cast<const CommandAlias *>(i->second))
+ printf("%-10s : %s\n", i->first.c_str(), i->second->get_description().c_str());
}
else
{
- map<string, Command>::const_iterator i = commands.find(args);
+ CommandMap::const_iterator i = commands.find(args);
if(i==commands.end())
- throw KeyError("Unknown command", args);
+ throw runtime_error("Unknown command "+args);
- const Command *cmd = &i->second;
- while(cmd->alias_for)
- cmd = cmd->alias_for;
+ const Command *cmd = i->second;
+ while(const CommandAlias *alias = dynamic_cast<const CommandAlias *>(cmd))
+ cmd = alias->get_target();
- IO::print("%s : %s\n", i->first, cmd->description);
- if(!i->second.help.empty())
- IO::print("\n%s", cmd->help);
+ printf("%s : %s\n", i->first.c_str(), cmd->get_description().c_str());
+ if(!cmd->get_help().empty())
+ printf("\n%s", cmd->get_help().c_str());
}
}
gldbg.launch();
}
+void CommandInterpreter::cmd_break(const string &args)
+{
+ unsigned short func = get_function(args.c_str());
+ if(!func)
+ throw runtime_error("Unknown function");
+
+ gldbg.set_breakpoint(func, BREAK_CALL, 0);
+}
+
+void CommandInterpreter::cmd_unbreak(const string &args)
+{
+ unsigned short func = get_function(args.c_str());
+ if(!func)
+ throw runtime_error("Unknown function");
+
+ gldbg.clear_breakpoint(func, BREAK_CALL, 0);
+}
+
+void CommandInterpreter::cmd_next(const string &)
+{
+ gldbg.set_breakpoint(0, BREAK_CALL, 0);
+ gldbg.get_process().resume();
+}
+
+void CommandInterpreter::cmd_finish(const string &)
+{
+ gldbg.set_breakpoint(0, BREAK_RETURN, 0);
+ gldbg.get_process().resume();
+}
+
void CommandInterpreter::cmd_continue(const string &)
{
- IO::print("Continuing.\n");
+ printf("Continuing.\n");
gldbg.get_process().resume();
}
sig = SIGSEGV;
else if(args=="TERM" || args=="SIGTERM")
sig = SIGTERM;
- else if(isnumrc(args))
- sig = lexical_cast<unsigned>(args);
else
- throw InvalidParameterValue("Invalid signal specification");
+ {
+ char *end;
+ sig = strtoul(args.c_str(), &end, 0);
+ if(end && *end)
+ throw runtime_error("Invalid signal specification");
+ }
gldbg.get_process().resume(sig);
}
{
if(gldbg.get_process().get_state()!=Process::INACTIVE)
{
- IO::print("Program is still running. Kill it?\n");
+ printf("Program is still running. Kill it?\n");
char *answer = readline("[y/n] ");
if(answer[0]=='y')
{
gldbg.quit(true);
}
else
- IO::print("Not confirmed.\n");
+ printf("Not confirmed.\n");
}
else
gldbg.quit(false);
}
-void CommandInterpreter::cmd_trace(const string &args)
-{
- Tracer &tracer = gldbg.get_tracer();
- if(args.empty() || args=="-")
- {
- tracer.set_output(IO::cout);
- IO::print("Tracing to stdout\n");
- }
- 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");
- }
- else
- {
- tracer.set_output(new IO::File(args, IO::M_WRITE));
- IO::print("Tracing to %s\n", args);
- }
-}
-void CommandInterpreter::cmd_profile(const string &args)
+void CommandInterpreter::Command::set_help(const string &d)
{
- Profiler &profiler = gldbg.get_profiler();
- if(args.empty() || args=="on")
- profiler.enable();
- else if(args=="off")
- profiler.disable();
- else
- throw InvalidParameterValue("Invalid argument");
+ description = d;
}
-void CommandInterpreter::cmd_state(const string &args)
+void CommandInterpreter::Command::set_help(const string &d, const string &h)
{
- const GlState &glstate = gldbg.get_glstate();
- if(args=="vertex")
- {
- IO::print("Current vertex attributes:\n");
- const Vector4 &color = glstate.get_color();
- IO::print(" Color: [%05.3f, %05.3f, %05.3f, %05.3f]\n", color.r, color.g, color.b, color.a);
- for(unsigned i=0; i<8; ++i)
- {
- const Vector4 &texcoord = glstate.get_texcoord(i);
- IO::print(" TexCoord%d: [%05.3f, %05.3f, %05.3f, %05.3f]\n", i, texcoord.s, texcoord.t, texcoord.p, texcoord.q);
- }
- const Vector3 &normal = glstate.get_normal();
- IO::print(" Normal: [%05.3f, %05.3f, %05.3f]\n", normal.x, normal.y, normal.z);
- }
- else if(args=="bind")
- {
- IO::print("Current bindings:\n");
- for(unsigned i=0; i<8; ++i)
- {
- IO::print(" Texture unit %d:\n", i);
- const TexUnitState &unit = glstate.get_texture_unit(i);
- IO::print(" GL_TEXTURE_2D: %s\n", unit.describe_binding(GL_TEXTURE_2D));
- IO::print(" GL_TEXTURE_3D: %s\n", unit.describe_binding(GL_TEXTURE_3D));
- }
- IO::print(" Buffers:\n");
- const BufferState *buf = glstate.get_current_buffer(GL_ARRAY_BUFFER);
- IO::print(" GL_ARRAY_BUFFER: %d\n", (buf ? buf->id : 0));
- buf = glstate.get_current_buffer(GL_ELEMENT_ARRAY_BUFFER);
- IO::print(" GL_ELEMENT_ARRAY_BUFFER: %d\n", (buf ? buf->id : 0));
- }
- else
- throw InvalidParameterValue("Invalid or missing argument");
-}
-
-void CommandInterpreter::cmd_texture(const string &args)
-{
- if(args.empty())
- {
- const map<unsigned, TextureState> &textures = gldbg.get_glstate().get_textures();
- IO::print("%d texture objects:\n", textures.size());
- for(map<unsigned, TextureState>::const_iterator i = textures.begin(); i!=textures.end(); ++i)
- {
- const TextureState &tex = i->second;
- IO::print(" %d: %s, %d images\n", i->first, tex.describe(), tex.images.size());
- }
- }
- else
- {
- unsigned id = lexical_cast<unsigned>(args);
- const TextureState &tex = gldbg.get_glstate().get_texture(id);
- IO::print("Texture object %d\n", id);
- IO::print(" Target: %s\n", describe_enum(tex.target, "TextureTarget"));
- IO::print(" Images:\n");
- for(unsigned i=0; i<tex.images.size(); ++i)
- {
- const TexImageState &img = tex.images[i];
- IO::print(" Level %2d: %s\n", i, img.describe());
- }
- IO::print(" Min. filter: %s\n", describe_enum(tex.min_filter, "TextureMinFilter"));
- IO::print(" Mag. filter: %s\n", describe_enum(tex.mag_filter, "TextureMagFilter"));
- IO::print(" Wrap modes: S=%s / T=%s / R=%s\n", describe_enum(tex.wrap_s, "TextureWrapMode"),
- describe_enum(tex.wrap_t, "TextureWrapMode"), describe_enum(tex.wrap_r, "TextureWrapMode"));
- IO::print(" Compare mode: %s\n", describe_enum(tex.compare_mode, ""));
- IO::print(" Compare func: %s\n", describe_enum(tex.compare_func, "DepthFunction"));
- IO::print(" Generate mipmap: %s\n", tex.generate_mipmap);
- }
-}
-
-void CommandInterpreter::cmd_buffer(const string &args)
-{
- if(args.empty())
- {
- const GlState::BufferMap &buffers = gldbg.get_glstate().get_buffers();
- IO::print("%d buffers:\n", buffers.size());
- for(GlState::BufferMap::const_iterator i=buffers.begin(); i!=buffers.end(); ++i)
- IO::print(" %d: %s\n", i->first, i->second.describe());
- }
- else
- {
- unsigned id = lexical_cast<unsigned>(args);
- const BufferState &buf = gldbg.get_glstate().get_buffer(id);
- IO::print("Buffer %d:\n", id);
- IO::print(" Size: %d bytes\n", buf.size);
- IO::print(" Usage: %s\n", describe_enum(buf.usage, ""));
- }
+ description = d;
+ help = h;
}
-CommandInterpreter::Command::Command():
- func(0),
- alias_for(0)
+CommandInterpreter::CommandAlias::CommandAlias(Command *t):
+ target(t)
{ }
-CommandInterpreter::Command::Command(Command *cmd):
- func(cmd->func),
- alias_for(cmd)
-{ }
-
-CommandInterpreter::Command::Command(Func f, const string &d):
- func(f),
- description(d),
- alias_for(0)
-{ }
-
-CommandInterpreter::Command::Command(Func f, const string &d, const string &h):
- func(f),
- description(d),
- help(h),
- alias_for(0)
-{ }
+void CommandInterpreter::CommandAlias::execute(const string &a)
+{
+ target->execute(a);
+}