]> git.tdb.fi Git - gldbg.git/blobdiff - source/commandinterpreter.cpp
Add framework necessary to support more modular tools
[gldbg.git] / source / commandinterpreter.cpp
index 36c3b3c18752e32ed2bb2197683a571fc5308ef4..367ac17f9651684e98113d472140cc1b23e4a4d2 100644 (file)
@@ -1,11 +1,12 @@
 /* $Id$
 
 This file is part of gldbg
-Copyright © 2009  Mikko Rasa, Mikkosoft Productions
+Copyright © 2009-2010  Mikko Rasa, Mikkosoft Productions
 Distributed under the GPL
 */
 
 #include <signal.h>
+#include <readline/readline.h>
 #include <msp/core/except.h>
 #include <msp/io/file.h>
 #include <msp/io/print.h>
@@ -22,58 +23,66 @@ 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["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 >FILE\n"
-               "  Send trace output to FILE.  As a special case, - means 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["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");
+       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("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");
+
+       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)
@@ -94,24 +103,30 @@ void CommandInterpreter::execute(const string &cmd)
        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)
-                       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))
+                               IO::print("%-10s : %s\n", i->first, i->second->get_description());
        }
        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);
-               IO::print("%s : %s\n", i->first, i->second.description);
-               if(!i->second.help.empty())
-                       IO::print("\n%s", i->second.help);
+
+               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());
+               if(!cmd->get_help().empty())
+                       IO::print("\n%s", cmd->get_help());
        }
 }
 
@@ -153,46 +168,63 @@ void CommandInterpreter::cmd_kill(const string &)
 
 void CommandInterpreter::cmd_exit(const string &)
 {
-       gldbg.quit();
+       if(gldbg.get_process().get_state()!=Process::INACTIVE)
+       {
+               IO::print("Program is still running.  Kill it?\n");
+               char *answer = readline("[y/n] ");
+               if(answer[0]=='y')
+               {
+                       gldbg.get_process().kill();
+                       gldbg.quit(true);
+               }
+               else
+                       IO::print("Not confirmed.\n");
+       }
+       else
+               gldbg.quit(false);
 }
 
 void CommandInterpreter::cmd_trace(const string &args)
 {
        Tracer &tracer = gldbg.get_tracer();
-       if(args[0]=='>')
+       if(args.empty() || args=="-")
        {
-               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);
-               }
+               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
        {
-               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");
-               }
+               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();
@@ -279,21 +311,109 @@ void CommandInterpreter::cmd_buffer(const string &args)
                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);
+                       }
+               }
        }
 }
 
 
-CommandInterpreter::Command::Command():
-       func(0)
-{ }
+void CommandInterpreter::Command::set_help(const string &d)
+{
+       description = d;
+}
+
+void CommandInterpreter::Command::set_help(const string &d, const string &h)
+{
+       description = d;
+       help = h;
+}
 
-CommandInterpreter::Command::Command(Func f, const string &d):
-       func(f),
-       description(d)
-{ }
 
-CommandInterpreter::Command::Command(Func f, const string &d, const string &h):
-       func(f),
-       description(d),
-       help(h)
+CommandInterpreter::CommandAlias::CommandAlias(Command *t):
+       target(t)
 { }
+
+void CommandInterpreter::CommandAlias::execute(const string &a)
+{
+       target->execute(a);
+}