mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-07-11 22:03:01 -06:00
Ide/Debuggers/Gdb_MI2 : refactored and added optional multithread version
git-svn-id: svn://ultimatepp.org/upp/trunk@6951 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
efefa2a638
commit
638dabd428
12 changed files with 1909 additions and 1257 deletions
|
|
@ -51,6 +51,8 @@ void RedDisplay::Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color
|
|||
|
||||
VectorMap<String, String> DataMap(const ArrayCtrl& data)
|
||||
{
|
||||
GuiLock __;
|
||||
|
||||
VectorMap<String, String> m;
|
||||
for(int i = 0; i < data.GetCount(); i++)
|
||||
m.Add(data.Get(i, 0), data.Get(i, 1));
|
||||
|
|
@ -59,6 +61,8 @@ VectorMap<String, String> DataMap(const ArrayCtrl& data)
|
|||
|
||||
void MarkChanged(const VectorMap<String, String>& m, ArrayCtrl& data)
|
||||
{
|
||||
GuiLock __;
|
||||
|
||||
for(int i = 0; i < data.GetCount(); i++) {
|
||||
int q = m.Find(data.Get(i, 0));
|
||||
if(q >= 0 && m[q] != data.Get(i, 1))
|
||||
|
|
|
|||
|
|
@ -9,20 +9,23 @@ file
|
|||
Debuggers.h,
|
||||
Debuggers.iml,
|
||||
Gdb.lay,
|
||||
MIValue.h,
|
||||
MIValue.cpp,
|
||||
TypeSimplify.h,
|
||||
TypeSimplify.cpp,
|
||||
UppSimplifiers.icpp,
|
||||
Terminal.cpp,
|
||||
Disas.cpp,
|
||||
Dbg.cpp,
|
||||
Gdb.cpp,
|
||||
GDB_MI2 readonly separator,
|
||||
MIValue.h,
|
||||
MIValue.cpp,
|
||||
VarItem.h,
|
||||
VarItem.cpp,
|
||||
TypeSimplify.h,
|
||||
TypeSimplify.cpp,
|
||||
Gdb_MI2.lay,
|
||||
Gdb_MI2.h,
|
||||
Gdb_MI2Explore.cpp,
|
||||
Gdb_MI2Eval.cpp,
|
||||
Gdb_MI2Gdb.cpp,
|
||||
Gdb_MI2.cpp,
|
||||
UppSimplifiers.icpp,
|
||||
PDB readonly separator,
|
||||
Pdb.h,
|
||||
cvconst.h,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2,6 +2,7 @@
|
|||
#define _ide_Debuggers_Gdb_MI2_h_
|
||||
|
||||
#include "MIValue.h"
|
||||
#include "VarItem.h"
|
||||
|
||||
class WatchEdit : public LineEdit
|
||||
{
|
||||
|
|
@ -11,6 +12,13 @@ class WatchEdit : public LineEdit
|
|||
#define LAYOUTFILE <ide/Debuggers/Gdb_MI2.lay>
|
||||
#include <CtrlCore/lay.h>
|
||||
|
||||
// abort command exception - used to stop non-main threads
|
||||
struct BreakExc : public Exc
|
||||
{
|
||||
BreakExc() : Exc("break") {}
|
||||
|
||||
};
|
||||
|
||||
class Gdb_MI2 : public Debugger, public ParentCtrl
|
||||
{
|
||||
private:
|
||||
|
|
@ -22,24 +30,39 @@ class Gdb_MI2 : public Debugger, public ParentCtrl
|
|||
// multithread support
|
||||
#ifdef flagMT
|
||||
// numbr of running debug threads
|
||||
int runningThreads;
|
||||
int threadRunning;
|
||||
|
||||
// flag to signal threads to stop
|
||||
bool stopThreads;
|
||||
bool stopThread;
|
||||
|
||||
// mutex and thead object
|
||||
Mutex mutex;
|
||||
Thread debugThread;
|
||||
|
||||
// mutex-protected functions
|
||||
bool IsThreadRunning(void);
|
||||
void IncThreadRunning();
|
||||
void DecThreadRunning();
|
||||
|
||||
bool IsStopThread(void);
|
||||
void SetStopThread(bool b);
|
||||
|
||||
#endif
|
||||
|
||||
// debug break support -- ONLY POSIX, by now
|
||||
#ifdef PLATFORM_POSIX
|
||||
// debug break support -- ONLY POSIX, by now
|
||||
bool InterruptDebugger(void);
|
||||
#endif
|
||||
|
||||
#ifdef PLATFORM_POSIX
|
||||
// current command break support -- ONLY POSIX, by now
|
||||
// used to speed up operations in MT mode
|
||||
bool InterruptCommand(void);
|
||||
#endif
|
||||
|
||||
// used to post and kill timed callbacks
|
||||
TimeCallback timeCallback;
|
||||
TimeCallback exploreCallback;
|
||||
|
||||
One<Host> host;
|
||||
One<AProcess> dbg;
|
||||
|
|
@ -84,9 +107,11 @@ class Gdb_MI2 : public Debugger, public ParentCtrl
|
|||
|
||||
void doExplore(String const &expr, bool appendHistory);
|
||||
|
||||
// explorer expressions and values
|
||||
Index<String>explorerExpressions;
|
||||
Vector<String>explorerValues;
|
||||
Index<String> explorerHistoryExpressions;
|
||||
int explorerHistoryPos;
|
||||
Index<String> explorerChildExpressions;
|
||||
|
||||
Label dlock;
|
||||
|
||||
|
|
@ -167,18 +192,38 @@ class Gdb_MI2 : public Debugger, public ParentCtrl
|
|||
|
||||
// sync auto vars treectrl
|
||||
void SyncAutos();
|
||||
|
||||
|
||||
#ifdef flagMT
|
||||
// sync local variables pane
|
||||
void SyncLocals(MIValue val = MIValue());
|
||||
void SyncLocals(void);
|
||||
|
||||
// Sync 'this' inspector data
|
||||
void SyncThis(MIValue val = MIValue());
|
||||
void SyncThis(void);
|
||||
|
||||
// sync watches treectrl
|
||||
void SyncWatches(MIValue val = MIValue());
|
||||
void SyncWatches(void);
|
||||
|
||||
// sync explorer pane
|
||||
void SyncExplorer();
|
||||
#else
|
||||
// sync local variables pane
|
||||
void SyncLocals(Vector<VarItem> localVars = Vector<VarItem>());
|
||||
|
||||
// Sync 'this' inspector data
|
||||
void SyncThis(Vector<VarItem> children = Vector<VarItem>());
|
||||
|
||||
// sync watches treectrl
|
||||
void SyncWatches(Vector<VarItem> children = Vector<VarItem>());
|
||||
|
||||
// sync explorer pane
|
||||
void SyncExplorer(Vector<VarItem> children = Vector<VarItem>());
|
||||
#endif
|
||||
|
||||
// sync data tabs, depending on which tab is shown
|
||||
bool dataSynced;
|
||||
bool localSynced;
|
||||
bool thisSynced;
|
||||
bool watchesSynced;
|
||||
bool explorerSynced;
|
||||
void SyncData();
|
||||
|
||||
// sync ide display with breakpoint position
|
||||
|
|
@ -229,27 +274,6 @@ class Gdb_MI2 : public Debugger, public ParentCtrl
|
|||
String GetHostPath(const String& path) { return host->GetHostPath(path); }
|
||||
String GetLocalPath(const String& path) { return host->GetLocalPath(path); }
|
||||
|
||||
// now we scan the result and add some info
|
||||
// so, for example, if we find tuple like this one:
|
||||
// data = simplevalue
|
||||
// this will be modified as
|
||||
// data = { value = simplevalue, expr = evaluable_expression }
|
||||
// and for a complex value
|
||||
// data = { some=complex, not_simple=val }
|
||||
// woll be modified as
|
||||
// data = { <!value> = { some=complex, not_simple=val }, <!expr> = evaluable_expression }
|
||||
// More attributes will be added by type simplifier phase
|
||||
void AddAttribs(String const &expr, MIValue &valExpr);
|
||||
|
||||
// collects evaluated variables got with Evaluate
|
||||
// hints are used to choose the visualizer when deep-inspecting members
|
||||
// 0 for simple values, 1 for arrays, 2 for map
|
||||
void CollectVariables(MIValue &val, Index<String> &exprs, Vector<String> &vals, Vector<int> &hints);
|
||||
|
||||
// collect evaluated variables got with Evaluate
|
||||
// into a single-line string for short display
|
||||
String CollectVariablesShort(MIValue &val);
|
||||
|
||||
// fill a pane with data from a couple of arrays without erasing it first
|
||||
// (avoid re-painting and resetting scroll if not needed)
|
||||
void FillPane(ArrayCtrl &pane, Index<String> const &nam, Vector<String> const &val);
|
||||
|
|
@ -279,20 +303,9 @@ class Gdb_MI2 : public Debugger, public ParentCtrl
|
|||
// sends an MI command and get answer back
|
||||
MIValue MICmd(const char *cmdLine);
|
||||
|
||||
// known types simplifier
|
||||
// takes a MIValue from '-data-evaluate-expression' command and try
|
||||
// do simplify diplay of known types
|
||||
// with deep = false it does just type simplification, no deep evaluation of containers
|
||||
// with deep = true it does ONE deep evaluation step
|
||||
// returns true if more deep evaluation steps are needed, false otherwise
|
||||
bool TypeSimplify(MIValue &val, bool deep);
|
||||
|
||||
// variable inspection support
|
||||
// returns a MIValue with inspected data and some info fields added
|
||||
// and known types simplified and cathegorized
|
||||
// unknown and simple types are left as they are
|
||||
// deep is true if we shall have a complete sub-elements evaluation
|
||||
MIValue Evaluate(String expr, bool deep = false);
|
||||
// quick exit from service thread when called and 'stopThread' is set
|
||||
// throws a BreakExc exception
|
||||
void RaiseIfStop(void);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,304 +0,0 @@
|
|||
#include "Debuggers.h"
|
||||
#include <ide/ide.h>
|
||||
|
||||
#include "TypeSimplify.h"
|
||||
|
||||
// now we scan the result and add some info
|
||||
// so, for example, if we find tuple like this one:
|
||||
// data = simplevalue
|
||||
// this will be modified as
|
||||
// data = { value = simplevalue, expr = evaluable_expression }
|
||||
// and for a complex value
|
||||
// data = { some=complex, not_simple=val }
|
||||
// woll be modified as
|
||||
// data = { <!value> = { some=complex, not_simple=val }, <!expr> = evaluable_expression }
|
||||
// More attributes will be added by type simplifier phase
|
||||
void Gdb_MI2::AddAttribs(String const &expr, MIValue &valExpr)
|
||||
{
|
||||
if(valExpr.IsTuple())
|
||||
{
|
||||
for(int i = 0; i < valExpr.GetCount(); i++)
|
||||
{
|
||||
String nam = valExpr.GetKey(i);
|
||||
String nExpr;
|
||||
if(!nam.StartsWith("<"))
|
||||
{
|
||||
if(expr != "")
|
||||
nExpr = expr + "." + nam;
|
||||
else
|
||||
nExpr = nam;
|
||||
}
|
||||
else
|
||||
nExpr = expr;
|
||||
MIValue v = valExpr[i];
|
||||
valExpr[i].Clear();
|
||||
valExpr[i].Add(SIMPLIFY_VALUE, v);
|
||||
valExpr[i].Add(SIMPLIFY_EXPR, nExpr);
|
||||
AddAttribs(nExpr, valExpr[i][SIMPLIFY_VALUE]);
|
||||
}
|
||||
}
|
||||
else if(valExpr.IsArray())
|
||||
{
|
||||
for(int i = 0; i < valExpr.GetCount(); i++)
|
||||
AddAttribs(expr, valExpr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool Gdb_MI2::TypeSimplify(MIValue &val, bool deep)
|
||||
{
|
||||
bool needMore = false;
|
||||
if(val.IsTuple())
|
||||
{
|
||||
for(int i = 0; i < val.GetCount(); i++)
|
||||
{
|
||||
// root of variable -- contains VALUE, EXPRESSION and will add HINTS
|
||||
MIValue &vRoot = val[i];
|
||||
|
||||
// value part of variable
|
||||
MIValue &v = vRoot[SIMPLIFY_VALUE];
|
||||
|
||||
if(v.IsTuple() && v.GetCount())
|
||||
{
|
||||
String key = v.GetKey(0);
|
||||
if(key.StartsWith("<!"))
|
||||
continue;
|
||||
if(key.StartsWith("<"))
|
||||
{
|
||||
TYPE_SIMPLIFIER_HANDLER handler = GetSimplifier(key);
|
||||
if(handler)
|
||||
{
|
||||
bool nm = handler(*this, vRoot, deep);
|
||||
if(deep)
|
||||
{
|
||||
if(nm)
|
||||
{
|
||||
// we shall remove the temporary value now...
|
||||
vRoot.Remove(SIMPLIFY_TEMPVAL);
|
||||
return nm;
|
||||
}
|
||||
}
|
||||
else if(nm)
|
||||
vRoot.FindAdd(SIMPLIFY_TEMPVAL, "<evaluating...>");
|
||||
needMore |= nm;
|
||||
}
|
||||
else
|
||||
needMore |= TypeSimplify(v, deep);
|
||||
}
|
||||
else
|
||||
needMore |= TypeSimplify(v, deep);
|
||||
}
|
||||
else
|
||||
needMore |= TypeSimplify(v, deep);
|
||||
}
|
||||
}
|
||||
else if(val.IsArray())
|
||||
{
|
||||
for(int i = 0; i < val.GetCount(); i++)
|
||||
{
|
||||
// encapsulate each element inside a tuple
|
||||
MIValue tuple;
|
||||
tuple.Add("<!DUMMY>", val[i]);
|
||||
|
||||
// simplify
|
||||
needMore |= TypeSimplify(tuple, deep);
|
||||
|
||||
// de-encapsulate the element
|
||||
val[i] = tuple[0];
|
||||
if(deep && needMore)
|
||||
return needMore;
|
||||
}
|
||||
}
|
||||
return needMore;
|
||||
}
|
||||
|
||||
// variable inspection support
|
||||
// returns a MIValue with inspected data and some info fields added
|
||||
// and known types simplified and cathegorized
|
||||
// unknown and simple types are left as they are
|
||||
// if deep is true, partial evaluation of containers is done
|
||||
MIValue Gdb_MI2::Evaluate(String expr, bool deep)
|
||||
{
|
||||
// add parhentesis around expression... gdb is dumb
|
||||
expr = "(" + expr + ")";
|
||||
|
||||
// ask gdb to evaluate expression
|
||||
// and gather result in a tuple
|
||||
MIValue valExpr = MICmd("data-evaluate-expression " + expr);
|
||||
|
||||
// return empty value on error
|
||||
if(!valExpr.IsTuple())
|
||||
return MIValue();
|
||||
|
||||
if(valExpr.Find("value") >= 0)
|
||||
{
|
||||
// weird but the value is quoted, so must be parsed again...
|
||||
MIValue const &tup = valExpr.Get("value");
|
||||
if(!tup.IsString())
|
||||
return MIValue();
|
||||
String s = tup.ToString();
|
||||
MIValue parsed(s);
|
||||
|
||||
// pack tuple names--remove spaces
|
||||
parsed.PackNames();
|
||||
|
||||
//RLOG(parsed.Dump());
|
||||
AddAttribs(expr, parsed);
|
||||
|
||||
// fix arrays
|
||||
parsed.FixArrays();
|
||||
|
||||
//RLOG(parsed.Dump());
|
||||
// now we go through the type simplifier, which try to give simple representation
|
||||
// for known types -- first step, just type simplification and NOT deep evaluation
|
||||
bool needMore = TypeSimplify(parsed, false);
|
||||
|
||||
// deep container evaluation on request
|
||||
if(deep)
|
||||
while(needMore)
|
||||
needMore = TypeSimplify(parsed, true);
|
||||
//RLOG(parsed.Dump());
|
||||
|
||||
// and finally return the cleaned evaluated data
|
||||
return parsed;
|
||||
}
|
||||
|
||||
else if(valExpr.Find("variables") >= 0)
|
||||
{
|
||||
RLOG("ARRAY SIDE UNHANDLED....");
|
||||
return MIValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
RLOG("WEIRD STUFF HERE....");
|
||||
return MIValue();
|
||||
}
|
||||
}
|
||||
|
||||
// collects evaluated variables got with Evaluate
|
||||
// hints are used to choose the visualizer when deep-inspecting members
|
||||
// 0 for simple values, 1 for arrays, 2 for map
|
||||
static void Collect0(MIValue &val, Index<String> &exprs, Vector<String> &vals, Vector<int> &hints)
|
||||
{
|
||||
if(!val.IsTuple())
|
||||
return;
|
||||
|
||||
for(int i = 0; i < val.GetCount(); i++)
|
||||
{
|
||||
MIValue &v = val[i];
|
||||
|
||||
// variables still to be evaluated...
|
||||
if(v.Find(SIMPLIFY_TEMPVAL) >= 0)
|
||||
{
|
||||
vals.Add(v[SIMPLIFY_TEMPVAL].ToString());
|
||||
exprs.Add(v[SIMPLIFY_EXPR].ToString());
|
||||
hints << 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
MIValue &data = v[SIMPLIFY_VALUE];
|
||||
if(data.IsError())
|
||||
continue;
|
||||
if(data.IsString())
|
||||
{
|
||||
String d = data.ToString();
|
||||
if(!d.StartsWith("<"))
|
||||
{
|
||||
vals.Add(data.ToString()),
|
||||
exprs.Add(v[SIMPLIFY_EXPR].ToString());
|
||||
String hint = v.Get(SIMPLIFY_HINT, SIMPLIFY_SIMPLE);
|
||||
if(hint == SIMPLIFY_SIMPLE)
|
||||
hints << 0;
|
||||
else if(hint == SIMPLIFY_ARRAY)
|
||||
hints << 1;
|
||||
else if(hint == SIMPLIFY_MAP)
|
||||
hints << 2;
|
||||
else
|
||||
hints << 0;
|
||||
}
|
||||
}
|
||||
else if(data.IsTuple())
|
||||
Collect0(data, exprs, vals, hints);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Gdb_MI2::CollectVariables(MIValue &val, Index<String> &exprs, Vector<String> &vals, Vector<int> &hints)
|
||||
{
|
||||
exprs.Clear();
|
||||
vals.Clear();
|
||||
hints.Clear();
|
||||
|
||||
Collect0(val, exprs, vals, hints);
|
||||
}
|
||||
|
||||
static void CollectShort0(MIValue &val, String &s)
|
||||
{
|
||||
if(val.IsTuple())
|
||||
{
|
||||
for(int i = 0; i < val.GetCount(); i++)
|
||||
{
|
||||
MIValue &v = val[i];
|
||||
String expr = v[SIMPLIFY_EXPR].ToString();
|
||||
|
||||
// variables still to be evaluated...
|
||||
if(v.Find(SIMPLIFY_TEMPVAL) >= 0)
|
||||
s << expr << "=" << v[SIMPLIFY_TEMPVAL].ToString() << " , ";
|
||||
else
|
||||
{
|
||||
MIValue &data = v[SIMPLIFY_VALUE];
|
||||
if(data.IsError())
|
||||
return;
|
||||
if(data.IsString())
|
||||
{
|
||||
// skip vtbls... quite useless
|
||||
if(expr.StartsWith("_vptr."))
|
||||
return;
|
||||
|
||||
String d = data.ToString();
|
||||
if(!d.StartsWith("<"))
|
||||
s << v[SIMPLIFY_EXPR].ToString() << "=" << d << " , ";
|
||||
}
|
||||
else if(data.IsTuple())
|
||||
CollectShort0(data, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(val.IsArray())
|
||||
{
|
||||
for(int i = 0; i < val.GetCount(); i++)
|
||||
{
|
||||
MIValue &v = val[i];
|
||||
|
||||
if(v.Find(SIMPLIFY_TEMPVAL) >= 0)
|
||||
s << v[SIMPLIFY_TEMPVAL].ToString() << " , ";
|
||||
else
|
||||
{
|
||||
MIValue &data = v[SIMPLIFY_VALUE];
|
||||
if(data.IsError())
|
||||
return;
|
||||
if(data.IsString())
|
||||
{
|
||||
String d = data.ToString();
|
||||
if(!d.StartsWith("<"))
|
||||
s << d << " , ";
|
||||
}
|
||||
else if(data.IsTuple())
|
||||
CollectShort0(data, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// collect evaluated variables got with Evaluate
|
||||
// into a vector of single-line string for short display
|
||||
String Gdb_MI2::CollectVariablesShort(MIValue &val)
|
||||
{
|
||||
String s;
|
||||
|
||||
CollectShort0(val, s);
|
||||
if(s == "")
|
||||
s = "<can't evaluate>";
|
||||
else if(s.EndsWith(" , "))
|
||||
s = s.Left(s.GetCount()-3);
|
||||
return s;
|
||||
}
|
||||
|
|
@ -1,5 +1,154 @@
|
|||
#include "Debuggers.h"
|
||||
#include <ide/ide.h>
|
||||
|
||||
// sync explorer pane
|
||||
#ifdef flagMT
|
||||
void Gdb_MI2::SyncExplorer()
|
||||
{
|
||||
// re-enter if called from main thread
|
||||
if(IsMainThread())
|
||||
{
|
||||
debugThread.Start(THISBACK(SyncExplorer));
|
||||
return;
|
||||
}
|
||||
|
||||
INTERLOCKED {
|
||||
IncThreadRunning();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
VectorMap<String, String> prev = DataMap(explorer);
|
||||
|
||||
// get expression from editfield
|
||||
String expr;
|
||||
{
|
||||
GuiLock __;
|
||||
expr = explorerExprEdit;
|
||||
}
|
||||
|
||||
// create a vari object and evaluate '*this' expression
|
||||
VarItem vItem(this);
|
||||
vItem.Evaluate(expr);
|
||||
|
||||
RaiseIfStop();
|
||||
|
||||
// get children if complex variable
|
||||
Vector<VarItem> children;
|
||||
if(vItem.kind == VarItem::COMPLEX)
|
||||
children = vItem.GetChildren();
|
||||
else
|
||||
children << vItem;
|
||||
|
||||
RaiseIfStop();
|
||||
|
||||
// fill explorer memners expressions, short expressions and values
|
||||
explorerExpressions.Clear();
|
||||
explorerValues.Clear();
|
||||
for(int iVar = 0; iVar < children.GetCount(); iVar++)
|
||||
{
|
||||
VarItem &v = children[iVar];
|
||||
explorerExpressions << v.shortExpression;
|
||||
explorerValues << v.value;
|
||||
}
|
||||
|
||||
RaiseIfStop();
|
||||
|
||||
// update 'this' pane
|
||||
FillPane(explorer, explorerExpressions, explorerValues);
|
||||
|
||||
// simplify batch
|
||||
for(int iVar = 0; iVar < children.GetCount(); iVar++)
|
||||
{
|
||||
RaiseIfStop();
|
||||
while(children[iVar].Simplify())
|
||||
RaiseIfStop();
|
||||
|
||||
VarItem &v = children[iVar];
|
||||
|
||||
explorerValues[iVar] = v.value;
|
||||
{
|
||||
GuiLock __;
|
||||
explorer.Set(iVar, 1, v.value);
|
||||
}
|
||||
}
|
||||
|
||||
// when finished, mark changed values
|
||||
MarkChanged(prev, explorer);
|
||||
|
||||
explorerSynced = true;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
explorerSynced = false;
|
||||
}
|
||||
|
||||
DecThreadRunning();
|
||||
}
|
||||
}
|
||||
#else
|
||||
void Gdb_MI2::SyncExplorer(Vector<VarItem> children)
|
||||
{
|
||||
static VectorMap<String, String> prev;
|
||||
|
||||
if(children.IsEmpty())
|
||||
{
|
||||
prev = DataMap(explorer);
|
||||
|
||||
// get expression from editfield
|
||||
String expr = explorerExprEdit;
|
||||
if(expr.IsEmpty())
|
||||
{
|
||||
explorerSynced = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// create a vari object and evaluate the expression
|
||||
VarItem vItem(this, expr);
|
||||
|
||||
// get children if complex variable
|
||||
if(vItem.kind == VarItem::COMPLEX)
|
||||
children = vItem.GetChildren();
|
||||
else
|
||||
children << vItem;
|
||||
|
||||
// fill explorer memners expressions, short expressions and values
|
||||
explorerExpressions.Clear();
|
||||
explorerValues.Clear();
|
||||
for(int iVar = 0; iVar < children.GetCount(); iVar++)
|
||||
{
|
||||
VarItem &v = children[iVar];
|
||||
explorerExpressions << v.shortExpression;
|
||||
explorerValues << v.value;
|
||||
}
|
||||
|
||||
// update 'this' pane
|
||||
FillPane(explorer, explorerExpressions, explorerValues);
|
||||
|
||||
exploreCallback.Set(500, THISBACK1(SyncExplorer, children));
|
||||
return;
|
||||
}
|
||||
|
||||
// simplify batch
|
||||
for(int iVar = 0; iVar < children.GetCount(); iVar++)
|
||||
{
|
||||
if(children[iVar].Simplify())
|
||||
{
|
||||
VarItem &v = children[iVar];
|
||||
explorer.Set(iVar, 1, v.value);
|
||||
explorerValues[iVar] = v.value;
|
||||
exploreCallback.Set(100, THISBACK1(SyncExplorer, children));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for(int iVar = 0; iVar < children.GetCount(); iVar++)
|
||||
explorer.Set(iVar, 1, children[iVar].value);
|
||||
|
||||
// when finished, mark changed values
|
||||
MarkChanged(prev, explorer);
|
||||
explorerSynced = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Gdb_MI2::doExplore(String const &expr, bool appendHistory)
|
||||
{
|
||||
|
|
@ -20,57 +169,9 @@ void Gdb_MI2::doExplore(String const &expr, bool appendHistory)
|
|||
explorerHistoryExpressions.Add(expr);
|
||||
}
|
||||
|
||||
// evaluate the expression, direct deep evaluation here
|
||||
String s = "<can't evaluate>";
|
||||
MIValue valExpr = MICmd("data-evaluate-expression " + expr);
|
||||
|
||||
if(valExpr.IsTuple() && valExpr.Find("value") >= 0)
|
||||
{
|
||||
MIValue const &tup = valExpr.Get("value");
|
||||
if(tup.IsString())
|
||||
s = tup.ToString();
|
||||
}
|
||||
explorerSynced = false;
|
||||
SyncExplorer();
|
||||
|
||||
// special behaviour for pointers and references
|
||||
// try to de-reference them
|
||||
if(s.StartsWith("@0x") || s.StartsWith("0x"))
|
||||
{
|
||||
MIValue valExpr;
|
||||
|
||||
if(s.StartsWith("@0x"))
|
||||
// reference
|
||||
valExpr = MICmd("data-evaluate-expression *&" + expr);
|
||||
else
|
||||
// pointer
|
||||
valExpr = MICmd("data-evaluate-expression *" + expr);
|
||||
|
||||
if(valExpr.IsTuple() && valExpr.Find("value") >= 0)
|
||||
{
|
||||
MIValue const &tup = valExpr.Get("value");
|
||||
if(tup.IsString())
|
||||
s = tup.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
s = expr + "=" + s;
|
||||
MIValue val(s);
|
||||
|
||||
val.PackNames();
|
||||
AddAttribs("", val);
|
||||
val.FixArrays();
|
||||
|
||||
bool more = TypeSimplify(val, false);
|
||||
while(more)
|
||||
more = TypeSimplify(val, true);
|
||||
|
||||
// collect results
|
||||
Vector<String> vals;
|
||||
Vector<int> hints;
|
||||
CollectVariables(val, explorerChildExpressions, vals, hints);
|
||||
|
||||
// update locals pane
|
||||
FillPane(explorer, explorerChildExpressions, vals);
|
||||
|
||||
// update history buttons visibility
|
||||
explorerBackBtn.Enable(explorerHistoryPos > 0);
|
||||
explorerForwardBtn.Enable(explorerHistoryPos < explorerHistoryExpressions.GetCount() - 1);
|
||||
|
|
@ -115,8 +216,8 @@ void Gdb_MI2::onExplorerChild()
|
|||
int line = explorer.GetCursor();
|
||||
if(line < 0)
|
||||
return;
|
||||
if(line < explorerChildExpressions.GetCount())
|
||||
doExplore(explorerChildExpressions[line], true);
|
||||
if(line < explorerExpressions.GetCount())
|
||||
doExplore(explorerExpressions[line], true);
|
||||
}
|
||||
|
||||
void Gdb_MI2::onExplorerBack()
|
||||
|
|
|
|||
313
uppsrc/ide/Debuggers/Gdb_MI2Gdb.cpp
Normal file
313
uppsrc/ide/Debuggers/Gdb_MI2Gdb.cpp
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
#include "Debuggers.h"
|
||||
|
||||
#ifdef PLATFORM_POSIX
|
||||
// sends a ctrl-c to debugger, returns true on success, false otherwise
|
||||
bool Gdb_MI2::InterruptDebugger(void)
|
||||
{
|
||||
int killed = 0;
|
||||
for(int iProc = 0; iProc < processes.GetCount(); iProc++)
|
||||
if(kill(processes[iProc], SIGINT) == 0)
|
||||
killed++;
|
||||
return killed;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PLATFORM_POSIX
|
||||
// current command break support -- ONLY POSIX, by now
|
||||
// used to speed up operations in MT mode
|
||||
bool Gdb_MI2::InterruptCommand(void)
|
||||
{
|
||||
try
|
||||
{
|
||||
LocalProcess &proc = dynamic_cast<LocalProcess &>(*dbg);
|
||||
pid_t pid = proc.GetPid();
|
||||
bool res = (kill(pid, SIGINT) == 0);
|
||||
return res;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
bool Gdb_MI2::InterruptCommand(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// read debugger output analyzing command responses and async output
|
||||
// things are quite tricky because debugger output seems to be
|
||||
// slow and we have almost no terminator to stop on -- (gdb) is not
|
||||
// so reliable as it can happen (strangely) in middle of nothing
|
||||
MIValue Gdb_MI2::ParseGdb(String const &output, bool wait)
|
||||
{
|
||||
MIValue res;
|
||||
|
||||
// parse result data
|
||||
StringStream ss(output);
|
||||
while(!ss.IsEof())
|
||||
{
|
||||
String s = TrimBoth(ss.GetLine());
|
||||
|
||||
// check 'running' and 'stopped' async output
|
||||
if(s.StartsWith("*running"))
|
||||
{
|
||||
started = true;
|
||||
stopReason.Clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
else if(s.StartsWith("*stopped"))
|
||||
{
|
||||
stopped = true;
|
||||
s = '{' + s.Mid(9) + '}';
|
||||
stopReason = MIValue(s);
|
||||
continue;
|
||||
}
|
||||
|
||||
// catch process start/stop and store/remove pids
|
||||
else if(s.StartsWith("=thread-group-started,id="))
|
||||
{
|
||||
String id, pid;
|
||||
int i = s.Find("id=");
|
||||
if(i < 0)
|
||||
continue;
|
||||
i += 4;
|
||||
while(s[i] && s[i] != '"')
|
||||
id.Cat(s[i++]);
|
||||
i = s.Find("pid=");
|
||||
if(i < 0)
|
||||
continue;
|
||||
i += 5;
|
||||
while(s[i] && s[i] != '"')
|
||||
pid.Cat(s[i++]);
|
||||
|
||||
processes.Add(id, atoi(pid));
|
||||
continue;
|
||||
}
|
||||
|
||||
else if(s.StartsWith("=thread-group-exited,id="))
|
||||
{
|
||||
String id;
|
||||
int i = s.Find("id=");
|
||||
if(i < 0)
|
||||
continue;
|
||||
i += 4;
|
||||
while(s[i] && s[i] != '"')
|
||||
id.Cat(s[i++]);
|
||||
i = processes.Find(id);
|
||||
if(i >= 0)
|
||||
processes.Remove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip asynchronous responses
|
||||
// in future, we could be gather/use them
|
||||
if(s[0] == '*'|| s[0] == '=')
|
||||
continue;
|
||||
|
||||
// here handling of command responses
|
||||
// we're not interested either, as we use MI interface
|
||||
if(s[0] == '~')
|
||||
continue;
|
||||
|
||||
// here handling of target output
|
||||
// well, for now discard this one too, but it should go on console output
|
||||
if(s[0] == '~')
|
||||
continue;
|
||||
|
||||
// here handling of gdb log/debug message
|
||||
// not interesting here
|
||||
if(s[0] == '&')
|
||||
continue;
|
||||
|
||||
// now we SHALL have something starting with any of
|
||||
// // "^done", "^running", "^connected", "^error" or "^exit" records
|
||||
if(s.StartsWith("^done") || s.StartsWith("^running"))
|
||||
{
|
||||
// here we've got succesful command output in list form, if any
|
||||
// shall skip the comma; following can be a serie of pairs,
|
||||
// or directly an array of maps in form of :
|
||||
// [{key="value",key="value",...},{key="value"...}...]
|
||||
|
||||
int i = 5; // just skip shortest, ^done
|
||||
while(s[i] && s[i] != ',')
|
||||
i++;
|
||||
if(!s[i])
|
||||
continue;
|
||||
i++;
|
||||
if(!s[i])
|
||||
continue;
|
||||
res = MIValue(s.Mid(i));
|
||||
continue;
|
||||
}
|
||||
else if(s.StartsWith("^error"))
|
||||
{
|
||||
// first array element is reserved for command result status
|
||||
s = s.Right(12); // '^error,msg=\"'
|
||||
s = s.Left(s.GetCount() - 1);
|
||||
res.SetError(s);
|
||||
}
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
MIValue Gdb_MI2::ReadGdb(bool wait)
|
||||
{
|
||||
String output, s;
|
||||
MIValue res;
|
||||
|
||||
// blocking path
|
||||
// waits for 2 minutes max, then return empty value
|
||||
// some commands (in particular if they return python exceptions)
|
||||
// have a delay between returned exception text and command result
|
||||
// so we shall wait up to the final (gdb)
|
||||
bool stop = false;
|
||||
int retries = 120 * 50;
|
||||
while(dbg && --retries && !stop)
|
||||
{
|
||||
dbg->Read(s);
|
||||
StringStream ss(s);
|
||||
while(!ss.IsEof())
|
||||
{
|
||||
String s2 = ss.GetLine();
|
||||
output << s2 << "\n";
|
||||
|
||||
// wait till (gdb) end marker appears
|
||||
s2 = TrimBoth(s2);
|
||||
if(s2 == "(gdb)" || s2 == "&\"quit\\n\"")
|
||||
{
|
||||
stop = true;
|
||||
#ifdef flagMT
|
||||
// exit if in service threa and thread is stoppint
|
||||
if(!IsMainThread() && IsStopThread())
|
||||
throw BreakExc();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// non-blocking quick exit
|
||||
if(!wait)
|
||||
break;
|
||||
|
||||
Sleep(20);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(output.IsEmpty())
|
||||
return res;
|
||||
|
||||
return ParseGdb(output);
|
||||
}
|
||||
|
||||
// new-way commands using GDB MI interface
|
||||
// on input : MI interface command line
|
||||
// on output : an MIValue containing GDB output
|
||||
// STREAM OUTPUT
|
||||
// ~ command response
|
||||
// @ target output
|
||||
// & gdb log/debug messages
|
||||
//
|
||||
// RESULT RECORDS
|
||||
// "^done" [ "," results ]
|
||||
// "^running" same as "^done"
|
||||
// "^connected" gdb has connected to a remote target.
|
||||
// "^error" "," c-string The operation failed. The c-string contains the corresponding error message.
|
||||
// "^exit" gdb has terminate
|
||||
//
|
||||
// ASYNCHRONOUS RECORDS
|
||||
// *running,thread-id="thread"
|
||||
// *stopped,reason="reason",thread-id="id",stopped-threads="stopped",core="core"
|
||||
// =thread-group-added,id="id"
|
||||
// =thread-group-removed,id="id"
|
||||
// =thread-group-started,id="id",pid="pid"
|
||||
// =thread-group-exited,id="id"[,exit-code="code"]
|
||||
// =thread-created,id="id",group-id="gid"
|
||||
// =thread-exited,id="id",group-id="gid"
|
||||
// =thread-selected,id="id"
|
||||
// =library-loaded,...
|
||||
// =library-unloaded,...
|
||||
// =breakpoint-created,bkpt={...}
|
||||
// =breakpoint-modified,bkpt={...}
|
||||
// =breakpoint-deleted,bkpt={...}
|
||||
//
|
||||
// FRAME INFO INSIDE RESPONSES
|
||||
// level The level of the stack frame. The innermost frame has the level of zero. This field is always present.
|
||||
// func The name of the function corresponding to the frame. This field may be absent if gdb is unable to determine the function name.
|
||||
// addr The code address for the frame. This field is always present.
|
||||
// file The name of the source files that correspond to the frame's code address. This field may be absent.
|
||||
// line The source line corresponding to the frames' code address. This field may be absent.
|
||||
// from The name of the binary file (either executable or shared library) the corresponds to the frame's code address. This field may be absent.
|
||||
|
||||
// THREAD INFO INSIDE RESPONSES
|
||||
// id The numeric id assigned to the thread by gdb. This field is always present.
|
||||
// target-id Target-specific string identifying the thread. This field is always present.
|
||||
// details Additional information about the thread provided by the target. It is supposed to be human-readable and not interpreted by the frontend. This field is optional.
|
||||
// state Either `stopped' or `running', depending on whether the thread is presently running. This field is always present.
|
||||
// core The value of this field is an integer number of the processor core the thread was last seen on. This field is optional.
|
||||
//
|
||||
// REMARKS : by now, we just handle synchronous output and check asynchronous one just to detect
|
||||
// debugger run/stop status -- all remaining asynchrnonous output is discarded
|
||||
MIValue Gdb_MI2::MICmd(const char *cmdLine)
|
||||
{
|
||||
MIValue res;
|
||||
|
||||
#ifdef flagMT
|
||||
// on MT, we interrupt all non-main threads
|
||||
// issued GDB commands (which normally can lag several seconds...)
|
||||
// before issuing the command
|
||||
if(IsMainThread() && IsThreadRunning())
|
||||
{
|
||||
// signal all other threads to stop
|
||||
SetStopThread(true);
|
||||
|
||||
// interrupt any active GDB command
|
||||
InterruptCommand();
|
||||
|
||||
// ugly hack, otherwise service thread can deadlock
|
||||
// MUST CHECK THIS ONE....
|
||||
int n = LeaveGuiMutexAll();
|
||||
|
||||
// give some time to recover
|
||||
do
|
||||
{
|
||||
Sleep(20);
|
||||
}
|
||||
while(IsThreadRunning());
|
||||
|
||||
// RE-ENTER GUI MUTEX -- SEE ABOVE...
|
||||
EnterGuiMutex(n);
|
||||
|
||||
// remove thread stopping flag
|
||||
SetStopThread(false);
|
||||
}
|
||||
|
||||
// quick exit for service thread
|
||||
if(!IsMainThread() && IsStopThread())
|
||||
throw BreakExc();
|
||||
|
||||
// lock other thread's access
|
||||
INTERLOCKED {
|
||||
#endif
|
||||
|
||||
// sends command to debugger and get result data
|
||||
// should handle dbg unexpected termination ?
|
||||
if(!dbg || !dbg->IsRunning() /* || IdeIsDebugLock() */)
|
||||
return MIValue();
|
||||
|
||||
// consume previous output from gdb... don't know why sometimes
|
||||
// is there and gives problems to MI interface. We shall maybe
|
||||
// parse and store it somewhere
|
||||
ReadGdb(false);
|
||||
|
||||
dbg->Write(String("-") + cmdLine + "\n");
|
||||
res = ReadGdb();
|
||||
#ifdef flagMT
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
|
@ -419,11 +419,26 @@ MIValue::MIValue(MIValue pick_ &v)
|
|||
MIValue::MIValue(String const &s)
|
||||
{
|
||||
Parse(s);
|
||||
|
||||
// tuple with 1 element and unnamed key is a string
|
||||
if(IsTuple() && tuple.GetCount() == 1 && tuple.GetKey(0) == "<UNNAMED>")
|
||||
{
|
||||
type = MIString;
|
||||
string = tuple[0];
|
||||
tuple.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
MIValue &MIValue::operator=(String const &s)
|
||||
{
|
||||
Parse(s);
|
||||
// tuple with 1 element and unnamed key is a string
|
||||
if(IsTuple() && tuple.GetCount() == 1 && tuple.GetKey(0) == "<UNNAMED>")
|
||||
{
|
||||
type = MIString;
|
||||
string = tuple[0];
|
||||
tuple.Clear();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#define _ide_Debuggers_TypeSimplify_h_
|
||||
|
||||
#include "Debuggers.h"
|
||||
#include <ide/ide.h>
|
||||
#include "VarItem.h"
|
||||
|
||||
#define SIMPLIFY_EXPR "<!EXPR>"
|
||||
#define SIMPLIFY_VALUE "<!VALUE>"
|
||||
|
|
@ -20,10 +20,12 @@
|
|||
#define SIMPLIFY_MAP "<!MAP>"
|
||||
|
||||
// Simplifier handler
|
||||
// parameters:
|
||||
// gdb handler to debugger object -- used mostly to inspect deeper
|
||||
// expRoot gdb evaluable expression of 'val' expression root. The one used to get the 'val'.
|
||||
typedef bool (*TYPE_SIMPLIFIER_HANDLER)(Gdb_MI2 &gdb, MIValue &val, bool deep);
|
||||
// step is the simplifying step, used for arrays and maps
|
||||
// step 0 -- base simplify, no deep evaluation of containers
|
||||
// step i -- deep evaluation of element 'i' of container
|
||||
// returns number of needed steps to complete optimization
|
||||
// all this stuff is needed to allow gui to have priority on data display
|
||||
typedef int (*TYPE_SIMPLIFIER_HANDLER)(VarItem &varItem, int step);
|
||||
|
||||
void RegisterSimplifier(const char *pattern, TYPE_SIMPLIFIER_HANDLER handler);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,19 @@
|
|||
#define EVALDEEP_ARRAYMAP 5
|
||||
#define EVALDEEP_INDEX 5
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SIMPLIFIERS MUST BE CODED AS STATE MACHINES -- THEY'LL BE CALLED MANY TIMES, WITH A 'step' PARAMETER
|
||||
// STEP = 0 MEANS BASE SIMPLIFY AND CHECK IF MORE STEPS ARE NEEDED
|
||||
// RETURN NEXT STEP, OR 0 IF NONE
|
||||
// STEP = N MEANS A SIMPLIFY STEP
|
||||
// RETURN NEXT STEP, OR 0 IF NONE
|
||||
// THEY MUST CHANGE 'value' MEMBER OF PASSED VarItem object ON EACH STEP
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#define SLEN 15
|
||||
#define LLEN 2
|
||||
static bool UppStringSimplify(Gdb_MI2 &gdb, MIValue &val, bool deep)
|
||||
static int UppStringSimplify(VarItem &varItem, int step)
|
||||
{
|
||||
union
|
||||
{
|
||||
|
|
@ -21,402 +31,412 @@ static bool UppStringSimplify(Gdb_MI2 &gdb, MIValue &val, bool deep)
|
|||
dword w[4];
|
||||
qword q[2];
|
||||
} u;
|
||||
|
||||
// see Upp::String code for how it works....
|
||||
try
|
||||
{
|
||||
MIValue &v = val[SIMPLIFY_VALUE][0][0][0][0][0][SIMPLIFY_VALUE];
|
||||
MIValue &unn = v[1][SIMPLIFY_VALUE];
|
||||
|
||||
if(!v.IsTuple() || !unn.IsTuple())
|
||||
return false;
|
||||
if(unn.Find("chr") < 0)
|
||||
return false;
|
||||
String chrs = unn["chr"][SIMPLIFY_VALUE];
|
||||
memcpy(u.chr, ~chrs, 16);
|
||||
|
||||
bool isSmall = (u.chr[14] == 0);
|
||||
String s;
|
||||
if(isSmall)
|
||||
{
|
||||
byte len = u.chr[SLEN];
|
||||
s = chrs.Left(len);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(unn.Find("ptr") < 0)
|
||||
return false;
|
||||
dword len = u.w[LLEN];
|
||||
s = unn["ptr"][SIMPLIFY_VALUE].ToString();
|
||||
|
||||
// strip address...
|
||||
int i = s.Find('"');
|
||||
if(i >= 0)
|
||||
{
|
||||
s = s.Mid(i+1);
|
||||
s = s.Left(s.GetCount()-1);
|
||||
}
|
||||
s = s.Left(len);
|
||||
}
|
||||
val[SIMPLIFY_VALUE].Set("\"" + s + "\"");
|
||||
val.FindAdd(SIMPLIFY_HINT, SIMPLIFY_SIMPLE);
|
||||
|
||||
// no need for further evaluation on this object
|
||||
return false;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool UppVectorSimplify(Gdb_MI2 &gdb, MIValue &val, bool deep)
|
||||
{
|
||||
// if we're just doing first scan phase, signal that we need further evaluation later
|
||||
#ifdef EVALDEEP
|
||||
if(!deep)
|
||||
return true;
|
||||
#endif
|
||||
try
|
||||
{
|
||||
MIValue &v = val[SIMPLIFY_VALUE];
|
||||
String vectorExpr = v["vector"][SIMPLIFY_EXPR];
|
||||
int items = atoi(v["items"][SIMPLIFY_VALUE].ToString());
|
||||
|
||||
val.FindAdd(SIMPLIFY_HINT, SIMPLIFY_ARRAY);
|
||||
val.FindAdd(SIMPLIFY_START, "0");
|
||||
val.FindAdd(SIMPLIFY_COUNT, FormatInt(items));
|
||||
|
||||
// display max 5 elements of array, starting from 0 and ONLY if elements evaluate to a simple type
|
||||
// otherwise display {...} string
|
||||
// to do this, we shall ask gdb to evaluate the undelying vector
|
||||
String vals;
|
||||
int count = min(EVALDEEP_VECTOR, items);
|
||||
//RLOG("COUNT : " << count);
|
||||
#ifdef EVALDEEP
|
||||
if(count)
|
||||
{
|
||||
vals = ": [ ... ]";
|
||||
String expr = vectorExpr + Format("[0]@%d", count);
|
||||
MIValue vi = gdb.Evaluate(expr);
|
||||
//RLOG(vi.Dump());
|
||||
if(vi.IsArray() && vi.GetCount() && vi[0][SIMPLIFY_VALUE].IsString())
|
||||
{
|
||||
vals = ": [ ";
|
||||
for(int i = 0; i < vi.GetCount(); i++)
|
||||
vals = vals + vi[i][SIMPLIFY_VALUE].ToString() + " , ";
|
||||
if(count < items)
|
||||
vals << "... ]";
|
||||
else
|
||||
vals = vals.Left(vals.GetCount() - 2) + "]";
|
||||
}
|
||||
}
|
||||
#else
|
||||
if(count)
|
||||
vals = ": [ ... ]";
|
||||
#endif
|
||||
vals = Format("Upp::Vector with %d elements %s", items, vals);
|
||||
val[SIMPLIFY_VALUE].Set(vals);
|
||||
|
||||
// signal we've done a deep evaluation
|
||||
#ifdef EVALDEEP
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool UppVectorMapSimplify(Gdb_MI2 &gdb, MIValue &val, bool deep)
|
||||
{
|
||||
//RLOG(val.Dump());
|
||||
// if we're just doing first scan phase, signal that we need further evaluation later
|
||||
#ifdef EVALDEEP
|
||||
if(!deep)
|
||||
return true;
|
||||
#endif
|
||||
try
|
||||
{
|
||||
MIValue &v = val[SIMPLIFY_VALUE];
|
||||
|
||||
MIValue &keyPtr = v[1][0]["key"][0][1][0][0][0];
|
||||
String keyExpr = keyPtr["vector"][SIMPLIFY_EXPR].ToString();
|
||||
int items = atoi(keyPtr["items"][SIMPLIFY_VALUE].ToString());
|
||||
|
||||
MIValue &valPtr = v[1][0]["value"][SIMPLIFY_VALUE];;
|
||||
String valExpr = valPtr["vector"][SIMPLIFY_EXPR].ToString();
|
||||
|
||||
val.FindAdd(SIMPLIFY_HINT, SIMPLIFY_MAP);
|
||||
val.FindAdd(SIMPLIFY_START, "0");
|
||||
val.FindAdd(SIMPLIFY_COUNT, FormatInt(items));
|
||||
|
||||
|
||||
// display max 5 elements of map, starting from 0 and ONLY if elements evaluate to a simple type
|
||||
// otherwise display {...} string
|
||||
// to do this, we shall ask gdb to evaluate the undelying vector
|
||||
String vals;
|
||||
int count = min(EVALDEEP_VECTORMAP, items);
|
||||
#ifdef EVALDEEP
|
||||
if(count)
|
||||
{
|
||||
vals = ": { ... }";
|
||||
String kExpr = keyExpr + Format("[0]@%d", count);
|
||||
MIValue viKey = gdb.Evaluate(kExpr);
|
||||
if(viKey.IsArray() && viKey[0][SIMPLIFY_VALUE].IsString())
|
||||
{
|
||||
String vExpr = valExpr + Format("[0]@%d", count);
|
||||
MIValue viVal = gdb.Evaluate(vExpr);
|
||||
if(viVal.IsArray() && viVal[0][SIMPLIFY_VALUE].IsString())
|
||||
{
|
||||
vals = ": { ";
|
||||
for(int i = 0; i < viKey.GetCount(); i++)
|
||||
vals << "( " << viKey[i][SIMPLIFY_VALUE].ToString() << " , " << viVal[i][SIMPLIFY_VALUE].ToString() << " ) , ";
|
||||
if(count < items)
|
||||
vals << "... }";
|
||||
else
|
||||
vals = vals.Left(vals.GetCount() - 2) + "}";
|
||||
}
|
||||
//RLOG(vals);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if(count)
|
||||
vals = ": { ... }";
|
||||
#endif
|
||||
vals = Format("Upp::VectorMap with %d elements %s", items, vals);
|
||||
val[SIMPLIFY_VALUE].Set(vals);
|
||||
|
||||
// signal we've done a deep evaluation
|
||||
#ifdef EVALDEEP
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool UppArraySimplify(Gdb_MI2 &gdb, MIValue &val, bool deep)
|
||||
{
|
||||
//RLOG(val.Dump());
|
||||
// if we're just doing first scan phase, signal that we need further evaluation later
|
||||
#ifdef EVALDEEP
|
||||
if(!deep)
|
||||
return true;
|
||||
#endif
|
||||
try
|
||||
{
|
||||
MIValue &v = val[SIMPLIFY_VALUE]["vector"][SIMPLIFY_VALUE];
|
||||
|
||||
String vectorExpr = v["vector"][SIMPLIFY_EXPR];
|
||||
int items = atoi(v["items"][SIMPLIFY_VALUE].ToString());
|
||||
|
||||
val.FindAdd(SIMPLIFY_HINT, SIMPLIFY_ARRAY);
|
||||
val.FindAdd(SIMPLIFY_START, "0");
|
||||
val.FindAdd(SIMPLIFY_COUNT, FormatInt(items));
|
||||
|
||||
// display max 5 elements of array, starting from 0 and ONLY if elements evaluate to a simple type
|
||||
// otherwise display {...} string
|
||||
// to do this, we shall ask gdb to evaluate the undelying vector
|
||||
String vals;
|
||||
int count = min(EVALDEEP_ARRAY, items);
|
||||
#ifdef EVALDEEP
|
||||
if(count)
|
||||
{
|
||||
// pre-fetch main array variable to speedup element request
|
||||
gdb.MICmd("gdb-set variable $thearray=" + vectorExpr);
|
||||
|
||||
// get array elements
|
||||
vals = ": [ ... ]";
|
||||
String expr = "{";
|
||||
for(int i = 0; i < count; i++)
|
||||
expr << Format("$thearray[%d][0]", i) << ",";
|
||||
expr = expr.Left(expr.GetCount()-1) + "}";
|
||||
MIValue vi = gdb.Evaluate(expr);
|
||||
|
||||
if(vi.IsArray() && vi.GetCount() && vi[0][SIMPLIFY_VALUE].IsString())
|
||||
{
|
||||
vals = ": [ ";
|
||||
for(int i = 0; i < vi.GetCount(); i++)
|
||||
vals = vals + vi[i][SIMPLIFY_VALUE].ToString() + " , ";
|
||||
if(count < items)
|
||||
vals << "... ]";
|
||||
else
|
||||
vals = vals.Left(vals.GetCount() - 2) + "]";
|
||||
}
|
||||
}
|
||||
#else
|
||||
if(count)
|
||||
vals = ": [ ... ]";
|
||||
#endif
|
||||
vals = Format("Upp::Array with %d elements %s", items, vals);
|
||||
val[SIMPLIFY_VALUE].Set(vals);
|
||||
|
||||
// signal we've done a deep evaluation
|
||||
#ifdef EVALDEEP
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool UppArrayMapSimplify(Gdb_MI2 &gdb, MIValue &val, bool deep)
|
||||
{
|
||||
//RLOG(val.Dump());
|
||||
// if we're just doing first scan phase, signal that we need further evaluation later
|
||||
#ifdef EVALDEEP
|
||||
if(!deep)
|
||||
return true;
|
||||
#endif
|
||||
try
|
||||
{
|
||||
MIValue &v = val[SIMPLIFY_VALUE];
|
||||
|
||||
MIValue &keyPtr = v[1][0]["key"][0][1][0][0][0];
|
||||
String keyExpr = keyPtr["vector"][SIMPLIFY_EXPR].ToString();
|
||||
int items = atoi(keyPtr["items"][SIMPLIFY_VALUE].ToString());
|
||||
|
||||
MIValue &valPtr = v[1][0]["value"][SIMPLIFY_VALUE];;
|
||||
String valExpr = valPtr["vector"][SIMPLIFY_VALUE]["vector"][SIMPLIFY_EXPR].ToString();
|
||||
|
||||
val.FindAdd(SIMPLIFY_HINT, SIMPLIFY_MAP);
|
||||
val.FindAdd(SIMPLIFY_START, "0");
|
||||
val.FindAdd(SIMPLIFY_COUNT, FormatInt(items));
|
||||
|
||||
|
||||
// display max 3 elements of map, starting from 0 and ONLY if elements evaluate to a simple type
|
||||
// otherwise display {...} string
|
||||
// to do this, we shall ask gdb to evaluate the undelying vector
|
||||
String vals;
|
||||
int count = min(EVALDEEP_ARRAYMAP, items);
|
||||
#ifdef EVALDEEP
|
||||
if(count)
|
||||
{
|
||||
// pre-fetch main array variable to speedup element request
|
||||
gdb.MICmd("gdb-set variable $thearray=" + valExpr);
|
||||
|
||||
vals = ": { ... }";
|
||||
String kExpr = keyExpr + Format("[0]@%d", count);
|
||||
MIValue viKey = gdb.Evaluate(kExpr);
|
||||
if(viKey.IsArray() && viKey[0][SIMPLIFY_VALUE].IsString())
|
||||
{
|
||||
String vExpr = "{";
|
||||
for(int i = 0; i < count; i++)
|
||||
vExpr << Format("$thearray[%d][0]", i) << ",";
|
||||
vExpr = vExpr.Left(vExpr.GetCount()-1) + "}";
|
||||
MIValue viVal = gdb.Evaluate(vExpr);
|
||||
|
||||
if(viVal.IsArray() && viVal[0][SIMPLIFY_VALUE].IsString())
|
||||
{
|
||||
vals = ": { ";
|
||||
for(int i = 0; i < viKey.GetCount(); i++)
|
||||
vals << "( " << viKey[i][SIMPLIFY_VALUE].ToString() << " , " << viVal[i][SIMPLIFY_VALUE].ToString() << " ) , ";
|
||||
if(count < items)
|
||||
vals << "... }";
|
||||
else
|
||||
vals = vals.Left(vals.GetCount() - 2) + "}";
|
||||
}
|
||||
//RLOG(vals);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if(count)
|
||||
vals = ": { ... }";
|
||||
#endif
|
||||
vals = Format("Upp::ArrayMap with %d elements %s", items, vals);
|
||||
val[SIMPLIFY_VALUE].Set(vals);
|
||||
|
||||
// signal we've done a deep evaluation
|
||||
#ifdef EVALDEEP
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
catch(...)
|
||||
// see Upp::String code for how it works....
|
||||
MIValue val = varItem.EvaluateExpression(varItem.evaluableExpression + "." + "chr");
|
||||
if(!val.IsString())
|
||||
return 0;
|
||||
String chrs = val.ToString();
|
||||
memcpy(u.chr, ~chrs, 16);
|
||||
|
||||
bool isSmall = (u.chr[14] == 0);
|
||||
String s;
|
||||
if(isSmall)
|
||||
{
|
||||
return false;
|
||||
byte len = u.chr[SLEN];
|
||||
s = chrs.Left(len);
|
||||
}
|
||||
else
|
||||
{
|
||||
dword len = u.w[LLEN];
|
||||
MIValue val = varItem.EvaluateExpression(varItem.evaluableExpression + "." + "ptr[0]@" + FormatInt(len));
|
||||
if(!val.IsString())
|
||||
return 0;
|
||||
s = val.ToString();
|
||||
}
|
||||
varItem.value = "\"" + s + "\"";
|
||||
varItem.kind = VarItem::SIMPLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool UppIndexSimplify(Gdb_MI2 &gdb, MIValue &val, bool deep)
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
static int UppVectorSimplify(VarItem &varItem, int step)
|
||||
{
|
||||
//RLOG(val.Dump());
|
||||
const char *placeHolder = " = [...]";
|
||||
|
||||
// setup item type
|
||||
varItem.kind = VarItem::ARRAY;
|
||||
|
||||
// if we're just doing first scan phase, signal that we need further evaluation later
|
||||
#ifdef EVALDEEP
|
||||
if(!deep)
|
||||
return true;
|
||||
#endif
|
||||
try
|
||||
{
|
||||
MIValue &keyExpr = val[SIMPLIFY_VALUE][1][SIMPLIFY_VALUE]["key"][SIMPLIFY_VALUE];
|
||||
String vectorExpr = keyExpr["vector"][SIMPLIFY_EXPR];
|
||||
int items = atoi(keyExpr["items"][SIMPLIFY_VALUE].ToString());
|
||||
|
||||
val.FindAdd(SIMPLIFY_HINT, SIMPLIFY_ARRAY);
|
||||
val.FindAdd(SIMPLIFY_START, "0");
|
||||
val.FindAdd(SIMPLIFY_COUNT, FormatInt(items));
|
||||
|
||||
// display max 5 elements of array, starting from 0 and ONLY if elements evaluate to a simple type
|
||||
// otherwise display {...} string
|
||||
// to do this, we shall ask gdb to evaluate the undelying vector
|
||||
String vals;
|
||||
int count = min(EVALDEEP_INDEX, items);
|
||||
#ifdef EVALDEEP
|
||||
if(count)
|
||||
{
|
||||
vals = ": [ ... ]";
|
||||
String expr = vectorExpr << Format("[0]@%d", count);
|
||||
MIValue vi = gdb.Evaluate(expr);
|
||||
if(vi.IsArray() && vi.GetCount() && vi[0][SIMPLIFY_VALUE].IsString())
|
||||
{
|
||||
vals = ": [ ";
|
||||
for(int i = 0; i < vi.GetCount(); i++)
|
||||
vals = vals + vi[i][SIMPLIFY_VALUE].ToString() + " , ";
|
||||
if(count < items)
|
||||
vals << "... ]";
|
||||
else
|
||||
vals = vals.Left(vals.GetCount() - 2) + "]";
|
||||
}
|
||||
}
|
||||
if(!step)
|
||||
// next step is 1
|
||||
return 1;
|
||||
#else
|
||||
if(count)
|
||||
vals = ": [ ... ]";
|
||||
varItem.value = placeHolder;
|
||||
return 0;
|
||||
#endif
|
||||
vals = Format("Upp::Index with %d elements %s", items, vals);
|
||||
val[SIMPLIFY_VALUE].Set(vals);
|
||||
|
||||
// just getting items count...
|
||||
if(step == 1)
|
||||
{
|
||||
// initialize default value
|
||||
varItem.value = "<can't evaluate>";
|
||||
|
||||
// get items count
|
||||
MIValue val = varItem.EvaluateExpression(varItem.evaluableExpression + ".items");
|
||||
if(val.IsError() || !val.IsString())
|
||||
return 0;
|
||||
varItem.items = atoi(val.ToString());
|
||||
|
||||
// update value
|
||||
varItem.value = Format("Upp::Vector with %d elements", varItem.items, "");
|
||||
|
||||
// if no elements, just quit
|
||||
if(!varItem.items)
|
||||
return 0;
|
||||
return 2;
|
||||
}
|
||||
|
||||
int count = min(EVALDEEP_VECTOR, varItem.items);
|
||||
|
||||
// start from item 0
|
||||
step -= 2;
|
||||
|
||||
if(!step)
|
||||
varItem.value << " = [ ]";
|
||||
|
||||
// fetch elements, check on first if they're SIMPLE, so displayable
|
||||
VarItem vItem(&varItem.Debugger(), varItem.evaluableExpression + Format(".vector[%d]", step));
|
||||
if(!vItem)
|
||||
{
|
||||
varItem.value = " <can't evaluate contents>";
|
||||
return 0;
|
||||
}
|
||||
if(vItem.kind != VarItem::SIMPLE)
|
||||
{
|
||||
varItem.value = placeHolder;
|
||||
return 0;
|
||||
}
|
||||
vItem.Simplify();
|
||||
const char *sep = step ? " , " : "";
|
||||
varItem.value = varItem.value.Left(varItem.value.GetCount() - 2) + sep + vItem.value + " ]";
|
||||
if(++step >= count)
|
||||
return 0;
|
||||
else
|
||||
return step + 2;
|
||||
}
|
||||
|
||||
// signal we've done a deep evaluation
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
static int UppVectorMapSimplify(VarItem &varItem, int step)
|
||||
{
|
||||
const char *placeHolder = " = {...}";
|
||||
|
||||
// setup item type
|
||||
varItem.kind = VarItem::MAP;
|
||||
|
||||
// if we're just doing first scan phase, signal that we need further evaluation later
|
||||
// if we're just doing first scan phase, signal that we need further evaluation later
|
||||
#ifdef EVALDEEP
|
||||
return true;
|
||||
if(!step)
|
||||
// next step is 1
|
||||
return 1;
|
||||
#else
|
||||
return false;
|
||||
varItem.value = placeHolder;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
catch(...)
|
||||
|
||||
// just getting items count...
|
||||
if(step == 1)
|
||||
{
|
||||
return false;
|
||||
// initialize default value
|
||||
varItem.value = "<can't evaluate>";
|
||||
|
||||
// get items count
|
||||
MIValue val = varItem.EvaluateExpression(varItem.evaluableExpression + ".key.key.items");
|
||||
if(val.IsError() || !val.IsString())
|
||||
return 0;
|
||||
varItem.items = atoi(val.ToString());
|
||||
|
||||
// update value
|
||||
varItem.value = Format("Upp::VectorMap with %d elements", varItem.items, "");
|
||||
|
||||
// if no elements, just quit
|
||||
if(!varItem.items)
|
||||
return 0;
|
||||
return 2;
|
||||
}
|
||||
|
||||
int count = min(EVALDEEP_VECTORMAP, varItem.items);
|
||||
|
||||
// start from item 0
|
||||
step -= 2;
|
||||
|
||||
if(!step)
|
||||
varItem.value << " = { }";
|
||||
|
||||
// fetch elements, check on first if they're SIMPLE, so displayable
|
||||
VarItem kItem(&varItem.Debugger(), varItem.evaluableExpression + Format(".key.key.vector[%d]", step));
|
||||
if(!kItem)
|
||||
{
|
||||
varItem.value = " <can't evaluate contents>";
|
||||
return 0;
|
||||
}
|
||||
// for complex types, just return placeholder
|
||||
if(kItem.kind != VarItem::SIMPLE)
|
||||
{
|
||||
varItem.value = placeHolder;
|
||||
return 0;
|
||||
}
|
||||
kItem.Simplify();
|
||||
|
||||
VarItem vItem(&varItem.Debugger(), varItem.evaluableExpression + Format(".value.vector[%d]", step));
|
||||
if(!vItem)
|
||||
{
|
||||
varItem.value = " <can't evaluate contents>";
|
||||
return 0;
|
||||
}
|
||||
// for complex types, just return placeholder
|
||||
if(vItem.kind != VarItem::SIMPLE)
|
||||
{
|
||||
varItem.value = placeHolder;
|
||||
return true;
|
||||
}
|
||||
vItem.Simplify();
|
||||
|
||||
const char *sep = step ? " , " : "";
|
||||
varItem.value = varItem.value.Left(varItem.value.GetCount() - 2) + sep + "(" + kItem.value + " , " + vItem.value + ") }";
|
||||
if(++step >= count)
|
||||
return 0;
|
||||
else
|
||||
return step + 2;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
static int UppArraySimplify(VarItem &varItem, int step)
|
||||
{
|
||||
const char *placeHolder = " = [...]";
|
||||
|
||||
// setup item type
|
||||
varItem.kind = VarItem::ARRAY;
|
||||
|
||||
// if we're just doing first scan phase, signal that we need further evaluation later
|
||||
#ifdef EVALDEEP
|
||||
if(!step)
|
||||
// next step is 1
|
||||
return 1;
|
||||
#else
|
||||
varItem.value = placeHolder;
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
// just getting items count...
|
||||
if(step == 1)
|
||||
{
|
||||
// initialize default value
|
||||
varItem.value = "<can't evaluate>";
|
||||
|
||||
// get items count
|
||||
MIValue val = varItem.EvaluateExpression(varItem.evaluableExpression + ".vector.items");
|
||||
if(val.IsError() || !val.IsString())
|
||||
return 0;
|
||||
varItem.items = atoi(val.ToString());
|
||||
|
||||
// update value
|
||||
varItem.value = Format("Upp::Array with %d elements", varItem.items, "");
|
||||
|
||||
// if no elements, just quit
|
||||
if(!varItem.items)
|
||||
return 0;
|
||||
return 2;
|
||||
}
|
||||
|
||||
int count = min(EVALDEEP_VECTOR, varItem.items);
|
||||
|
||||
// start from item 0
|
||||
step -= 2;
|
||||
|
||||
if(!step)
|
||||
varItem.value << " = [ ]";
|
||||
|
||||
// fetch elements, check on first if they're SIMPLE, so displayable
|
||||
VarItem vItem(&varItem.Debugger(), varItem.evaluableExpression + Format(".vector.vector[%d][0]", step));
|
||||
if(!vItem)
|
||||
{
|
||||
varItem.value = " <can't evaluate contents>";
|
||||
return 0;
|
||||
}
|
||||
if(vItem.kind != VarItem::SIMPLE)
|
||||
{
|
||||
varItem.value = placeHolder;
|
||||
return 0;
|
||||
}
|
||||
vItem.Simplify();
|
||||
const char *sep = step ? " , " : "";
|
||||
varItem.value = varItem.value.Left(varItem.value.GetCount() - 2) + sep + vItem.value + " ]";
|
||||
if(++step >= count)
|
||||
return 0;
|
||||
else
|
||||
return step + 2;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
static int UppArrayMapSimplify(VarItem &varItem, int step)
|
||||
{
|
||||
const char *placeHolder = " = {...}";
|
||||
|
||||
// setup item type
|
||||
varItem.kind = VarItem::MAP;
|
||||
|
||||
// if we're just doing first scan phase, signal that we need further evaluation later
|
||||
// if we're just doing first scan phase, signal that we need further evaluation later
|
||||
#ifdef EVALDEEP
|
||||
if(!step)
|
||||
// next step is 1
|
||||
return 1;
|
||||
#else
|
||||
varItem.value = placeHolder;
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
// just getting items count...
|
||||
if(step == 1)
|
||||
{
|
||||
// initialize default value
|
||||
varItem.value = "<can't evaluate>";
|
||||
|
||||
// get items count
|
||||
MIValue val = varItem.EvaluateExpression(varItem.evaluableExpression + ".key.key.items");
|
||||
if(val.IsError() || !val.IsString())
|
||||
return 0;
|
||||
varItem.items = atoi(val.ToString());
|
||||
|
||||
// update value
|
||||
varItem.value = Format("Upp::ArrayMap with %d elements", varItem.items, "");
|
||||
|
||||
// if no elements, just quit
|
||||
if(!varItem.items)
|
||||
return 0;
|
||||
return 2;
|
||||
}
|
||||
|
||||
int count = min(EVALDEEP_VECTORMAP, varItem.items);
|
||||
|
||||
// start from item 0
|
||||
step -= 2;
|
||||
|
||||
if(!step)
|
||||
varItem.value << " = { }";
|
||||
|
||||
// fetch elements, check on first if they're SIMPLE, so displayable
|
||||
VarItem kItem(&varItem.Debugger(), varItem.evaluableExpression + Format(".key.key.vector[%d]", step));
|
||||
if(!kItem)
|
||||
{
|
||||
varItem.value = " <can't evaluate contents>";
|
||||
return 0;
|
||||
}
|
||||
// for complex types, just return placeholder
|
||||
if(kItem.kind != VarItem::SIMPLE)
|
||||
{
|
||||
varItem.value = placeHolder;
|
||||
return 0;
|
||||
}
|
||||
kItem.Simplify();
|
||||
|
||||
VarItem vItem(&varItem.Debugger(), varItem.evaluableExpression + Format(".value.vector.vector[%d][0]", step));
|
||||
if(!vItem)
|
||||
{
|
||||
varItem.value = " <can't evaluate contents>";
|
||||
return 0;
|
||||
}
|
||||
// for complex types, just return placeholder
|
||||
if(vItem.kind != VarItem::SIMPLE)
|
||||
{
|
||||
varItem.value = placeHolder;
|
||||
return true;
|
||||
}
|
||||
vItem.Simplify();
|
||||
|
||||
const char *sep = step ? " , " : "";
|
||||
varItem.value = varItem.value.Left(varItem.value.GetCount() - 2) + sep + "(" + kItem.value + " , " + vItem.value + ") }";
|
||||
if(++step >= count)
|
||||
return 0;
|
||||
else
|
||||
return step + 2;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
static int UppIndexSimplify(VarItem &varItem, int step)
|
||||
{
|
||||
const char *placeHolder = " = [...]";
|
||||
|
||||
// setup item type
|
||||
varItem.kind = VarItem::ARRAY;
|
||||
|
||||
// if we're just doing first scan phase, signal that we need further evaluation later
|
||||
#ifdef EVALDEEP
|
||||
if(!step)
|
||||
// next step is 1
|
||||
return 1;
|
||||
#else
|
||||
varItem.value = placeHolder;
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
// just getting items count...
|
||||
if(step == 1)
|
||||
{
|
||||
// initialize default value
|
||||
varItem.value = "<can't evaluate>";
|
||||
|
||||
// get items count
|
||||
MIValue val = varItem.EvaluateExpression(varItem.evaluableExpression + ".key.items");
|
||||
if(val.IsError() || !val.IsString())
|
||||
return 0;
|
||||
varItem.items = atoi(val.ToString());
|
||||
|
||||
// update value
|
||||
varItem.value = Format("Upp::Index with %d elements", varItem.items, "");
|
||||
|
||||
// if no elements, just quit
|
||||
if(!varItem.items)
|
||||
return 0;
|
||||
return 2;
|
||||
}
|
||||
|
||||
int count = min(EVALDEEP_VECTOR, varItem.items);
|
||||
|
||||
// start from item 0
|
||||
step -= 2;
|
||||
|
||||
if(!step)
|
||||
varItem.value << " = [ ]";
|
||||
|
||||
// fetch elements, check on first if they're SIMPLE, so displayable
|
||||
VarItem vItem(&varItem.Debugger(), varItem.evaluableExpression + Format(".key.vector[%d]", step));
|
||||
if(!vItem)
|
||||
{
|
||||
varItem.value = " <can't evaluate contents>";
|
||||
return 0;
|
||||
}
|
||||
if(vItem.kind != VarItem::SIMPLE)
|
||||
{
|
||||
varItem.value = placeHolder;
|
||||
return 0;
|
||||
}
|
||||
vItem.Simplify();
|
||||
const char *sep = step ? " , " : "";
|
||||
varItem.value = varItem.value.Left(varItem.value.GetCount() - 2) + sep + vItem.value + " ]";
|
||||
if(++step >= count)
|
||||
return 0;
|
||||
else
|
||||
return step + 2;
|
||||
}
|
||||
|
||||
// Register the simplifiers
|
||||
REGISTERSIMPLIFIER("<Upp::Moveable<Upp::String,Upp::AString<Upp::String0>>>" , UppStringSimplify);
|
||||
REGISTERSIMPLIFIER("<Upp::MoveableAndDeepCopyOption<Upp::Vector<" , UppVectorSimplify);
|
||||
REGISTERSIMPLIFIER("<Upp::MoveableAndDeepCopyOption<Upp::VectorMap<" , UppVectorMapSimplify);
|
||||
REGISTERSIMPLIFIER("<Upp::MoveableAndDeepCopyOption<Upp::Array<" , UppArraySimplify);
|
||||
REGISTERSIMPLIFIER("<Upp::MoveableAndDeepCopyOption<Upp::ArrayMap<" , UppArrayMapSimplify);
|
||||
REGISTERSIMPLIFIER("<Upp::MoveableAndDeepCopyOption<Upp::Index<" , UppIndexSimplify);
|
||||
|
||||
REGISTERSIMPLIFIER("Upp::String" , UppStringSimplify);
|
||||
REGISTERSIMPLIFIER("Upp::Vector<" , UppVectorSimplify);
|
||||
REGISTERSIMPLIFIER("Upp::VectorMap<" , UppVectorMapSimplify);
|
||||
REGISTERSIMPLIFIER("Upp::Array<" , UppArraySimplify);
|
||||
REGISTERSIMPLIFIER("Upp::ArrayMap<" , UppArrayMapSimplify);
|
||||
REGISTERSIMPLIFIER("Upp::Index<" , UppIndexSimplify);
|
||||
|
|
|
|||
307
uppsrc/ide/Debuggers/VarItem.cpp
Normal file
307
uppsrc/ide/Debuggers/VarItem.cpp
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
#include "VarItem.h"
|
||||
#include "TypeSimplify.h"
|
||||
|
||||
// constructor
|
||||
VarItem::VarItem(Gdb_MI2 *deb)
|
||||
{
|
||||
debugger = deb;
|
||||
Clear();
|
||||
}
|
||||
|
||||
VarItem::VarItem(Gdb_MI2 *deb, String const &expr)
|
||||
{
|
||||
debugger = deb;
|
||||
Evaluate(expr);
|
||||
}
|
||||
|
||||
// copy
|
||||
VarItem::VarItem(pick_ VarItem &v)
|
||||
{
|
||||
debugger = v.debugger;
|
||||
empty = v.empty;
|
||||
simplifyStep = v.simplifyStep;
|
||||
|
||||
varName = v.varName;
|
||||
((VarItem &)v).varName.Clear();
|
||||
|
||||
shortExpression = v.shortExpression;
|
||||
evaluableExpression = v.evaluableExpression;
|
||||
type = v.type;
|
||||
kind = v.kind;
|
||||
value = v.value;
|
||||
numChildren = v.numChildren;
|
||||
items = v.items;
|
||||
}
|
||||
|
||||
// destructor
|
||||
VarItem::~VarItem()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
VarItem const &VarItem::operator=(pick_ VarItem &v)
|
||||
{
|
||||
if(!varName.IsEmpty() && varName.Find('.') < 0)
|
||||
debugger->MICmd("var-delete " + varName);
|
||||
|
||||
debugger = v.debugger;
|
||||
empty = v.empty;
|
||||
simplifyStep = v.simplifyStep;
|
||||
|
||||
varName = v.varName;
|
||||
((VarItem &)v).varName.Clear();
|
||||
|
||||
shortExpression = v.shortExpression;
|
||||
evaluableExpression = v.evaluableExpression;
|
||||
type = v.type;
|
||||
kind = v.kind;
|
||||
value = v.value;
|
||||
numChildren = v.numChildren;
|
||||
items = v.items;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// clears contents
|
||||
void VarItem::Clear(void)
|
||||
{
|
||||
// store variable to be deleted later
|
||||
if(!varName.IsEmpty() && varName.Find('.') < 0)
|
||||
PutDeleted(varName);
|
||||
|
||||
empty = true;
|
||||
simplifyStep = -1;
|
||||
varName.Clear();
|
||||
shortExpression.Clear();
|
||||
evaluableExpression.Clear();
|
||||
type.Clear();
|
||||
value.Clear();
|
||||
numChildren = 0;
|
||||
items = 0;
|
||||
kind = SIMPLE;
|
||||
}
|
||||
|
||||
bool VarItem::Simplify(void)
|
||||
{
|
||||
// if already simplified, return false
|
||||
if(!simplifyStep)
|
||||
return false;
|
||||
|
||||
// lookup for simplifier
|
||||
TYPE_SIMPLIFIER_HANDLER simplifier = GetSimplifier(type);
|
||||
if(!simplifier)
|
||||
{
|
||||
// none found, mark as already simplifie and leave
|
||||
simplifyStep = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// simplifier found
|
||||
if(simplifyStep == -1)
|
||||
{
|
||||
// fast, non-deep simplification
|
||||
// set simplified to false if need deep one
|
||||
simplifyStep = simplifier(*this, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// slow, deep simplification
|
||||
simplifyStep = simplifier(*this, simplifyStep);
|
||||
}
|
||||
return (simplifyStep != 0);
|
||||
}
|
||||
|
||||
// evaluate an expression usign gdb variables
|
||||
bool VarItem::Evaluate(String const &expr)
|
||||
{
|
||||
Clear();
|
||||
|
||||
// create the variable
|
||||
MIValue var = debugger->MICmd("var-create - * " + expr);
|
||||
if(var.IsError())
|
||||
return false;
|
||||
empty = false;
|
||||
|
||||
// store its name
|
||||
varName = var["name"];
|
||||
|
||||
// store its value
|
||||
value = var["value"];
|
||||
|
||||
// store type
|
||||
type = var["type"];
|
||||
|
||||
// store number of children (temporary number...)
|
||||
// and set temporary object kind
|
||||
numChildren = atoi(var.Get("numchild", "0"));
|
||||
kind = numChildren ? COMPLEX : SIMPLE;
|
||||
|
||||
// get and store expression
|
||||
evaluableExpression = expr;
|
||||
shortExpression = expr;
|
||||
|
||||
// fast simplify known types
|
||||
Simplify();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// fetch variable children
|
||||
Vector<VarItem> VarItem::GetChildren0(MIValue const &val, String const &prePath)
|
||||
{
|
||||
Vector<VarItem> res;
|
||||
|
||||
MIValue const &children = val["children"];
|
||||
if(!children.IsArray())
|
||||
return res;
|
||||
|
||||
for(int iChild = 0; iChild < children.GetCount(); iChild++)
|
||||
{
|
||||
MIValue const &child = children[iChild];
|
||||
|
||||
// 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 = debugger->MICmd("var-list-children 1 " + nam);
|
||||
if(!val2.IsTuple())
|
||||
continue;
|
||||
res.Append(GetChildren0(val2, prePath));
|
||||
}
|
||||
else
|
||||
{
|
||||
VarItem &v = res.Add(VarItem(debugger));
|
||||
v.empty = false;
|
||||
v.varName = nam;
|
||||
v.shortExpression = prePath + "." + exp;
|
||||
v.type = typ;
|
||||
|
||||
v.value = child["value"];
|
||||
v.numChildren = atoi(child.Get("numchild", "0"));
|
||||
v.kind = v.numChildren ? COMPLEX : SIMPLE;
|
||||
|
||||
MIValue vExp = debugger->MICmd("var-info-path-expression " + nam);
|
||||
v.evaluableExpression = vExp.Get("path_expr", "");
|
||||
|
||||
// fast simplify known types
|
||||
Simplify();
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// fetch variable children
|
||||
Vector<VarItem> VarItem::GetChildren(void)
|
||||
{
|
||||
Vector<VarItem> res;
|
||||
|
||||
// do not enumerate children for non-complex types
|
||||
// (for arrays and maps just use GetArray and GetMap functions)
|
||||
if(kind != COMPLEX)
|
||||
return res;
|
||||
|
||||
// if no variable name, just return empty array
|
||||
if(varName.IsEmpty())
|
||||
return res;
|
||||
|
||||
// get children of current variable
|
||||
MIValue val = debugger->MICmd("var-list-children 1 " + varName);
|
||||
if(!val.IsTuple())
|
||||
return res;
|
||||
|
||||
res = GetChildren0(val, evaluableExpression);
|
||||
return res;
|
||||
}
|
||||
|
||||
// fetch array elements
|
||||
Vector<VarItem> VarItem::GetArray(int start, int count)
|
||||
{
|
||||
Vector<VarItem> res;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// fetch map elements
|
||||
VectorMap<VarItem, VarItem> VarItem::GetMap(int start, int count)
|
||||
{
|
||||
VectorMap<VarItem, VarItem> res;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// helpers for simplifiers
|
||||
// helper for simplifiers
|
||||
/*
|
||||
void VarItem::ListChildren0(String const &varName, MIValue &res) const
|
||||
{
|
||||
MIValue val = debugger->MICmd("var-list-children " + varName);
|
||||
if(!val.IsTuple())
|
||||
return;
|
||||
MIValue const &children = val["children"];
|
||||
if(!children.IsArray())
|
||||
return;
|
||||
|
||||
for(int iChild = 0; iChild < children.GetCount(); iChild++)
|
||||
{
|
||||
MIValue const &child = children[iChild];
|
||||
|
||||
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 = debugger->MICmd("var-list-children 1 " + nam);
|
||||
if(!val2.IsTuple())
|
||||
continue;
|
||||
ListChildren0(val2, prePath));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
MIValue VarItem::ListChildren(void) const
|
||||
{
|
||||
}
|
||||
*/
|
||||
|
||||
MIValue VarItem::EvaluateExpression(String const &exp) const
|
||||
{
|
||||
MIValue val = debugger->MICmd("data-evaluate-expression " + exp);
|
||||
if(!val.IsTuple())
|
||||
return MIValue();
|
||||
MIValue v = val["value"];
|
||||
if(v.IsError() || !v.IsString())
|
||||
return MIValue();
|
||||
String s = v.ToString();
|
||||
return MIValue(s);
|
||||
}
|
||||
|
||||
|
||||
// cleanup support -- variables can't be deleted in destructor
|
||||
// as usually destructor is called when thread is hard-stopped
|
||||
// by main thread with an exception.
|
||||
// so we store all var names in a static index and delete them
|
||||
// when back to main thread
|
||||
StaticMutex VarItem::varMutex;
|
||||
Vector<String> VarItem::deletedVars;
|
||||
|
||||
void VarItem::PutDeleted(String const &name)
|
||||
{
|
||||
INTERLOCKED_(varMutex) {
|
||||
deletedVars.Add(name);
|
||||
}
|
||||
}
|
||||
|
||||
void VarItem::CleanVariables(Gdb_MI2 *deb)
|
||||
{
|
||||
ASSERT(IsMainThread());
|
||||
INTERLOCKED_(varMutex) {
|
||||
for(int i = 0; i < deletedVars.GetCount(); i++)
|
||||
deb->MICmd("var-delete " + deletedVars[i]);
|
||||
}
|
||||
}
|
||||
|
||||
118
uppsrc/ide/Debuggers/VarItem.h
Normal file
118
uppsrc/ide/Debuggers/VarItem.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#ifndef _ide_Debuggers_VarItem_h_
|
||||
#define _ide_Debuggers_VarItem_h_
|
||||
|
||||
#include <Core/Core.h>
|
||||
using namespace Upp;
|
||||
|
||||
#include "MIValue.h"
|
||||
|
||||
// item for a GDB variable
|
||||
class Gdb_MI2;
|
||||
|
||||
class VarItem : Moveable<VarItem>
|
||||
{
|
||||
private:
|
||||
|
||||
// connected debugger object
|
||||
Gdb_MI2 *debugger;
|
||||
|
||||
// error/empty state
|
||||
bool empty;
|
||||
|
||||
// next simplify step, 0 if completed
|
||||
int simplifyStep;
|
||||
|
||||
// gdb internal variable name
|
||||
String varName;
|
||||
|
||||
// fetch variable children
|
||||
Vector<VarItem> GetChildren0(MIValue const &children, String const &prePath);
|
||||
|
||||
// helper for simplifiers
|
||||
void ListChildren0(String const &varName, MIValue &res) const;
|
||||
|
||||
// cleanup support -- variables can't be deleted in destructor
|
||||
// as usually destructor is called when thread is hard-stopped
|
||||
// by main thread with an exception.
|
||||
// so we store all var names in a static index and delete them
|
||||
// when back to main thread
|
||||
static StaticMutex varMutex;
|
||||
static Vector<String> deletedVars;
|
||||
void PutDeleted(String const &name);
|
||||
|
||||
public:
|
||||
typedef enum { SIMPLE, COMPLEX, ARRAY, MAP } VarKind;
|
||||
|
||||
// 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;
|
||||
|
||||
// number of items for array and maps
|
||||
int items;
|
||||
|
||||
// check if value contains an error
|
||||
bool IsEmpty(void) const { return empty; }
|
||||
bool operator!(void) const { return IsEmpty(); }
|
||||
operator bool() { return !IsEmpty(); }
|
||||
|
||||
// check if value is simplified
|
||||
bool IsSimplified(void) { return simplifyStep == 0; }
|
||||
|
||||
// clears contents
|
||||
void Clear(void);
|
||||
|
||||
// evaluate expression
|
||||
bool Evaluate(String const &expr);
|
||||
|
||||
// deep simplify known types
|
||||
bool Simplify(void);
|
||||
|
||||
// constructors
|
||||
VarItem(Gdb_MI2 *dbg);
|
||||
VarItem(Gdb_MI2 *dbg, String const &expr);
|
||||
|
||||
// destructor
|
||||
~VarItem();
|
||||
|
||||
// copy (pick)
|
||||
VarItem(pick_ VarItem &v);
|
||||
VarItem const &operator=(pick_ VarItem &v);
|
||||
|
||||
// get children
|
||||
Vector<VarItem>GetChildren(void);
|
||||
|
||||
// fetch array elements
|
||||
Vector<VarItem> GetArray(int start = 0, int count = -1);
|
||||
|
||||
// fetch map elements
|
||||
VectorMap<VarItem, VarItem> GetMap(int start = 0, int count = -1);
|
||||
|
||||
// helpers for simplifiers
|
||||
Gdb_MI2 &Debugger() { return *debugger; }
|
||||
MIValue ListChildren(void) const;
|
||||
MIValue EvaluateExpression(String const &exp) const;
|
||||
|
||||
// cleanup support -- variables can't be deleted in destructor
|
||||
// as usually destructor is called when thread is hard-stopped
|
||||
// by main thread with an exception.
|
||||
// so we store all var names in a static index and delete them
|
||||
// when back to main thread
|
||||
static void CleanVariables(Gdb_MI2 *deb);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue