Implemented it in GDB :lol:
I will have to study CDB a little more before I can implement it there too...
I have not committed it yet because I was in the middle of some other debugger changes. I will finish them all and then commit.
Here's the script that adds wxString support in GDB:
////////////////////////////////////////////////////////////////////////////////
// Parses GDB's output of unicode wxString and turns it to human-readable
////////////////////////////////////////////////////////////////////////////////
//
// Example input:
// {38 '&', 69 'E', 110 'n', 97 'a', 98 'b', 108 'l', 101 'e'}
//
// Example output:
// "&Enable"
////////////////////////////////////////////////////////////////////////////////
// Entry point for testing.
// This is here only for testing the parsing function inside the IDE (while writing it).
// It is *not* used when the script runs in the debugger...
// So, it can safely be removed.
int main()
{
wxString r;
GDB_ParseWXString("{38 '&', 69 'E', 110 'n', 97 'a', 98 'b', 108 'l', 101 'e'}", r);
Log(r);
return 0;
}
void RegisterTypes(DebuggerDriver@ driver)
{
driver.RegisterType(
// The type's name (must be unique, the debugger driver won't accept duplicates).
"wxString",
// Regular expression for type matching.
"[^[:alnum:]_]*wxString[^[:alnum:]_]*",
// Parser function's name (defined below).
"GDB_ParseWXString",
// Define the print function body (this will become a GDB function named print_wxstring).
// Note that we 're using the m_pchData member of wxString to access
// its actual data...
// Also note that we 'll be printing at most 100 chars, i.e. we 're setting a limit.
"output /c (*$arg0.m_pchData)@(($slen=(unsigned int)$arg0.Len())>100?100:$slen)"
);
}
// This function parses GDB's output.
// When it returns, the "result" argument contains the parsing result.
void GDB_ParseWXString(const wxString& in a_str, wxString& out result)
{
result = "\"";
uint len = a_str.length();
uint c = 0;
while (c < len)
{
switch (a_str[c])
{
case '\'':
++c;
while (c < len)
{
switch (a_str[c])
{
case '\\':
result += a_str[c++];
result += a_str[c++];
break;
default:
result += a_str[c++];
break;
}
if (a_str[c] == '\'')
break;
}
break;
default:
break;
}
++c;
}
result += "\"";
}
Implemented it in GDB :lol:
I will have to study CDB a little more before I can implement it there too...
I have not committed it yet because I was in the middle of some other debugger changes. I will finish them all and then commit.
Here's the script that adds wxString support in GDB:
**snip**
Some points:
Combining some of the above suggestions, maybe the scripts should create a list as output, with elements like (Name, <Watch expression (or string)>). For instance the vector example would output something like (("[0]", <string at address0>), ("[1]", <string at address1>), ("[2]", <string at address2>), ("[3]", <string at address3>))
(in a data structure, not a string) and then the debugger driver or whatever calls the scripts can use the script for std::string to produce the representation of each individual string.
Preferably this would of course be done without the script needing to know what a std::string even is.
All the above mean that you can parse practically whatever. If you want, I can post an example for parsing a std::vector. It's perfectly possible with this setup.
Please do.
Here it is:
void RegisterTypes(DebuggerDriver@ driver)
{
driver.RegisterType(
"StdVector",
"[^[:alnum:]_]*vector<.*",
"GDB_ParseStdVector",
"set $vec = ($arg0)\n"
"set $vec_size = $vec->_M_impl._M_finish - $vec->_M_impl._M_start\n"
"if ($vec_size != 0)\n"
" set $i = 0\n"
" while ($i < $vec_size)\n"
" p *($vec->_M_impl._M_start+$i)\n"
" set $i++\n"
" end\n"
"end\n"
);
}
void GDB_ParseStdVector(const wxString& in a_str, wxString& out result)
{
result = "{" + a_str + "}";
result.Replace("\n", ",", true);
}
EDIT: The attached image shows a std::vector<float> being watched.
Still needs some work, but as you see it is possible :)
[attachment deleted by admin]
void RegisterTypes(DebuggerDriver@ driver)
{
driver.RegisterType(
"StdVector",
"[^[:alnum:]_]*vector<.*",
"GDB_ParseStdVector",
"set $vec = ($arg0)\n"
"set $vec_size = $vec->_M_impl._M_finish - $vec->_M_impl._M_start\n"
"if ($vec_size != 0)\n"
" set $i = 0\n"
" while ($i < $vec_size)\n"
" p *($vec->_M_impl._M_start+$i)\n"
" set $i++\n"
" end\n"
"end\n"
);
}
void GDB_ParseStdVector(const wxString& in a_str, wxString& out result)
{
result = "{" + a_str + "}";
result.Replace("\n", ",", true);
}
That seems to do most of the work in some weird gdb script though. It looks like the angelscript is mostly just a wrapper because it needs to be done in angelscript ;).
In the script you posted I noticed you called $arg0.Len(). Does this mean the debugger can call member functions?
Yes, you can call functions, do casts, basically whatever the language allows. As long as the debugger has full info on the type that is...
That opens up some possibilities once the script gets direct access to debugger commands, I think. (Though it's probably possible through that gdb script)
For instance, the use of _M_start isn't portable, but accessing the elements through a standard interface should work for every implementation of std::vector. So []/.operator[]() (if possible), .at() or even iterators (if possible) would make for a much more portable script. And it would be even better if this could be done from a script independent of the compiler and debugger used.
Does this mean these things would have to be rewritten for each debugger?
As it is now, yes. But I plan change this. I 'm rethinking the whole process. In the std::vector example I posted above, I doubt it would work with wxStrings (well it would work, but not using the print_wxstring function).
So maybe I will expose some debugger commands to the scripts so they can call the debugger directly as needed. I 'm thinking it over...
That sounds like a good idea.
Hmm... I don't know much about gdb, but does this mean you could already write such a generic vector parsing script that would be parsed by C::B into a tree representation? (taking into account the scripts for the element type etc.)
See my post above.
But it doesn't use the custom wxString representation, correct?
I'd like vector script to look more like this:
void RegisterTypes(DebuggerDriver@ driver)
{
// Notice: No debugger-dependent code
driver.RegisterType(
"StdVector",
"[^[:alnum:]_]*vector<.*",
"ParseStdVector"
);
}
void ParseStdVector(DebuggerDriver@ driver, wxString vecname, DebuggerTree@ tree)
{
for (size_t i = 0; i < vecname.length(); ++i) {
wxString index;
index << "[" << i << "]";
tree.addChild(index, vecname + index); // (<label>, <watch expression>)
}
}
Which would then work for any type of vector (and use custom representations).
DebuggerTree would be a representation of the tree in the watch window.
Maybe addChild() should have an overload for a single string argument too, to allow complete control over the text being shown.
Note: I don't know much about angelscript, if any syntax is wrong please feel free to ignore that and just get the general idea anyway :P. In particular, if operator overloads don't work please imagine I used .Append() (on copies of the string) and .Printf() instead of << and + ;).
If it's not easy to get vector[0] etc. to be evaluated by the debugger, imagine I used .at() or something.
And if gdb/cdb are advanced enough (especially w.r.t. operator overloads) to allow access to and use of iterators, this might be done in an even cooler way: imagine using only one script for ALL stl containers without special cases being necessary (except maybe std::string and variants, which have a more natural representation). 8)
How does this look?
// Registers new types with driver
void RegisterTypes(DebuggerDriver@ driver)
{
driver.RegisterType(
// The type's name (must be unique, the debugger driver won't accept duplicates).
"STL String",
// Regular expression for type matching.
"[^[:alnum:]_]*string[^[:alnum:]_]*",
// Evaluate function's name (defined below).
"Evaluate_StlString",
// Parser function's name (defined below).
"Parse_StlString"
);
}
// This function tells the driver how to evaluate this type.
// a_str contains the variable and result must contain the debugger's command when it returns.
void Evaluate_StlString(const wxString& in a_str, wxString& out result)
{
result = "output " + a_str + ".c_str()";
}
// This function parses driver's output.
// When it returns, the "result" argument contains the parsing result.
void Parse_StlString(const wxString& in a_str, wxString& out result)
{
// nothing needs to be done
result = a_str;
}