-/* $Id$
-
-This file is part of gldbg
-Copyright © 2009-2010 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)
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)
" execution. Currently recognized signal names are HUP, INT, TERM, SEGV\n"
" and TERM.\n");
- register_command("trace", this, &CommandInterpreter::cmd_trace)
- .set_help("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");
-
- register_command("profile", this, &CommandInterpreter::cmd_profile)
- .set_help("Profiles GL usage and performance",
- "profile {on|off}\n"
- " Enables or disables profiling\n");
-
- register_command("state", this, &CommandInterpreter::cmd_state)
- .set_help("Inspects general GL state",
- "state vertex\n"
- " Print current vertex attributes\n\n"
- "state bind\n"
- " Show current bindings\n");
-
- register_command("texture", this, &CommandInterpreter::cmd_texture)
- .set_help("Inspect texture state",
- "texture\n"
- " Lists texture objects\n\n"
- "texture ID\n"
- " Print information about a texture object\n");
-
- register_command("buffer", this, &CommandInterpreter::cmd_buffer)
- .set_help("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;
{
for(CommandMap::const_iterator i=commands.begin(); i!=commands.end(); ++i)
if(!dynamic_cast<const CommandAlias *>(i->second))
- IO::print("%-10s : %s\n", i->first, i->second->get_description());
+ printf("%-10s : %s\n", i->first.c_str(), i->second->get_description().c_str());
}
else
{
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(const CommandAlias *alias = dynamic_cast<const CommandAlias *>(cmd))
cmd = alias->get_target();
- IO::print("%s : %s\n", i->first, cmd->get_description());
+ printf("%s : %s\n", i->first.c_str(), cmd->get_description().c_str());
if(!cmd->get_help().empty())
- IO::print("\n%s", cmd->get_help());
+ 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)
-{
- Profiler &profiler = gldbg.get_profiler();
- if(args.empty() || args=="on")
- profiler.enable();
- else if(args=="off")
- profiler.disable();
- else
- throw InvalidParameterValue("Invalid argument");
-}
-
-void CommandInterpreter::cmd_state(const string &args)
-{
- 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, ""));
- if(buf.content.stride)
- {
- IO::print(" Stride: %d bytes\n", buf.content.stride);
-
- IO::print(" Arrays:\n");
- const vector<BufferContent::Array> &arrays = buf.content.arrays;
- for(vector<BufferContent::Array>::const_iterator i=arrays.begin(); i!=arrays.end(); ++i)
- {
- if(i->kind)
- IO::print(" %2d: %s, %d %s\n", i->offset,
- describe_enum(i->kind, ""), i->size, describe_enum(i->type, "DataType"));
- else
- IO::print(" %2d: Attrib %d, %d %s\n", i->offset,
- i->index, i->size, describe_enum(i->type, "DataType"));
- }
-
- IO::print(" Data:\n");
- string header;
- for(vector<BufferContent::Array>::const_iterator i=arrays.begin(); i!=arrays.end(); ++i)
- {
- if(!header.empty())
- header += " | ";
-
- string label;
- if(i->kind==GL_VERTEX_ARRAY)
- label = "Vertex";
- else if(i->kind==GL_NORMAL_ARRAY)
- label = "Normal";
- else if(i->kind==GL_COLOR_ARRAY)
- label = "Color";
- else if(i->kind==GL_TEXTURE_COORD_ARRAY)
- {
- if(i->size==1)
- label = "TexC";
- else
- label = "TexCoord";
- }
- else if(!i->kind)
- {
- if(i->size==1)
- label = format("A %d", i->index);
- else
- label = format("Attrib %d", i->index);
- }
-
- unsigned width = i->size;
- if(i->type==GL_FLOAT)
- width *= 5;
- else if(i->type==GL_UNSIGNED_BYTE)
- width *= 3;
- width += i->size-1;
-
- header.append((width-label.size())/2, ' ');
- header += label;
- header.append((width-label.size()+1)/2, ' ');
- }
- IO::print(" %s\n", header);
-
- unsigned n_verts = buf.size/buf.content.stride;
- for(unsigned i=0; i<n_verts; ++i)
- {
- const char *vertex = buf.data+i*buf.content.stride;
-
- string line;
- for(vector<BufferContent::Array>::const_iterator j=arrays.begin(); j!=arrays.end(); ++j)
- {
- if(!line.empty())
- line += " |";
-
- const char *base = vertex+j->offset;
- for(unsigned k=0; k<j->size; ++k)
- {
- if(j->type==GL_FLOAT)
- line += format(" %5.2f", *(reinterpret_cast<const float *>(base)+k));
- else if(j->type==GL_UNSIGNED_BYTE)
- line += format(" %3u", *(reinterpret_cast<const unsigned char *>(base)+k));
- }
- }
-
- IO::print("%3d:%s\n", i, line);
- }
- }
- }
-}
-
void CommandInterpreter::Command::set_help(const string &d)
{