Ide/Debuggers/Gdb_MI2 : many enhancements and speedups - still experimental

git-svn-id: svn://ultimatepp.org/upp/trunk@6910 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
micio 2014-02-11 20:52:40 +00:00
parent 91a665d8d4
commit fcbd6ec6eb
8 changed files with 896 additions and 140 deletions

View file

@ -11,6 +11,8 @@ file
Gdb.lay,
MIValue.h,
MIValue.cpp,
TypeSimplify.cpp,
VarItem.cpp,
Terminal.cpp,
Disas.cpp,
Dbg.cpp,

View file

@ -116,6 +116,7 @@ bool Gdb_MI2::RunTo()
Run();
if(df)
disas.SetFocus();
return true;
}
@ -154,6 +155,7 @@ void Gdb_MI2::Run()
ReadGdb(false);
}
Unlock();
if(stopped)
CheckStopReason();
@ -167,6 +169,8 @@ void Gdb_MI2::AsyncBrk()
// send an interrupt command to all running threads
StopAllThreads();
dataSynced = false;
// gdb usually returns to command prompt instantly, BEFORE
// giving out stop reason, which we need. So, we wait some
// milliseconds and re-read (non blocking) GDB output to get it
@ -196,18 +200,33 @@ bool Gdb_MI2::IsFinished()
bool Gdb_MI2::Tip(const String& exp, CodeEditor::MouseTip& mt)
{
/*
// quick exit
if(exp.IsEmpty() || !dbg)
return false;
int idx = localVarNames.Find(exp);
String nam, val;
if(idx < 0)
return false;
{
idx = thisNames.Find(exp);
if(idx < 0)
return false;
nam = thisFullNames[idx];
val = thisValues[idx];
}
else
{
nam = exp;
val = localVarValues[idx];
}
mt.display = &StdDisplay();
mt.value = exp + "=" + localVarValues[idx];
mt.value = nam + "=" + val;
mt.sz = mt.display->GetStdSize(String(mt.value) + "X");
return true;
*/
return false;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -249,7 +268,6 @@ Gdb_MI2::Gdb_MI2()
autos.NoHeader();
autos.AddColumn("", 1);
autos.AddColumn("", 10);
autos.AddColumn("", 3);
autos.WhenLeftDouble = THISBACK1(onExploreExpr, &autos);
autos.EvenRowColor();
autos.OddRowColor();
@ -257,15 +275,20 @@ Gdb_MI2::Gdb_MI2()
locals.NoHeader();
locals.AddColumn("", 1);
locals.AddColumn("", 10);
locals.AddColumn("", 3);
locals.WhenLeftDouble = THISBACK1(onExploreExpr, &locals);
locals.EvenRowColor();
locals.OddRowColor();
members.NoHeader();
members.AddColumn("", 3);
members.AddColumn("", 10);
members.WhenLeftDouble = THISBACK1(onExploreExpr, &members);
members.EvenRowColor();
members.OddRowColor();
watches.NoHeader();
watches.AddColumn("", 1).Edit(watchedit);
watches.AddColumn("", 10);
watches.AddColumn("", 3);
watches.Inserting().Removing();
watches.WhenLeftDouble = THISBACK1(onExploreExpr, &watches);
watches.EvenRowColor();
@ -293,6 +316,7 @@ Gdb_MI2::Gdb_MI2()
Add(tab.SizePos());
tab.Add(autos.SizePos(), "Autos");
tab.Add(locals.SizePos(), t_("Locals"));
tab.Add(members.SizePos(), t_("This"));
tab.Add(watches.SizePos(), t_("Watches"));
tab.Add(explorerPane.SizePos(), t_("Explorer"));
@ -323,6 +347,7 @@ Gdb_MI2::Gdb_MI2()
stopped = false;
periodic.Set(-50, THISBACK(Periodic));
dataSynced = false;
}
Gdb_MI2::~Gdb_MI2()
@ -816,11 +841,15 @@ void Gdb_MI2::StopAllThreads(void)
// reselect current thread as it was before stopping all others
if(current != "")
MICmd("thread-select " + current);
}
// check for stop reason
void Gdb_MI2::CheckStopReason(void)
{
// data must be synced on stop...
dataSynced = false;
// we need to store stop reason BEFORE interrupting all other
// threads, otherwise it'll be lost
MIValue stReason = stopReason;
@ -1079,42 +1108,146 @@ void Gdb_MI2::UpdateLocalVars(void)
}
}
// deeply explore a value
// taking its members and skipping types
// WARNING, it APPENDS results to given arrays
void Gdb_MI2::ExploreValueDeep(String baseName, MIValue const &val, Vector<String> &fullNames, Vector<String> &values) const
{
// if value is a string, just append baseName and value to result
if(val.IsString())
{
fullNames.Add(baseName);
values.Add(val.ToString());
}
// otherwise, if value is a tuple, recursively add all subvalues
else if(val.IsTuple())
{
for(int iVal = 0; iVal < val.GetCount(); iVal++)
{
String name = val.GetKey(iVal);
if(!name.StartsWith("<"))
name = baseName + '.' + name;
else
name = baseName;
ExploreValueDeep(name, val.Get(iVal), fullNames, values);
}
}
}
// update 'this' inspector data
void Gdb_MI2::UpdateThis(void)
{
/*
MIValue thisVal = MICmd("data-evaluate-expression *this");
RLOG(thisVal.Dump());
thisNames.Clear();
thisExpressions.Clear();
thisValues.Clear();
VarItem v = EvalGdb("*this");
v.shortExpression = ".";
thisNames.Add(v.shortExpression);
thisExpressions.Add(v.evaluableExpression);
thisValues.Add(v.value);
Vector<VarItem> vv;
if(v.kind == VarItem::SIMPLE)
vv = GetChildren(v);
else
vv = GetChildren(v, 0, 10);
for(int iChild = 0; iChild < vv.GetCount(); iChild++)
{
VarItem &v = vv[iChild];
thisNames.Add(v.shortExpression);
thisExpressions.Add(v.evaluableExpression);
thisValues.Add(v.value);
}
*/
thisNames.Clear();
thisExpressions.Clear();
thisValues.Clear();
Vector<String>names, values;
MIValue thisVal = MICmd("data-evaluate-expression *this");
if(!thisVal.IsTuple())
return;
if(thisVal.Find("value") >= 0)
{
// weird but the value is quoted, so must be parsed again...
MIValue const &tup = thisVal.Get("value");
if(!tup.IsString())
return;
MIValue s(tup.ToString());
s.PackNames();
TypeSimplify(s);
ExploreValueDeep("", s, names, values);
}
else if(thisVal.Find("variables") >= 0)
{
MIValue &arr = thisVal.Get("variables");
if(!arr.IsArray())
return;
for(int iVal = 0; iVal < arr.GetCount(); iVal++)
{
MIValue &val = arr[iVal];
if(!val.IsTuple() || val.Find("name") < 0 || val.Find("value") < 0)
continue;
MIValue &s = val.Get("value");
s.PackNames();
TypeSimplify(s);
val.PackNames();
ExploreValueDeep("." + val.Get("name").ToString(), s, thisExpressions, thisValues);
}
}
// build short names from full names
for(int iName = 0; iName < thisExpressions.GetCount(); iName++)
{
String const &nam = thisExpressions[iName];
String shortName;
int pos = nam.ReverseFind('.');
if(pos < 0)
shortName = nam;
else
shortName = nam.Mid(pos + 1);
thisNames.Add(shortName);
}
// here the 'this' object is dumped inside the 2 vectors 'names' and 'values'
// we need to do some cleanup, removing empty values, class names and so on
// we shall also construct the 'thisNames' vector containing just the last name path
// used for tooltip
for(int iName = 0; iName < names.GetCount(); iName++)
{
String const &name = names[iName];
if(name.IsEmpty())
continue;
if(name == "<No data fields>")
continue;
if(name.StartsWith("_vptr."))
continue;
String const &val = values[iName];
if(val.IsEmpty())
continue;
String shortName;
int dotPos = name.ReverseFind('.');
if(dotPos < 0)
shortName = name;
else
shortName = name.Mid(dotPos + 1);
thisNames.Add(shortName);
thisExpressions.Add(name);
thisValues.Add(val);
}
}
// sync watches treectrl
void Gdb_MI2::SyncLocals()
{
VectorMap<String, String> prev = DataMap2(locals);
locals.Clear();
for(int iLoc = 0; iLoc < localVarNames.GetCount(); iLoc++)
locals.Add(localVarNames[iLoc], localVarValues[iLoc]);
MarkChanged2(prev, locals);
}
// sync watches treectrl
void Gdb_MI2::SyncWatches()
{
// re-fill the control
VectorMap<String, String> prev = DataMap2(watches);
for(int i = 0; i < watches.GetCount(); i++)
{
MIValue res = MICmd("data-evaluate-expression \"" + watches.Get(i, 0).ToString() + "\"");
if(res.IsError() || res.IsEmpty())
watches.Set(i, 2, t_("<can't evaluate expression>"));
else
watches.Set(i, 2, res["value"].ToString());
}
MarkChanged2(prev, watches);
}
// sync auto vars treectrl
void Gdb_MI2::SyncAutos()
{
@ -1149,9 +1282,49 @@ void Gdb_MI2::SyncAutos()
MarkChanged2(prev, autos);
}
// sync watches treectrl
void Gdb_MI2::SyncLocals()
{
VectorMap<String, String> prev = DataMap2(locals);
locals.Clear();
for(int iLoc = 0; iLoc < localVarNames.GetCount(); iLoc++)
locals.Add(localVarNames[iLoc], localVarValues[iLoc]);
MarkChanged2(prev, locals);
}
void Gdb_MI2::SyncMembers(void)
{
VectorMap<String, String> prev = DataMap2(members);
members.Clear();
for(int iMem = 0; iMem < thisNames.GetCount(); iMem++)
members.Add(thisExpressions[iMem], thisValues[iMem]);
MarkChanged2(prev, members);
}
// sync watches treectrl
void Gdb_MI2::SyncWatches()
{
// re-fill the control
VectorMap<String, String> prev = DataMap2(watches);
for(int i = 0; i < watches.GetCount(); i++)
{
MIValue res = MICmd("data-evaluate-expression \"" + watches.Get(i, 0).ToString() + "\"");
if(res.IsError() || res.IsEmpty())
watches.Set(i, 2, t_("<can't evaluate expression>"));
else
watches.Set(i, 2, res["value"].ToString());
}
MarkChanged2(prev, watches);
}
// sync data tabs, depending on which tab is shown
void Gdb_MI2::SyncData()
{
if(dataSynced)
return;
// updates stored local variables
// those are needed for Autos, Locals and Tooltips
UpdateLocalVars();
@ -1160,20 +1333,12 @@ void Gdb_MI2::SyncData()
// also used for tooltips and 'this' pane page
UpdateThis();
switch(tab.Get())
{
case 0:
SyncAutos();
break;
case 1:
SyncLocals();
break;
case 2:
SyncWatches();
break;
}
SyncAutos();
SyncLocals();
SyncMembers();
SyncWatches();
dataSynced = true;
}
// watches arrayctrl key handling
@ -1486,7 +1651,7 @@ void Gdb_MI2::onExploreExpr(ArrayCtrl *what)
doExplore(expr, "", false, true);
// activate explorer tab
tab.Set(3);
tab.Set(4);
}
void Gdb_MI2::onExplorerChild()
@ -1590,8 +1755,8 @@ bool Gdb_MI2::Create(One<Host> _host, const String& exefile, const String& cmdli
MICmd("gdb-set args " + cmdline);
// enable pretty printing
SendPrettyPrinters();
MICmd("enable-pretty-printing");
// SendPrettyPrinters();
// MICmd("enable-pretty-printing");
firstRun = true;

View file

@ -15,6 +15,64 @@ class Gdb_MI2 : public Debugger, public ParentCtrl
{
private:
// item for a GDB variable
struct VarItem : Moveable<VarItem>
{
typedef enum { SIMPLE, ARRAY, MAP } VarKind;
// error/empty state
bool error;
// gdb internal variable name
String varName;
// short name
String shortExpression;
// evaluable expression
String evaluableExpression;
// type
String type;
// kind
int kind;
// value of expression for non-sequence types
String value;
// children
int numChildren;
// dynamic varobj--number of children must be queried
bool dynamic;
// check if value contains an error
bool IsError(void) const;
bool operator!(void) const { return IsError(); }
operator bool() { return !IsError(); }
// constructor
VarItem();
// copy (pick)
VarItem(VarItem pick_ &v);
VarItem &operator=(pick_ VarItem &v);
};
// evaluate an expression usign gdb variables
VarItem EvalGdb(String const &expr);
// remove GDB variable for item
void KillVariable(VarItem &v);
// fetch variable children
Vector<VarItem> GetChildren(MIValue const &val, String const &prePath = "");
// fetch variable children
Vector<VarItem> GetChildren(VarItem &v, int minChild = -1, int maxChild = -1);
// used to post and kill timed callbacks
TimeCallback timeCallback;
@ -42,12 +100,14 @@ class Gdb_MI2 : public Debugger, public ParentCtrl
DropList frame;
DropList threadSelector;
TabCtrl tab;
ArrayCtrl locals;
ArrayCtrl watches;
ArrayCtrl autos;
ArrayCtrl autos;
ArrayCtrl locals;
ArrayCtrl members;
ArrayCtrl watches;
ArrayCtrl explorer;
// explorer stuffs -- just starting
ArrayCtrl explorer;
EditString explorerExprEdit;
Button explorerBackBtn, explorerForwardBtn;
StaticRect explorerPane;
@ -111,7 +171,7 @@ class Gdb_MI2 : public Debugger, public ParentCtrl
// 'this' variable inspection data
Index<String>thisNames;
Vector<String>thisFullNames;
Vector<String>thisExpressions;
Vector<String>thisValues;
// stored autos expressions, values and types
@ -120,7 +180,18 @@ class Gdb_MI2 : public Debugger, public ParentCtrl
// update local variables on demand
void UpdateLocalVars(void);
// deeply explore a value
// taking its members and skipping types
// WARNING, it APPENDS results to given arrays
void ExploreValueDeep(String baseName, MIValue const &val, Vector<String> &fullNames, Vector<String> &values) const;
// known types simplifier
// takes a MIValue from '-data-evaluate-expression' command and try
// do simplify diplay of known types
void TypeSimplify(MIValue &val);
// update 'this' inspector data
void UpdateThisDeep(VarItem &v);
void UpdateThis(void);
// logs frame data on console
@ -147,16 +218,20 @@ class Gdb_MI2 : public Debugger, public ParentCtrl
// sync disassembler pane
void SyncDisas(MIValue &fInfo, bool fr);
// sync local variables pane
void SyncLocals(void);
// sync watches treectrl
void SyncWatches();
// sync auto vars treectrl
void SyncAutos();
// sync local variables pane
void SyncLocals(void);
// sync 'this' members pane
void SyncMembers(void);
// sync watches treectrl
void SyncWatches();
// sync data tabs, depending on which tab is shown
bool dataSynced;
void SyncData();
// sync ide display with breakpoint position

View file

@ -13,22 +13,67 @@ static MIValue &ErrorMIValue(String const &msg)
return v;
}
bool MIValue::expect(String const &where, char exp, int i, String const &s)
{
if(s[i] == exp)
return true;
int start = i - 30;
if(start < 0)
start = 0;
if(!s[i])
SetError(Format(where + " : Expected '%c', got end of string at pos %d around '%s'", exp, i, s.Mid(start, 60)));
else
SetError(Format(where + " : Expected '%c', got '%c' at pos %d in '%s'", exp, s[i], i, s.Mid(start, 60)));
return false;
}
int MIValue::ParsePair(String &name, MIValue &val, String const &s, int i)
{
name.Clear();
val.Clear();
while(s[i] && (s[i] != '=') && s[i] != ']' && s[i] != '}')
name.Cat(s[i++]);
if(s[i] != '=')
while(s[i] && isspace(s[i]))
i++;
if(!s[i])
{
SetError("ParsePair:Unexpected end of string");
return i;
i++;
}
// is starting wirh '[' or '{' take it as a value with empty name
if(s[i] == '{' || s[i] == '[')
name = "";
else
{
int aCount = 0;
while(s[i] && ((s[i] != '=' && s[i] != '}' && s[i] != ']') || aCount))
{
name.Cat(s[i]);
if(s[i] == '<')
aCount++;
else if(s[i] == '>')
aCount--;
i++;
// skip blanks if not inside <>
if(!aCount)
while(s[i] && isspace(s[i]))
i++;
}
while(s[i] && isspace(s[i]))
i++;
if(s[i] != '=')
return i;
i++;
while(s[i] && isspace(s[i]))
i++;
}
switch(s[i])
{
case '"':
{
i = val.ParseString(s, i);
break;
}
break;
case '[':
@ -40,8 +85,10 @@ int MIValue::ParsePair(String &name, MIValue &val, String const &s, int i)
break;
default:
NEVER();
i = val.ParseUnquotedString(s, i);
break;
}
return i;
}
@ -51,27 +98,26 @@ int MIValue::ParseTuple(String const &s, int i)
type = MITuple;
// drop opening delimiter
if(s[i] != '{')
{
SetError(Format("Expected '{' at pos %d in '%s'", i, s));
if(!expect("ParseTuple", '{', i, s))
return s.GetCount();
}
i++;
while(s[i] && s[i] != '}')
{
while(s[i] && isspace(s[i]))
i++;
String name;
MIValue val;
i = ParsePair(name, val, s, i);
tuple.Add(name, val);
while(s[i] && isspace(s[i]))
i++;
if(s[i] == '}')
break;
if(s[i] != ',')
{
SetError(Format("Expected ',' at pos %d in '%s'", i, s));
if(!expect("ParseTuple", ',', i, s))
return s.GetCount();
}
i++;
}
return i + 1;
}
@ -81,14 +127,15 @@ int MIValue::ParseArray(String const &s, int i)
type = MIArray;
// drop opening delimiter
if(s[i] != '[')
{
SetError(Format("Expected '[' at pos %d in '%s'", i, s));
if(!expect("ParseArray", '[', i, s))
return s.GetCount();
}
i++;
while(s[i] && isspace(s[i]))
i++;
while(s[i] && s[i] != ']')
{
while(s[i] && isspace(s[i]))
i++;
String name;
MIValue val;
if(s[i] == '[')
@ -97,16 +144,17 @@ int MIValue::ParseArray(String const &s, int i)
i = val.ParseTuple(s, i);
else if(s[i] == '"')
i = val.ParseString(s, i);
else if(s[i] == '<')
i = val.ParseAngle(s, i);
else
i = ParsePair(name, val, s, i);
array.Add(val);
while(s[i] && isspace(s[i]))
i++;
if(s[i] == ']')
break;
if(s[i] != ',')
{
SetError(Format("Expected ',' at pos %d in '%s'", i, s));
if(!expect("ParseArray", ',', i, s))
return s.GetCount();
}
i++;
}
return i + 1;
@ -118,11 +166,8 @@ int MIValue::ParseString(String const &s, int i)
type = MIString;
char c;
if(s[i] != '"')
{
SetError(Format("Expected '\"' at pos %d in '%s'", i, s));
if(!expect("ParseString", '"', i, s))
return s.GetCount();
}
i++;
while( (c = s[i++]) != 0)
{
@ -134,11 +179,83 @@ int MIValue::ParseString(String const &s, int i)
else
string.Cat(c);
}
if(c != '"')
{
SetError(Format("Expected '\"' at pos %d in '%s'", i, s));
if(!expect("ParseString", '"', i-1, s))
return s.GetCount();
return i;
}
int MIValue::ParseAngle(String const &s, int i)
{
Clear();
type = MIString;
int aCount = 0;
char c;
if(!expect("ParseAngle", '<', i, s))
return s.GetCount();
string = "<";
aCount++;
i++;
while( (c = s[i++]) != 0)
{
// verbatim if escaped
if(c == '\\')
string.Cat(s[i++]);
else if(c == '>' && !--aCount)
break;
else
string.Cat(c);
if(c == '<')
aCount++;
}
if(!expect("ParseAngle", '"', i-1, s))
return s.GetCount();
string.Cat('>');
return i;
}
// sigh
static bool comma(String const &s, int i)
{
if(s[i] != ',')
return false;
if(!i)
return true;
if(IsDigit(s[i-1]) && IsDigit(s[i+1]))
return false;
return true;
}
// we can hava a non-quoted string... so we read up
// to terminator, which can be '}', ']' or ','
int MIValue::ParseUnquotedString(String const &s, int i)
{
String valStr;
int aCount = 0;
bool inQuote = false;
while(s[i] && ((s[i] != '=' && s[i] != '}' && s[i] != ']' && !comma(s, i)) || inQuote || aCount))
{
valStr.Cat(s[i]);
if(s[i] == '\\')
{
i++;
if(s[i])
valStr.Cat(s[i++]);
continue;
}
if(s[i] == '<' && !inQuote)
aCount++;
else if(s[i] == '>' && !inQuote)
aCount--;
else if(s[i] == '"' && !aCount)
inQuote = !inQuote;
i++;
}
type = MIString;
string = valStr;
return i;
}
@ -150,8 +267,12 @@ int MIValue::Parse(String const &s, int i)
// otherwise, it can be a sequence of pair name="value" which is stored like a tuple
// latter case is an example o bad design of MI interface....
Clear();
while(s[i] && isspace(s[i]))
i++;
if(s[i] == '"')
return ParseString(s, i);
else if(s[i] == '<')
return ParseAngle(s, i);
else if(s[i] == '[')
return ParseArray(s, i);
else if(s[i] == '{')
@ -165,10 +286,13 @@ int MIValue::Parse(String const &s, int i)
{
i = ParsePair(name, val, s, i);
tuple.Add(name, val);
while(s[i] && isspace(s[i]))
i++;
if(s[i] != ',')
break;
i++;
}
return i;
}
}
@ -282,9 +406,22 @@ MIValue &MIValue::Get(int i)
{
if(IsError())
return *this;
if(type != MIArray)
return ErrorMIValue("Not an Array value type");
return array[i];
if(type == MIArray)
return array[i];
if(type == MITuple)
return tuple[i];
return ErrorMIValue("Not an Array value type");
}
MIValue const &MIValue::Get(int i) const
{
if(IsError())
return *this;
if(type == MIArray)
return array[i];
if(type == MITuple)
return tuple[i];
return ErrorMIValue("Not an Array value type");
}
MIValue &MIValue::Get(const char *key)
@ -296,6 +433,15 @@ MIValue &MIValue::Get(const char *key)
return tuple.Get(key);
}
MIValue const &MIValue::Get(const char *key) const
{
if(type != MITuple)
return ErrorMIValue("Not a Tuple value type");
if(tuple.Find(key) < 0)
return ErrorMIValue(String("key '") + key + "' not found");
return tuple.Get(key);
}
String &MIValue::Get(void)
{
if(type != MIString)
@ -326,6 +472,21 @@ String MIValue::Get(const char *key, const char *def) const
return def;
}
// gets key by index for tuple values
String MIValue::GetKey(int idx) const
{
if(type != MITuple)
return ErrorMIValue("Not a Tuple value type");
return tuple.GetKey(idx);
}
void MIValue::Set(String const &s)
{
Clear();
type = MIString;
string = s;
}
// data dump
String MIValue::Dump(int level) const
{
@ -333,11 +494,11 @@ String MIValue::Dump(int level) const
switch(type)
{
case MIString:
return spacer + string;
return spacer + "<STRING>" + string;
case MITuple:
{
String s = spacer + "{\n";
String s = spacer + "<TUPLE>" + "{\n";
level += 4;
spacer = String(' ', level);
for(int i = 0; i < tuple.GetCount(); i++)
@ -364,7 +525,7 @@ String MIValue::Dump(int level) const
case MIArray:
{
String s = spacer + "[ \n";
String s = spacer + "<ARRAY>" + "[ \n";
level += 4;
for(int i = 0; i < array.GetCount(); i++)
{
@ -401,3 +562,34 @@ MIValue &MIValue::FindBreakpoint(String const &file, int line)
}
return NullMIValue();
}
static String PackName(String const &name)
{
String res;
const char *c = ~name;
while(*c)
{
if(!IsSpace(*c))
res.Cat(*c);
c++;
}
return res;
}
// packs names inside tuples -- to make type recognition easy
void MIValue::PackNames(void)
{
if(type == MITuple)
{
for(int i = 0; i < tuple.GetCount(); i++)
{
tuple.SetKey(i, PackName(tuple.GetKey(i)));
tuple[i].PackNames();
}
}
else if(type == MIArray)
{
for(int i = 0; i < array.GetCount(); i++)
array[i].PackNames();
}
}

View file

@ -10,10 +10,14 @@ typedef enum { MIString, MIArray, MITuple } MIValueType;
class MIValue : public Moveable<MIValue>
{
private:
bool expect(String const &where, char exp, int i, String const &s);
int ParsePair(String &name, MIValue &val, String const &s, int i = 0);
int ParseTuple(String const &s, int i = 0);
int ParseArray(String const &s, int i = 0);
int ParseString(String const &s, int i = 0);
int ParseAngle(String const &s, int i = 0);
int ParseUnquotedString(String const &s, int i = 0);
int ParseValue(String const &s, int i = 0);
int Parse(String const &s, int i = 0);
@ -45,12 +49,25 @@ class MIValue : public Moveable<MIValue>
// simple accessors
int GetCount(void) const;
int Find(const char *key) const;
MIValue &Get(int i);
MIValue const &Get(int i) const;
MIValue &operator[](int i) { return Get(i); }
MIValue const &operator[](int i) const { return Get(i); }
MIValue &Get(const char *s);
MIValue const &Get(const char *s) const;
MIValue &operator[](const char *key) { return Get(key); }
MIValue const &operator[](const char *key) const { return Get(key); }
String &Get(void);
String const &Get(void) const;
// gets key by index for tuple values
String GetKey(int idx) const;
operator String&() { return Get(); }
operator const String &() const { return Get(); }
String &ToString(void) { return Get(); }
@ -60,6 +77,9 @@ class MIValue : public Moveable<MIValue>
String Get(const char *key, const char *def) const;
String operator()(const char *key, const char *def) const { return Get(key, def); }
// setter for string (operator= starts parser...)
void Set(String const &s);
// some type checking
bool IsArray(void) const { return type == MIArray; }
void AssertArray(void) const { ASSERT(type == MIArray); }
@ -73,6 +93,9 @@ class MIValue : public Moveable<MIValue>
// finds breakpoint data given file and line
MIValue &FindBreakpoint(String const &file, int line);
// packs names inside tuples -- to make type recognition easy
void PackNames(void);
};
#endif

View file

@ -13,6 +13,20 @@ def dump(obj):
newobj[attr]=dump(newobj[attr])
return newobj
#floating pointer printer
class FloatPrinter(object):
"Print a floating point number with fixed DOT separator"
def __init__(self, val):
self.val = val
def to_string(self):
return "%.15f" % self.val
def display_hint(self):
return 'String'
# Upp::String printer
class UppStringPrinter(object):
"Print an Upp::String"
@ -60,24 +74,19 @@ class UppVectorPrinter(object):
def __init__ (self, val):
self.val = val
self.item = val['vector']
self.finish = self.item + val['items']
self.count = 0
self.count = val['items']
self.idx = -1
def __iter__(self):
return self
def next(self):
count = self.count
self.count = self.count + 1
if count == 0:
return ('.items', self.val['items'])
if count == 1:
return ('.alloc', self.val['alloc'])
if self.item == self.finish:
def __next__(self):
self.idx += 1
if self.idx >= self.count:
raise StopIteration
elt = self.item.dereference()
self.item = self.item + 1
return ('[%d]' % (count-2), elt)
self.item += 1
return ('[%d]' % self.idx, elt)
def __init__(self, typename, val):
self.typename = typename
@ -87,11 +96,10 @@ class UppVectorPrinter(object):
return self._iterator(self.val)
def to_string(self):
start = 0
finish = self.val['items']
end = self.val['alloc']
return ('%s count %d alloc %d'
% (self.typename, int (finish - start), int (end - start)))
count = self.val['items']
alloc = self.val['alloc']
return ('<%s> = {<No data fields>}, count = %d, alloc = %d, elements'
% (self.typename, count, alloc))
def display_hint(self):
return 'array'
@ -103,24 +111,19 @@ class UppArrayPrinter(object):
def __init__ (self, val):
self.val = val['vector']
self.item = self.val['vector']
self.finish = self.item + self.val['items']
self.count = 0
self.count = self.val['items']
self.idx = -1
def __iter__(self):
return self
def next(self):
count = self.count
self.count = self.count + 1
if count == 0:
return ('.items', self.val['items'])
if count == 1:
return ('.alloc', self.val['alloc'])
if self.item == self.finish:
def __next__(self):
self.idx += 1
if self.idx >= self.count:
raise StopIteration
elt = self.item.dereference().dereference()
self.item = self.item + 1
return ('[%d]' % (count-2), elt)
self.item += + 1
return ('[%d]' % self.idx, elt)
def __init__(self, typename, val):
self.typename = typename
@ -130,34 +133,99 @@ class UppArrayPrinter(object):
return self._iterator(self.val)
def to_string(self):
start = 0
finish = self.val['vector']['items']
end = self.val['vector']['alloc']
return ('%s count %d alloc %d'
% (self.typename, int (finish - start), int (end - start)))
count = self.val['vector']['items']
alloc = self.val['vector']['alloc']
return ('<%s> = {<No data fields>}, count = %d, alloc = %d, elements'
% (self.typename, count, alloc))
def display_hint(self):
return 'array'
# Upp::VectorMap and ArrayMap printer
class UppMapPrinter(object):
"Print an Upp::VectorMap or ArrayMap"
# Upp::VectorMap printer
class UppVectorMapPrinter(object):
"Print an Upp::VectorMap"
class _iterator:
def __init__ (self, val):
self.val = val
self.key = val['key']['key']['vector']
self.value = val['value']['vector']
self.count = val['value']['items']
self.idx = -1
def __iter__(self):
return self
def __next__(self):
self.idx += 1
if self.idx >= self.count * 2:
raise StopIteration
if self.idx & 1:
aValue = self.value.dereference()
self.value += 1
return ('value', str(aValue))
else:
aKey = self.key.dereference()
self.key += 1
return ('key', str(aKey))
def __init__(self, typename, val):
self.typename = typename
self.val = val
def children(self):
return [('.keys', self.val['key']), ('.values', self.val['value'])].__iter__()
return self._iterator(self.val)
def to_string(self):
count = self.val['key']['key']['items']
alloc = self.val['key']['key']['alloc']
return ('%s count %d alloc %d'
return ('<%s> = {<No data fields>}, count = %d, alloc = %d, elements'
% (self.typename, count, alloc))
def display_hint(self):
return 'array'
return 'map'
# Upp::ArrayMap printer
class UppArrayMapPrinter(object):
"Print an Upp::ArrayMap"
class _iterator:
def __init__ (self, val):
self.val = val
self.key = val['key']['key']['vector']
self.value = val['value']['vector']['vector']
self.count = val['value']['vector']['items']
self.idx = -1
def __iter__(self):
return self
def __next__(self):
self.idx += 1
if self.idx >= self.count * 2:
raise StopIteration
if self.idx & 1:
aValue = self.value.dereference().dereference()
self.value += 1
return ('value', str(aValue))
else:
aKey = self.key.dereference()
self.key += 1
return ('key', str(aKey))
def __init__(self, typename, val):
self.typename = typename
self.val = val
def children(self):
return self._iterator(self.val)
def to_string(self):
count = self.val['key']['key']['items']
alloc = self.val['key']['key']['alloc']
return ('<%s> = {<No data fields>}, count = %d, alloc = %d, elements'
% (self.typename, count, alloc))
def display_hint(self):
return 'map'
# Upp::Value printer
class UppValuePrinter(object):
@ -289,6 +357,9 @@ class UppRectPtrPrinter(object):
def UppLookupFunction(val):
typeStr = str(val.type)
if typeStr == 'double' or typeStr == 'float' or typeStr == 'long double':
return FloatPrinter(val)
if typeStr == 'Upp::String *':
return UppStringPtrPrinter(val)
@ -372,11 +443,11 @@ def UppLookupFunction(val):
regex = re.compile("^Upp::VectorMap<.*>$")
if regex.match(lookup_tag):
return UppMapPrinter(lookup_tag, val)
return UppVectorMapPrinter(lookup_tag, val)
regex = re.compile("^Upp::ArrayMap<.*>$")
if regex.match(lookup_tag):
return UppMapPrinter(lookup_tag, val)
return UppArrayMapPrinter(lookup_tag, val)
regex = re.compile("^Upp::Vector<.*>$")
if regex.match(lookup_tag):

View file

@ -0,0 +1,71 @@
#include "Debuggers.h"
#include <ide/ide.h>
typedef void (*TYPE_SIMPLIFIER_HANDLER)(MIValue &val);
static void UppStringSimplify(MIValue &val)
{
// strings are "easy", in debug mode 's' and 'len' members are provided
// so we shall just look at them in tuple
try
{
MIValue &v = val[0][0][0];
if(!v.IsTuple())
return;
if(v.Find("s") < 0)
return;
String s = v["s"];
// strip address part
int pos = 0;
while(s[pos] && s[pos] != '"')
pos++;
if(!s[pos])
return;
val.Set(s.Mid(pos));
}
catch(...)
{
return;
}
}
struct TYPE_SIMPLIFIER
{
const char *type;
TYPE_SIMPLIFIER_HANDLER handler;
};
TYPE_SIMPLIFIER TYPE_SIMPLIFIERS[] =
{
{ "<Upp::Moveable<Upp::String,Upp::AString<Upp::String0>>>", UppStringSimplify }
};
#define SIMPLIFIERS_COUNT ((int)(sizeof(TYPE_SIMPLIFIERS) / sizeof(TYPE_SIMPLIFIER)))
void Gdb_MI2::TypeSimplify(MIValue &val)
{
static VectorMap<String, TYPE_SIMPLIFIER_HANDLER> handlers;
if(!handlers.GetCount())
for(int i = 0; i < SIMPLIFIERS_COUNT; i++)
handlers.Add(TYPE_SIMPLIFIERS[i].type, TYPE_SIMPLIFIERS[i].handler);
if(val.IsTuple())
{
for(int i = 0; i < val.GetCount(); i++)
{
MIValue &v = val[i];
if(v.IsTuple() && v.GetCount())
{
int idx = handlers.Find(v.GetKey(0));
if(idx >= 0)
handlers[idx](v);
else
TypeSimplify(v);
}
else
TypeSimplify(v);
}
}
}

View file

@ -0,0 +1,157 @@
#include "Debuggers.h"
#include <ide/ide.h>
// check if value contains an error
bool Gdb_MI2::VarItem::IsError(void) const
{
return error;
}
// constructor
Gdb_MI2::VarItem::VarItem()
{
error = true;
dynamic = false;
numChildren = 0;
kind = SIMPLE;
}
// copy (pick)
Gdb_MI2::VarItem::VarItem(Gdb_MI2::VarItem pick_ &v)
{
error = v.error;
dynamic = v.dynamic;
varName = v.varName;
shortExpression = v.shortExpression;
evaluableExpression = v.evaluableExpression;
type = v.type;
kind = v.kind;
value = v.value;
numChildren = v.numChildren;
}
Gdb_MI2::VarItem &Gdb_MI2::VarItem::operator=(pick_ Gdb_MI2::VarItem &v)
{
error = v.error;
dynamic = v.dynamic;
varName = v.varName;
shortExpression = v.shortExpression;
evaluableExpression = v.evaluableExpression;
type = v.type;
kind = v.kind;
value = v.value;
numChildren = v.numChildren;
return *this;
}
// evaluate an expression usign gdb variables
Gdb_MI2::VarItem Gdb_MI2::EvalGdb(String const &expr)
{
VarItem v;
MIValue val = MICmd("var-create - @ " + expr);
if(val.IsError())
return v;
v.error = false;
v.varName = val["name"];
v.evaluableExpression = expr;
v.shortExpression = expr;
v.value = val["value"];
v.numChildren = atoi(val.Get("numchild", "0"));
v.type = val["type"];
String dispHint = val.Get("displayhint", "");
if(ToUpper(dispHint) == "ARRAY")
v.kind = VarItem::ARRAY;
else if(ToUpper(dispHint) == "MAP")
v.kind = VarItem::MAP;
else
v.kind = VarItem::SIMPLE;
v.dynamic = (val.Get("dynamic", "") != "");
return v;
}
// remove GDB variable for item
void Gdb_MI2::KillVariable(Gdb_MI2::VarItem &v)
{
MICmd("var-delete " + v.varName);
}
// fetch variable children
Vector<Gdb_MI2::VarItem> Gdb_MI2::GetChildren(MIValue const &val, String const &prePath)
{
Vector<VarItem> res;
if(val.IsError() || !val.IsTuple())
return res;
MIValue const &children = val["children"];
if(!children.IsArray())
return res;
for(int i = 0; i < children.GetCount(); i++)
{
MIValue const &child = children[i];
// for private, protected, public and inherited fake childs, just go deeper
String exp = child["exp"];
String typ = child.Get("type", "");
String nam = child.Get("name");
if(exp == "private" || exp == "protected" || exp == "public" || exp == typ)
{
MIValue val2 = MICmd("var-list-children 1 " + nam);
res.Append(GetChildren(val2, prePath));
}
else
{
VarItem &v = res.Add();
v.error = false;
v.varName = nam;
v.shortExpression = prePath + "." + exp;
v.type = typ;
v.value = child["value"];
v.dynamic = (child.Get("dynamic", "") != "");
v.numChildren = atoi(child.Get("numchild", "0"));
String dispHint = child.Get("displayhint", "");
if(ToUpper(dispHint) == "ARRAY")
v.kind = VarItem::ARRAY;
else if(ToUpper(dispHint) == "MAP")
v.kind = VarItem::MAP;
else
v.kind = VarItem::SIMPLE;
/*
MIValue vExp = MICmd("var-info-path-expression " + nam);
v.evaluableExpression = vExp.Get("path_expr", "");
*/
}
}
return res;
}
// fetch variable children
Vector<Gdb_MI2::VarItem> Gdb_MI2::GetChildren(Gdb_MI2::VarItem &v, int minChild, int maxChild)
{
// for 'simple' (i.e. not maps or arrays..) get all children
// otherwise get the given range
int first, last;
if(v.kind == VarItem::SIMPLE)
{
first = 0;
last = INT_MAX;
}
else
{
if(minChild < 0)
first = 0;
else
first = minChild;
if(maxChild < 0)
last = v.numChildren;
else
last = maxChild;
}
MIValue val = MICmd(Format("var-list-children 1 %s %d %d", v.varName, first, last));
return GetChildren(val, v.shortExpression);
}