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:
micio 2014-02-25 16:34:59 +00:00
parent efefa2a638
commit 638dabd428
12 changed files with 1909 additions and 1257 deletions

View file

@ -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))

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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()

View 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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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);

View 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]);
}
}

View 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