diff --git a/uppsrc/ide/Debuggers/Debuggers.upp b/uppsrc/ide/Debuggers/Debuggers.upp index a66841845..e6ce5fdaa 100644 --- a/uppsrc/ide/Debuggers/Debuggers.upp +++ b/uppsrc/ide/Debuggers/Debuggers.upp @@ -11,6 +11,8 @@ file Gdb.lay, MIValue.h, MIValue.cpp, + TypeSimplify.cpp, + VarItem.cpp, Terminal.cpp, Disas.cpp, Dbg.cpp, diff --git a/uppsrc/ide/Debuggers/Gdb_MI2.cpp b/uppsrc/ide/Debuggers/Gdb_MI2.cpp index 13e026b96..057917e3c 100644 --- a/uppsrc/ide/Debuggers/Gdb_MI2.cpp +++ b/uppsrc/ide/Debuggers/Gdb_MI2.cpp @@ -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 &fullNames, Vector &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 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(); + + Vectornames, 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 == "") + 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 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 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_("")); - 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 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 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 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_("")); + 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, 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; diff --git a/uppsrc/ide/Debuggers/Gdb_MI2.h b/uppsrc/ide/Debuggers/Gdb_MI2.h index 2345ae146..581429bec 100644 --- a/uppsrc/ide/Debuggers/Gdb_MI2.h +++ b/uppsrc/ide/Debuggers/Gdb_MI2.h @@ -15,6 +15,64 @@ class Gdb_MI2 : public Debugger, public ParentCtrl { private: + // item for a GDB variable + struct VarItem : Moveable + { + 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 GetChildren(MIValue const &val, String const &prePath = ""); + + // fetch variable children + Vector 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 IndexthisNames; - VectorthisFullNames; + VectorthisExpressions; VectorthisValues; // 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 &fullNames, Vector &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 diff --git a/uppsrc/ide/Debuggers/MIValue.cpp b/uppsrc/ide/Debuggers/MIValue.cpp index 5ebcba3a3..3bc630e10 100644 --- a/uppsrc/ide/Debuggers/MIValue.cpp +++ b/uppsrc/ide/Debuggers/MIValue.cpp @@ -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; case MITuple: { - String s = spacer + "{\n"; + String s = spacer + "" + "{\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 + "" + "[ \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(); + } +} diff --git a/uppsrc/ide/Debuggers/MIValue.h b/uppsrc/ide/Debuggers/MIValue.h index 25fa955c7..0bd40968c 100644 --- a/uppsrc/ide/Debuggers/MIValue.h +++ b/uppsrc/ide/Debuggers/MIValue.h @@ -10,10 +10,14 @@ typedef enum { MIString, MIArray, MITuple } MIValueType; class MIValue : public Moveable { 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 // 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 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 // 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 diff --git a/uppsrc/ide/Debuggers/PrettyPrinters.py b/uppsrc/ide/Debuggers/PrettyPrinters.py index 3124363ba..4cde11e1e 100644 --- a/uppsrc/ide/Debuggers/PrettyPrinters.py +++ b/uppsrc/ide/Debuggers/PrettyPrinters.py @@ -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> = {}, 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> = {}, 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> = {}, 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> = {}, 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): diff --git a/uppsrc/ide/Debuggers/TypeSimplify.cpp b/uppsrc/ide/Debuggers/TypeSimplify.cpp new file mode 100644 index 000000000..95f231957 --- /dev/null +++ b/uppsrc/ide/Debuggers/TypeSimplify.cpp @@ -0,0 +1,71 @@ +#include "Debuggers.h" +#include + +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[] = +{ + { ">>", UppStringSimplify } +}; + +#define SIMPLIFIERS_COUNT ((int)(sizeof(TYPE_SIMPLIFIERS) / sizeof(TYPE_SIMPLIFIER))) + +void Gdb_MI2::TypeSimplify(MIValue &val) +{ + static VectorMap 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); + } + } +} \ No newline at end of file diff --git a/uppsrc/ide/Debuggers/VarItem.cpp b/uppsrc/ide/Debuggers/VarItem.cpp new file mode 100644 index 000000000..0be880fa7 --- /dev/null +++ b/uppsrc/ide/Debuggers/VarItem.cpp @@ -0,0 +1,157 @@ +#include "Debuggers.h" +#include + +// 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::GetChildren(MIValue const &val, String const &prePath) +{ + Vector 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::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); +} + +