Ide: GDB_MI2 removed from TheIDE.

git-svn-id: svn://ultimatepp.org/upp/trunk@12119 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
klugier 2018-07-29 23:32:24 +00:00
parent 1e0574c5d3
commit 0581d4b38f
19 changed files with 7 additions and 4795 deletions

View file

@ -174,7 +174,7 @@ void Sentinel(Stream& s, const char *txt)
void Ide::Serialize(Stream& s)
{
int version = 13;
int version = 14;
Sentinel(s, "before 12341234");
s.Magic(0x12341234);
Sentinel(s, "after magic");
@ -261,7 +261,10 @@ void Ide::Serialize(Stream& s)
s % hydra1_threads;
if(s.IsLoading())
console.SetSlots(hydra1_threads);
s % gdbSelector;
if(version < 14) {
int dummy_gdb_selector;
s % dummy_gdb_selector;
}
s % doc;
s % chstyle;
s % astyle_BracketIndent;

View file

@ -297,10 +297,7 @@ void Ide::BuildAndDebug(bool runto)
bool console = ShouldHaveConsole();
if(findarg(builder, "GCC", "CLANG") >= 0) {
if(gdbSelector)
debugger = Gdb_MI2Create(pick(host), target, runarg, console);
else
debugger = GdbCreate(pick(host), target, runarg, console);
debugger = GdbCreate(pick(host), target, runarg, console);
}
#ifdef PLATFORM_WIN32
else

View file

@ -240,8 +240,6 @@ private:
One<IGdbUtils> gdb_utils;
};
#include "Gdb_MI2.h"
#define KEYGROUPNAME "Debugger"
#define KEYNAMESPACE PdbKeys
#define KEYFILE <ide/Debuggers/Pdb.key>
@ -251,6 +249,4 @@ private:
#include "Pdb.h"
#endif
INITIALIZE(UppSimplifiers)
#endif

View file

@ -20,19 +20,6 @@ file
Gdb.cpp,
GdbUtils.h,
GdbUtils.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_MI2Gdb.cpp,
Gdb_MI2.cpp,
UppSimplifiers.cpp,
PDB readonly separator,
Pdb.h,
cvconst.h,

File diff suppressed because it is too large Load diff

View file

@ -1,329 +0,0 @@
#ifndef _ide_Debuggers_Gdb_MI2_h_
#define _ide_Debuggers_Gdb_MI2_h_
#include "MIValue.h"
#include "VarItem.h"
class WatchEdit : public LineEdit
{
virtual void HighlightLine(int line, Vector<Highlight>& h, int pos);
};
#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
{
friend class VarItem;
private:
// list of processes (and connected thread groups)
// used to stop them
VectorMap<String, int> processes;
// list of debug variables created and thread-safe functions to
// manage them -- used to clean up
Vector<String>debugVariables;
#ifdef flagMT
Vector<String>prevDebugVariables;
#endif
Mutex varMutex;
void StoreVariable(String const &name);
void CleanupVariables(void);
// multithread support
#ifdef flagMT
// numbr of running debug threads
int threadRunning;
// flag to signal threads to stop
bool stopThread;
// mutex and thead object
Mutex mutex;
Thread debugThread;
// mutex-protected functions
bool IsThreadRunning(void);
void IncThreadRunning();
void DecThreadRunning();
public:
bool IsStopThread(void);
private:
void SetStopThread(bool b);
// shut down threads and wait till done
void ShutDownThreads(void);
#endif
#ifdef PLATFORM_POSIX
// debug break support -- ONLY POSIX, by now
bool InterruptDebugger(void);
#endif
// current command break support -- ONLY POSIX, by now
// used to speed up operations in MT mode
bool InterruptCommand(void);
// used to post and kill timed callbacks
TimeCallback timeCallback;
TimeCallback exploreCallback;
One<Host> host;
One<AProcess> dbg;
bool firstRun;
// the disassembler window
DbgDisas disas;
// the registers pane
#ifdef CPU_64
FrameBottom<WithGdb_MI2Registers64Layout<StaticRect> > regs;
#define RPREFIX "r"
#else
FrameBottom<WithGdb_MI2RegistersLayout<StaticRect> > regs;
#define RPREFIX "e"
#endif
// the quick watch dialog
WithGdb_MI2QuickwatchLayout<TopWindow> quickwatch;
EditString watchedit;
DropList frame;
DropList threadSelector;
TabCtrl tab;
ArrayCtrl autos;
ArrayCtrl locals;
ArrayCtrl members;
ArrayCtrl watches;
ArrayCtrl explorer;
// explorer stuffs -- just starting
EditString explorerExprEdit;
Button explorerBackBtn, explorerForwardBtn;
StaticRect explorerPane;
void onExploreExpr(ArrayCtrl *what = NULL);
void onExplorerChild();
void onExplorerBack();
void onExplorerForward();
void ExplorerMenu(Bar& bar);
void doExplore(String const &expr, bool appendHistory);
// explorer expressions and values
Index<String>explorerExpressions;
Vector<String>explorerValues;
Index<String> explorerHistoryExpressions;
int explorerHistoryPos;
Label dlock;
Vector<String> regname;
Vector<Label *> reglbl;
void AddReg(const char *reg, Label *lbl) { regname.Add(reg); reglbl.Add(lbl); }
// find free space at right of tabs (we should probably add something to TabCtrl for that..)
int FindTabsRight(void);
// running flags -- setup by ReadGdb function when async input is catched
bool started;
bool stopped;
MIValue stopReason;
// read debugger output analyzing command responses
// and async output
MIValue ParseGdb(String const &s, bool wait = true);
MIValue ReadGdb(bool wait = true);
// format breakpoint line from ide file and line
String BreakPos(String const &file, int line);
// get breakpoints info
MIValue GetBreakpoints(void);
MIValue GetBreakpoint(int id);
MIValue GetBreakPoint(const char *file, int line);
// try to set a breakpoint - returns false if no code there
bool TryBreak(adr_t addr, bool temp);
bool TryBreak(String const &file, int line, bool temp);
// set breakpoint
MIValue InsertBreakpoint(const char *file, int line);
// stored local variable expressions, and values
Index<String>localExpressions;
Vector<String>localValues;
Vector<int>localHints;
// stored watches expressions and values
Index<String>watchesExpressions;
Vector<String>watchesValues;
Vector<int>watchesHints;
// 'this' variable inspection data
Index<String>thisExpressions;
Vector<String>thisValues;
Vector<int>thisHints;
Index<String>thisShortExpressions;
// stored autos expressions, values and types
String autoLine;
// logs frame data on console
void LogFrame(String const &msg, MIValue &frame);
// check for stop reason
void CheckStopReason(void);
// stop all running threads and re-select previous current thread
void StopAllThreads(void);
// single step command handler
void Step(const char *cmd);
// run to command for menu -- just removes return value
void doRunTo(void) { RunTo(); }
// setup ide cursor based on disassembler one
void DisasCursor();
// reset ide default cursor image when disassembler loose focus
void DisasFocus();
// sync disassembler pane
void SyncDisas(MIValue &fInfo, bool fr);
// sync auto vars treectrl
void SyncAutos();
#ifdef flagMT
// sync local variables pane
void SyncLocals(void);
// Sync 'this' inspector data
void SyncThis(void);
// sync watches treectrl
void SyncWatches(void);
// sync explorer pane
void SyncExplorer();
#else
// sync local variables pane
void SyncLocals(const Vector<VarItem>& localVars = Vector<VarItem>());
// Sync 'this' inspector data
void SyncThis(const Vector<VarItem>& children = Vector<VarItem>());
// sync watches treectrl
void SyncWatches(const Vector<VarItem>& children = Vector<VarItem>());
// sync explorer pane
void SyncExplorer(const Vector<VarItem>& children = Vector<VarItem>());
#endif
// sync data tabs, depending on which tab is shown
bool localSynced;
bool thisSynced;
bool watchesSynced;
bool explorerSynced;
void SyncData();
// sync ide display with breakpoint position
void SyncIde(bool frame = false);
// watches arrayctrl key handling
bool Key(dword key, int count);
// create a string representation of frame given its info and args
String FormatFrame(MIValue &fInfo, MIValue &fArgs);
// re-fills frame's droplist when dropping it
bool FillDropFrames(int min, int max, bool val);
void DropFrames();
// shows selected stack frame in editor
void ShowFrame();
// re-fills thread selector droplist on drop
void dropThreads();
// selects current thread
void showThread(void);
// opens quick watch dialog
void QuickWatch(void);
// format watch line
String FormatWatchLine(String exp, String const &val, int level);
// deep watch current quickwatch variable
void WatchDeep0(String parentExp, String const &name, int level, int &maxRemaining);
void WatchDeep(String parentExp, String const &name);
// copy stack frame list to clipboard
void CopyStack(void);
// copy disassembly listing to clipboard
void CopyDisas(void);
// lock/unlock debugger controls
void Lock();
void Unlock();
// Period check for killed console
TimeCallback periodic;
void Periodic();
String GetHostPath(const String& path) { return host->GetHostPath(path); }
String GetLocalPath(const String& path) { return host->GetLocalPath(path); }
// 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);
void Setup(ArrayCtrl& a, int x = 1);
void SyncWidth(ArrayCtrl& a);
protected:
public :
typedef Gdb_MI2 CLASSNAME;
// debugger IDE inteface
virtual void DebugBar(Bar& bar);
virtual bool SetBreakpoint(const String& filename, int line, const String& bp);
virtual void AsyncBrk();
virtual bool RunTo();
virtual void Run();
virtual void Stop();
virtual bool IsFinished();
virtual bool Tip(const String& exp, CodeEditor::MouseTip& mt);
// create GDB process and initializes it
bool Create(One<Host>&& _host, const String& exefile, const String& cmdline, bool console);
Gdb_MI2();
virtual ~Gdb_MI2();
// sends an MI command and get answer back
MIValue MICmd(const char *cmdLine);
// quick exit from service thread when called and 'stopThread' is set
// throws a BreakExc exception
void RaiseIfStop(void);
};
#endif

View file

@ -1,243 +0,0 @@
#include "Debuggers.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.Add(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(const Vector<VarItem>& children_)
{
static VectorMap<String, String> prev;
Vector<VarItem> children = clone(children_);
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 = clone(vItem.GetChildren());
else
children.Add(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);
exploreEvent<> .Set(500, THISBACK1(SyncExplorer, DeepClone(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;
exploreEvent<> .Set(100, THISBACK1(SyncExplorer, DeepClone(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)
{
// set the expression inside expression editor
explorerExprEdit <<= expr;
// update the history : trim it from past current position
// and append it at end
if(appendHistory)
{
if(explorerHistoryPos >= 0)
{
explorerHistoryPos++;
explorerHistoryExpressions.Trim(explorerHistoryPos);
}
else
explorerHistoryPos = 0;
explorerHistoryExpressions.Add(expr);
}
explorerSynced = false;
SyncExplorer();
// update history buttons visibility
explorerBackBtn.Enable(explorerHistoryPos > 0);
explorerForwardBtn.Enable(explorerHistoryPos < explorerHistoryExpressions.GetCount() - 1);
}
void Gdb_MI2::onExploreExpr(ArrayCtrl *what)
{
String expr;
if(!what)
{
// if expression don't come from another ArrayCtrl
// we use the expression editbox
expr = ~explorerExprEdit;
}
else if(what == &members)
{
int line = what->GetCursor();
if(line >= 0)
expr = thisExpressions[line];
}
else
{
// otherwise, we use the expression from sending ArrayCtrl
int line = what->GetCursor();
int col = what->GetClickColumn();
if(line >= 0 && (what != &watches || col != 0))
expr = what->Get(line, 0);
}
// nothing to do on empty expression
if(expr == "")
return;
doExplore(expr, true);
// activate explorer tab
tab.Set(4);
}
void Gdb_MI2::onExplorerChild()
{
// click on first line (value line) does nothing
int line = explorer.GetCursor();
if(line < 0)
return;
if(line < explorerExpressions.GetCount())
doExplore(explorerExpressions[line], true);
}
void Gdb_MI2::onExplorerBack()
{
if(explorerHistoryPos < 1)
return;
explorerHistoryPos--;
String expr = explorerHistoryExpressions[explorerHistoryPos];
doExplore(expr, false);
}
void Gdb_MI2::onExplorerForward()
{
if(explorerHistoryPos >= explorerHistoryExpressions.GetCount() - 1)
return;
explorerHistoryPos++;
String expr = explorerHistoryExpressions[explorerHistoryPos];
doExplore(expr, false);
}
void Gdb_MI2::ExplorerMenu(Bar& bar)
{
}

View file

@ -1,357 +0,0 @@
#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;
String str = ss.GetLine();
s = str;
while(str.GetCount() == 1024 && !ss.IsEof())
{
str = ss.GetLine();
s << str;
}
s = TrimBoth(s);
// 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())
ShutDownThreads();
// 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;
}
// support for debugger variables cleanup
void Gdb_MI2::StoreVariable(String const &name)
{
INTERLOCKED_(varMutex) {
debugVariables.Add(name);
}
}
void Gdb_MI2::CleanupVariables(void)
{
#ifdef flagMT
// restart if called from main thread
if(IsMainThread())
{
INTERLOCKED_(varMutex) {
prevDebugVariables.Append(debugVariables);
debugVariables.Clear();
}
debugThread.Start(THISBACK(CleanupVariables));
return;
}
IncThreadRunning();
String name;
try
{
do
{
name = "";
INTERLOCKED_(varMutex) {
if(!prevDebugVariables.IsEmpty())
{
name = prevDebugVariables.Top();
MICmd("var-delete " + name);
prevDebugVariables.Pop();
}
}
Sleep(50);
}
while(!IsStopThread() && name != "");
}
catch(...)
{
}
DecThreadRunning();
#else
if(!debugVariables.IsEmpty())
{
String name = debugVariables.Pop();
MICmd("var-delete " + name);
timeEvent<> .Set(50, THISBACK(CleanupVariables));
}
#endif
}

View file

@ -1,806 +0,0 @@
#include "MIValue.h"
//#define MITUPLE_DUMP_MARKERS
static MIValue &NullMIValue(void)
{
static MIValue v;
return v;
}
static MIValue &ErrorMIValue(String const &msg)
{
static MIValue v;
v.SetError(msg);
return v;
}
bool MIValue::expect(String const &where, char exp, int i, String const &s)
{
if(s[i] == exp)
return true;
int start = i - 30;
if(start < 0)
start = 0;
if(!s[i])
SetError(Format(where + " : Expected '%c', got end of string at pos %d around '%s'", exp, i, s.Mid(start, 60)));
else
SetError(Format(where + " : Expected '%c', got '%c' at pos %d in '%s'", exp, s[i], i, s.Mid(start, 60)));
return false;
}
static char backslash(String const &s, int &i)
{
i++;
if(IsDigit(s[i]))
{
char c = (s[i]-'0')*64 + (s[i+1]-'0')*8 + s[i+2]-'0';
i += 2;
return c;
}
// control chars and octals
switch(s[i])
{
case 'a' :
return '\a';
case 'b' :
return '\b';
case 'f' :
return '\f';
case 'n' :
return '\n';
case 'r' :
return '\r';
case 't' :
return '\t';
case 'v' :
return '\v';
default:
return s[i];
}
}
int MIValue::ParsePair(String &name, MIValue &val, String const &s, int i)
{
name.Clear();
val.Clear();
while(s[i] && isspace(s[i]))
i++;
if(!s[i])
{
SetError("ParsePair:Unexpected end of string");
return i;
}
// is starting wirh '[' or '{' take it as a value with empty name
if(s[i] == '{' || s[i] == '[')
{
name = "<UNNAMED>";
return val.ParseTuple(s, i);
}
else
{
int aCount = 0;
while(s[i] && ((s[i] != '=' && s[i] != '}' && s[i] != ']' && s[i] != ',') || aCount))
{
if(s[i] == '<')
aCount++;
else if(s[i] == '>')
aCount--;
if(s[i] == '\\')
name.Cat(backslash(s, i));
else
name.Cat(s[i]);
i++;
// skip blanks if not inside <>
/*
if(!aCount)
while(s[i] && isspace(s[i]))
i++;
*/
}
while(s[i] && isspace(s[i]))
i++;
if(s[i] != '=')
{
// we take the data without = as the value part
// of keyless tuple...
val.Set(name);
name = "<UNNAMED>";
return i;
}
i++;
while(s[i] && isspace(s[i]))
i++;
}
// skip address part before a tuple start, if any... it's useless and confuses the parser
if(s[i] == '@')
{
int j = i;
while(s[j] && s[j] != ':')
j++;
if(s[j] == ':')
j++;
while(s[j] && IsSpace(s[j]))
j++;
if(s[j] == '{')
i = j;
}
switch(s[i])
{
case '"':
i = val.ParseString(s, i);
break;
break;
case '[':
i = val.ParseArray(s, i);
break;
case '{':
i = val.ParseTuple(s, i);
break;
default:
i = val.ParseUnquotedString(s, i);
break;
}
return i;
}
int MIValue::ParseTuple(String const &s, int i)
{
Clear();
type = MITuple;
// drop opening delimiter
if(!expect("ParseTuple", '{', i, s))
return s.GetCount();
i++;
while(s[i] && s[i] != '}')
{
while(s[i] && isspace(s[i]))
i++;
String name;
MIValue val;
i = ParsePair(name, val, s, i);
tuple.AddPick(name, pick(val));
while(s[i] && isspace(s[i]))
i++;
if(s[i] == '}')
break;
if(!expect("ParseTuple", ',', i, s))
return s.GetCount();
i++;
}
return i + 1;
}
int MIValue::ParseArray(String const &s, int i)
{
Clear();
type = MIArray;
// drop opening delimiter
if(!expect("ParseArray", '[', i, s))
return s.GetCount();
i++;
while(s[i] && isspace(s[i]))
i++;
while(s[i] && s[i] != ']')
{
while(s[i] && isspace(s[i]))
i++;
String name;
MIValue val;
if(s[i] == '[')
i = val.ParseArray(s, i);
else if(s[i] == '{')
i = val.ParseTuple(s, i);
else if(s[i] == '"')
i = val.ParseString(s, i);
else if(s[i] == '<')
i = val.ParseAngle(s, i);
else
i = ParsePair(name, val, s, i);
array.Add() = pick(val);
while(s[i] && isspace(s[i]))
i++;
if(s[i] == ']')
break;
if(!expect("ParseArray", ',', i, s))
return s.GetCount();
i++;
}
return i + 1;
}
int MIValue::ParseString(String const &s, int i)
{
Clear();
type = MIString;
if(!expect("ParseString", '"', i, s))
return s.GetCount();
i++;
while(s[i])
{
// verbatim if escaped
if(s[i] == '\\')
string.Cat(backslash(s, i));
else if(s[i] == '"')
{
i++;
break;
}
else
string.Cat(s[i]);
i++;
}
if(!expect("ParseString", '"', i-1, s))
return s.GetCount();
return i;
}
int MIValue::ParseAngle(String const &s, int i)
{
Clear();
type = MIString;
int aCount = 0;
if(!expect("ParseAngle", '<', i, s))
return s.GetCount();
string = "<";
aCount++;
i++;
while(s[i])
{
// verbatim if escaped
if(s[i] == '\\')
string.Cat(backslash(s, i));
else if(s[i] == '>' && !--aCount)
{
i++;
break;
}
else
{
string.Cat(s[i]);
if(s[i] == '<')
aCount++;
}
i++;
}
if(!expect("ParseAngle", '>', i-1, s))
return s.GetCount();
string.Cat('>');
return i;
}
// sigh
static bool comma(String const &s, int i)
{
if(s[i] != ',')
return false;
if(!i)
return true;
if(IsDigit(s[i-1]) && IsDigit(s[i+1]))
return false;
return true;
}
// we can hava a non-quoted string... so we read up
// to terminator, which can be '}', ']' or ','
int MIValue::ParseUnquotedString(String const &s, int i)
{
String valStr;
int aCount = 0;
bool inQuote = false;
while(s[i] && ((s[i] != '=' && s[i] != '}' && s[i] != ']' && !comma(s, i)) || inQuote || aCount))
{
valStr.Cat(s[i]);
if(s[i] == '\\')
{
i++;
if(s[i])
valStr.Cat(s[i++]);
continue;
}
if(s[i] == '<' && !inQuote)
aCount++;
else if(s[i] == '>' && !inQuote)
aCount--;
else if(s[i] == '"' && !aCount)
inQuote = !inQuote;
i++;
}
type = MIString;
string = valStr;
return i;
}
int MIValue::Parse(String const &s, int i)
{
// if starts with '"', it's a string
// if starts with '[', it's an array
// if starts with '{', it's a tuple
// otherwise, it can be a sequence of pair name="value" which is stored like a tuple
// latter case is an example o bad design of MI interface....
Clear();
while(s[i] && isspace(s[i]))
i++;
if(s[i] == '"')
return ParseString(s, i);
else if(s[i] == '<')
return ParseAngle(s, i);
else if(s[i] == '[')
return ParseArray(s, i);
else if(s[i] == '{')
return ParseTuple(s, i);
else
{
String name;
MIValue val;
type = MITuple;
while(s[i])
{
i = ParsePair(name, val, s, i);
tuple.AddPick(name, pick(val));
while(s[i] && isspace(s[i]))
i++;
if(s[i] != ',')
break;
i++;
}
return i;
}
}
MIValue &MIValue::operator=(MIValue&& v)
{
Clear();
type = pick(v.type);
switch(type)
{
case MIString:
string = v.string;
break;
case MIArray:
array = pick(v.array);
break;
case MITuple:
tuple = pick(v.tuple);
break;
default:
SetError("Unknown MIValue type");
}
return *this;
}
MIValue::MIValue()
{
Clear();
}
MIValue::MIValue(MIValue&& v)
{
Clear();
type = v.type;
switch(type)
{
case MIString:
string = v.string;
break;
case MIArray:
array = pick(v.array);
break;
case MITuple:
tuple = pick(v.tuple);
break;
default:
SetError("Unknown MIValue type");
}
}
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;
}
void MIValue::Clear()
{
type = MIString;
string = "";
array.Clear();
tuple.Clear();
}
// sets value to an error condition
MIValue &MIValue::SetError(String const &msg)
{
type = MIString;
string = "error:" + msg;
return *this;
}
// check if value contains an error
bool MIValue::IsError(void) const
{
return type == MIString && string.StartsWith("error:");
}
// check for emptyness
bool MIValue::IsEmpty(void) const
{
return type == MIString && string == "";
}
// simple accessors
int MIValue::GetCount(void) const
{
if(IsError())
return 0;
if(type == MIArray)
return array.GetCount();
else if(type == MITuple)
return tuple.GetCount();
else
return string.GetCount();
}
int MIValue::Find(const char *key) const
{
if(type != MITuple)
return -1;
return tuple.Find(key);
}
MIValue &MIValue::Get(int i)
{
if(IsError())
return *this;
if(type == MIArray)
return array[i];
if(type == MITuple)
return tuple[i];
return ErrorMIValue("Not an Array value type");
}
MIValue const &MIValue::Get(int i) const
{
if(IsError())
return *this;
if(type == MIArray)
return array[i];
if(type == MITuple)
return tuple[i];
return ErrorMIValue("Not an Array value type");
}
MIValue &MIValue::Get(const char *key)
{
if(type != MITuple)
return ErrorMIValue("Not a Tuple value type");
if(tuple.Find(key) < 0)
return ErrorMIValue(String("key '") + key + "' not found");
return tuple.Get(key);
}
MIValue const &MIValue::Get(const char *key) const
{
if(type != MITuple)
return ErrorMIValue("Not a Tuple value type");
if(tuple.Find(key) < 0)
return ErrorMIValue(String("key '") + key + "' not found");
return tuple.Get(key);
}
String &MIValue::Get(void)
{
if(type != MIString)
return ErrorMIValue("Not a String value type");
return string;
}
String const &MIValue::Get(void) const
{
if(type != MIString)
return ErrorMIValue("Not a String value type");
return string;
}
// tuple string member accessor with default value if not found
String MIValue::Get(const char *key, const char *def) const
{
if(type != MITuple)
return ErrorMIValue("Not a Tuple value type");
int i = tuple.Find(key);
if(i >= 0)
{
if(tuple[i].type != MIString)
return def;
return tuple[i].Get();
}
else
return def;
}
// gets key by index for tuple values
String MIValue::GetKey(int idx) const
{
if(type != MITuple)
return ErrorMIValue("Not a Tuple value type");
return tuple.GetKey(idx);
}
void MIValue::Set(String const &s)
{
Clear();
type = MIString;
string = s;
}
// data dump
#ifdef MITUPLE_DUMP_MARKERS
#define MARK_STRING "<STRING>"
#define MARK_ARRAY "<ARRAY>"
#define MARK_TUPLE "<TUPLE>"
#else
#define MARK_STRING ""
#define MARK_ARRAY ""
#define MARK_TUPLE ""
#endif
// dumps a string with special chars inside
String MIValue::Dump(String const &s)
{
String res;
for(int i = 0; i < s.GetCharCount(); i++)
{
byte c = s[i];
if(isprint(c))
res << c;
else
res += Format("\\%03o", c);
}
return res;
}
String MIValue::Dump(int level) const
{
String spacer(' ', level);
switch(type)
{
case MIString:
return spacer + MARK_STRING + Dump(string);
case MITuple:
{
String s = spacer + MARK_TUPLE + "{\n";
level += 4;
spacer = String(' ', level);
for(int i = 0; i < tuple.GetCount(); i++)
{
String s1 = spacer + tuple.GetKey(i) + "=";
s += s1;
MIValue const &val = tuple[i];
if(val.type == MIString)
s += val.Dump();
else
{
s += '\n' + val.Dump(level + 4);
s = s.Left(s.GetCount()-1);
}
if(i < tuple.GetCount() - 1)
s += ',';
s += '\n';
}
level -= 4;
spacer = String(' ', level);
s += spacer + "}\n";
return s;
}
case MIArray:
{
String s = spacer + MARK_ARRAY + "[ \n";
level += 4;
for(int i = 0; i < array.GetCount(); i++)
{
MIValue const &val = array[i];
s += val.Dump(level);
if(val.type != MIString)
s = s.Left(s.GetCount()-1);
if(i < array.GetCount() - 1)
s += ',';
s += '\n';
}
s += spacer + "]\n";
return s;
}
default:
return spacer + "*UNKNOWN MIVALUE TYPE*";
}
}
// finds breakpoint data given file and line
MIValue &MIValue::FindBreakpoint(String const &file, int line)
{
MIValue &body = Get("body");
if(body.IsError() || !body.IsArray())
return NullMIValue();
for(int i = 0; i < body.GetCount(); i++)
{
MIValue &bp = body[i];
if(bp.IsError() || !bp.IsTuple())
return NullMIValue();
if(bp["file"] == file && atoi(bp["line"].Get()) == line)
return bp;
}
return NullMIValue();
}
static String PackName(String const &name)
{
String res;
const char *c = ~name;
while(*c)
{
if(!IsSpace(*c))
res.Cat(*c);
c++;
}
return res;
}
// packs names inside tuples -- to make type recognition easy
void MIValue::PackNames(void)
{
if(type == MITuple)
{
for(int i = 0; i < tuple.GetCount(); i++)
{
tuple.SetKey(i, PackName(tuple.GetKey(i)));
tuple[i].PackNames();
}
}
else if(type == MIArray)
{
for(int i = 0; i < array.GetCount(); i++)
array[i].PackNames();
}
}
// fix arrays -- i.e. replace a tuple containing ALL unnamed elements
// with the corresponding array
// ( gdb evaluator array data is returned as tuple with {} instead []=
void MIValue::FixArrays(void)
{
bool named = false;
if(IsTuple())
{
for(int iVal = 0; iVal < tuple.GetCount(); iVal++)
if(tuple.GetKey(iVal) != "<UNNAMED>")
{
named = true;
break;
}
if(!named)
{
array.Clear();
for(int iVal = 0; iVal < tuple.GetCount(); iVal++)
array.Add() = pick(tuple[iVal]);
tuple.Clear();
type = MIArray;
}
}
if(IsTuple() || IsArray())
for(int i = 0; i < GetCount(); i++)
Get(i).FixArrays();
}
// add an item to a tuple
MIValue &MIValue::Add(String const &key, MIValue&& v)
{
if(IsEmpty())
{
Clear();
type = MITuple;
}
if(type != MITuple)
return ErrorMIValue("Not a Tuple value type");
tuple.AddPick(key, pick(v));
return *this;
}
MIValue &MIValue::Add(String const &key, String const &data)
{
MIValue v;
v.Set(data);
return Add(key, pick(v));
}
MIValue &MIValue::FindAdd(String const &key, String const &data)
{
if(IsEmpty())
{
Clear();
type = MITuple;
}
if(type != MITuple)
return ErrorMIValue("Not a Tuple value type");
int idx = tuple.Find(key);
MIValue v;
v.Set(data);
if(idx >= 0)
tuple[idx] = pick(v);
else
tuple.AddPick(key, pick(v));
return *this;
}
// add an item to an array
MIValue &MIValue::Add(MIValue&& v)
{
if(IsEmpty())
{
Clear();
type = MIArray;
}
if(type != MIArray)
return ErrorMIValue("Not a Array value type");
array.AddPick(pick(v));
return *this;
}
MIValue &MIValue::Add(String const &data)
{
MIValue v;
v.Set(data);
return Add(pick(v));
}
// remove a tuple key
MIValue &MIValue::Remove(String const &key)
{
if(type != MITuple)
return *this;
tuple.RemoveKey(key);
return *this;
}

View file

@ -1,122 +0,0 @@
#ifndef _ide_Debuggers_MIValue_h_
#define _ide_Debuggers_MIValue_h_
#include <Core/Core.h>
using namespace Upp;
// this struct contains values returned by GDB MI interface
typedef enum { MIString, MIArray, MITuple } MIValueType;
class MIValue : public Moveable<MIValue>
{
private:
bool expect(String const &where, char exp, int i, String const &s);
int ParsePair(String &name, MIValue &val, String const &s, int i = 0);
int ParseTuple(String const &s, int i = 0);
int ParseArray(String const &s, int i = 0);
int ParseString(String const &s, int i = 0);
int ParseAngle(String const &s, int i = 0);
int ParseUnquotedString(String const &s, int i = 0);
int ParseValue(String const &s, int i = 0);
int Parse(String const &s, int i = 0);
MIValueType type;
String string;
Vector<MIValue> array;
VectorMap<String, MIValue> tuple;
public:
// sets value to an error condition
MIValue &SetError(String const &msg);
// check if value contains an error
bool IsError(void) const;
bool operator!(void) const { return IsError(); }
operator bool() { return !IsError(); }
// check for emptyness
bool IsEmpty(void) const;
MIValue &operator=(MIValue&& v);
MIValue &operator=(String const &s);
MIValue();
MIValue(MIValue&& v);
MIValue(String const &s);
void Clear(void);
// simple accessors
int GetCount(void) const;
int Find(const char *key) const;
MIValue &Get(int i);
MIValue const &Get(int i) const;
MIValue &operator[](int i) { return Get(i); }
MIValue const &operator[](int i) const { return Get(i); }
MIValue &Get(const char *s);
MIValue const &Get(const char *s) const;
MIValue &operator[](const char *key) { return Get(key); }
MIValue const &operator[](const char *key) const { return Get(key); }
String &Get(void);
String const &Get(void) const;
// gets key by index for tuple values
String GetKey(int idx) const;
operator String&() { return Get(); }
operator const String &() const { return Get(); }
String &ToString(void) { return Get(); }
String const &ToString(void) const { return Get(); }
// tuple string member accessor with default value if not found
String Get(const char *key, const char *def) const;
String operator()(const char *key, const char *def) const { return Get(key, def); }
// setter for string (operator= starts parser...)
void Set(String const &s);
// some type checking
bool IsArray(void) const { return type == MIArray; }
void AssertArray(void) const { ASSERT(type == MIArray); }
bool IsTuple(void) const { return type == MITuple; }
void AssertTuple(void) const { ASSERT(type == MITuple); }
bool IsString(void) const { return type == MIString; }
void AssertString(void) const { ASSERT(type == MIString); }
// dumps a string with special chars inside
static String Dump(String const &s);
// data dump
String Dump(int level = 0) const;
// finds breakpoint data given file and line
MIValue &FindBreakpoint(String const &file, int line);
// packs names inside tuples -- to make type recognition easy
void PackNames(void);
// fix arrays -- i.e. replace a tuple containing ALL unnamed elements
// with the corresponding array
void FixArrays(void);
// add some data to a value
// add an item to a tuple
MIValue &Add(String const &key, MIValue&& v);
MIValue &Add(String const &key, String const &data);
MIValue &FindAdd(String const &key, String const &data);
// add an item to an array
MIValue &Add(MIValue&& v);
MIValue &Add(String const &data);
// remove a tuple key
MIValue &Remove(String const &key);
};
#endif

View file

@ -1,45 +0,0 @@
#include "TypeSimplify.h"
static VectorMap<String, TYPE_SIMPLIFIER_HANDLER> &GetSimplifierMap(void)
{
static VectorMap<String, TYPE_SIMPLIFIER_HANDLER> map;
return map;
}
void RegisterSimplifier(const char *pattern, TYPE_SIMPLIFIER_HANDLER handler)
{
GetSimplifierMap().Add(pattern, handler);
}
TYPE_SIMPLIFIER_HANDLER GetSimplifier(String const &pattern)
{
VectorMap<String, TYPE_SIMPLIFIER_HANDLER> &map = GetSimplifierMap();
for(int i = 0; i < map.GetCount(); i++)
{
if(pattern.StartsWith(map.GetKey(i)))
return map[i];
}
return NULL;
}
// next index when stepping through containers values
int SIMPLIFIER_NEXT_INDEX(MIValue &val, int total)
{
int step;
int idx = val.Find(SIMPLIFY_STEP);
if(idx < 0)
{
step = 0;
val.Add(SIMPLIFY_STEP, "0");
}
else
{
step = atoi(val[SIMPLIFY_STEP].ToString()) + 1;
if(step >= total)
val.Remove(SIMPLIFY_STEP);
else
val[SIMPLIFY_STEP].Set(FormatInt(step));
}
return step;
}

View file

@ -1,42 +0,0 @@
#ifndef _ide_Debuggers_TypeSimplify_h_
#define _ide_Debuggers_TypeSimplify_h_
#include "Debuggers.h"
#include "VarItem.h"
#define SIMPLIFY_EXPR "<!EXPR>"
#define SIMPLIFY_VALUE "<!VALUE>"
#define SIMPLIFY_TEMPVAL "<!TEMPVAL>"
#define SIMPLIFY_HINT "<!HINT>"
#define SIMPLIFY_START "<!START>"
#define SIMPLIFY_COUNT "<!COUNT>"
#define SIMPLIFY_STEP "<!STEP>"
#define SIMPLIFY_STEPVAL "<!STEPVAL>"
#define SIMPLIFY_SIMPLE "<!SIMPLE>"
#define SIMPLIFY_ARRAY "<!ARRAY>"
#define SIMPLIFY_MAP "<!MAP>"
// Simplifier handler
// 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);
TYPE_SIMPLIFIER_HANDLER GetSimplifier(String const &pattern);
// next index when stepping through containers values
int SIMPLIFIER_NEXT_INDEX(MIValue &val, int total);
#define REGISTERSIMPLIFIER(pattern, handler) \
INITBLOCK { \
RegisterSimplifier(pattern, handler); \
}
#endif

View file

@ -1,669 +0,0 @@
#include "TypeSimplify.h"
#define EVALDEEP
#define EVALDEEP_VECTOR 5
#define EVALDEEP_ARRAY 5
#define EVALDEEP_VECTORMAP 5
#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
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int UppStringSimplify(VarItem &varItem, int step)
{
enum { SMALL = 0, MEDIUM = 31 }; // SMALL has to be 0 because of GetSpecial and because is it ending zero
enum { KIND = 14, SLEN = 15, LLEN = 2, SPECIAL = 13 };
union
{
char chr[16];
char *ptr;
dword *wptr;
qword *qptr;
word v[8];
dword w[4];
qword q[2];
} u;
// 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)
{
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 int UppVectorSimplify(VarItem &varItem, int step)
{
// 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 + ".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;
// 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 << " = [...]";
return 0;
}
vItem.Simplify();
if(!step)
varItem.value << " = [ ]";
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 UppVectorMapSimplify(VarItem &varItem, int step)
{
// 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::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;
// 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 << " = {...}";
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 << " = {...}";
return 0;
}
vItem.Simplify();
if(!step)
varItem.value << " = { }";
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)
{
// 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_ARRAY, varItem.items);
// start from item 0
step -= 2;
// 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 << " = [...]";
return 0;
}
if(!step)
varItem.value << " = [ ]";
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)
{
// 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;
// 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 << " = {...}";
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 << " = {...}";
return 0;
}
vItem.Simplify();
if(!step)
varItem.value << " = { }";
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)
{
// 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;
// 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 << " = [...]";
return 0;
}
vItem.Simplify();
if(!step)
varItem.value << " = [ ]";
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 UppOneSimplify(VarItem &varItem, int step)
{
// setup item type
varItem.kind = VarItem::COMPLEX;
// 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
// de-reference and forward simplify
MIValue val = varItem.EvaluateExpression(varItem.evaluableExpression + "." + "ptr");
if(val.IsError() || !val.IsString())
{
varItem.value = "Upp::One<> = <can't evaluate contents>";
return 0;
}
String ptr = val.ToString();
if(ptr == "0x0")
{
varItem.value = "Upp::One<> = <EMPTY>";
return 0;
}
// replace variable with de-referenced one
VarItem vItem(&varItem.Debugger(), "*" + varItem.evaluableExpression + "." + "ptr");
varItem = vItem;
return varItem.GetSimplifyStep();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int UppTimeSimplify(VarItem &varItem, int step)
{
MIValue val = varItem.EvaluateExpression(varItem.evaluableExpression);
val.PackNames();
int day = atoi(val[0]["day"].ToString());
int month = atoi(val[0]["month"].ToString());
int year = atoi(val[0]["year"].ToString());
int hour = atoi(val["hour"].ToString());
int minute = atoi(val["minute"].ToString());
int second = atoi(val["second"].ToString());
varItem.value = Format("Upp::Time = %02d/%02d/%04d - %02d:%02d:%02d", day, month, year, hour, minute, second);
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int UppDateSimplify(VarItem &varItem, int step)
{
MIValue val = varItem.EvaluateExpression(varItem.evaluableExpression);
val.PackNames();
int day = atoi(val["day"].ToString());
int month = atoi(val["month"].ToString());
int year = atoi(val["year"].ToString());
varItem.value = Format("Upp::Date = %02d/%02d/%04d", day, month, year);
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int UppValueSimplify(VarItem &varItem, int step)
{
enum { SMALL = 0, MEDIUM = 31 }; // SMALL has to be 0 because of GetSpecial and because is it ending zero
enum { KIND = 14, SLEN = 15, LLEN = 2, SPECIAL = 13 };
enum { STRING = 0, REF = 255, VOIDV = 3 };
// get the embedded 'data' string 'chr' member
// it contains info about value type
union
{
char chr[16];
char *ptr;
dword *wptr;
qword *qptr;
word v[8];
dword w[4];
qword q[2];
int iData;
int64 i64Data;
double dData;
bool bData;
struct
{
byte day;
byte month;
int16 year;
byte hour;
byte minute;
byte second;
};
} u;
// see Upp::String code for how it works....
MIValue val = varItem.EvaluateExpression("(" + varItem.evaluableExpression + ").data.chr");
if(!val.IsString())
return 0;
String chrs = val.ToString();
memcpy(u.chr, ~chrs, 16);
// get value type, among the fixed ones
// we could try later to decode registered types....
dword type;
bool isSpecial = !u.v[7] && u.v[6];
if(!isSpecial)
type = STRING_V;
else
{
byte st = u.chr[SPECIAL];
if(st == REF)
{
// ptr()->GetType()
// by now, just mark as ref...
type = REF;
}
else if(st == VOIDV)
type = VOID_V;
else
type = st;
}
// by now, treat all types beyond VALUEMAP_V as unknown
if(type > VALUEMAP_V)
type = UNKNOWN_V;
// now, based on type, we can decode it
varItem.kind = VarItem::SIMPLE;
switch(type)
{
case VOID_V:
{
varItem.value = "<VOID>";
return 0;
}
case INT_V:
{
varItem.value = FormatInt(u.iData);
return 0;
}
case DOUBLE_V:
{
varItem.value = FormatDouble(u.dData);
return 0;
}
case STRING_V:
{
// we simply replace the VarItem with the string
VarItem vItem(&varItem.Debugger(), "(" + varItem.evaluableExpression + ").data");
vItem.evaluableExpression = varItem.evaluableExpression;
vItem.shortExpression = varItem.shortExpression;
varItem = vItem;
return 0;
}
case DATE_V:
{
varItem.value = Format("Upp::Date = %02d/%02d/%04d", u.day, u.month, u.year);
return 0;
}
case TIME_V:
{
varItem.value = Format("Upp::Time = %02d/%02d/%04d - %02d:%02d:%02d", u.day, u.month, u.year, u.hour, u.minute, u.second);
return 0;
}
break;
case ERROR_V:
{
varItem.value = "<ERROR_V>";
return 0;
}
case VALUE_V:
{
varItem.value = "<VALUE_V>";
return 0;
}
case WSTRING_V:
{
varItem.value = "<WSTRING_V>";
return 0;
}
case VALUEARRAY_V:
{
varItem.value = "<VALUEARRAY_V>";
return 0;
}
case INT64_V:
{
varItem.value = FormatInt64(u.i64Data);
return 0;
}
case BOOL_V:
{
varItem.value = (u.bData ? "TRUE" : "FALSE");
return 0;
}
case VALUEMAP_V:
{
varItem.value = "<VALUEMAP_V>";
return 0;
}
case UNKNOWN_V:
default:
{
varItem.value = "<UNKNOWN_V>";
return 0;
}
}
}
INITIALIZER(UppSimplifiers) {}
// Register the simplifiers
REGISTERSIMPLIFIER("Upp::String" , UppStringSimplify);
REGISTERSIMPLIFIER("Upp::Vector<" , UppVectorSimplify);
REGISTERSIMPLIFIER("Upp::VectorMap<" , UppVectorMapSimplify);
REGISTERSIMPLIFIER("Upp::Array<" , UppArraySimplify);
REGISTERSIMPLIFIER("Upp::ArrayMap<" , UppArrayMapSimplify);
REGISTERSIMPLIFIER("Upp::Index<" , UppIndexSimplify);
REGISTERSIMPLIFIER("Upp::One<" , UppOneSimplify);
REGISTERSIMPLIFIER("Upp::Time" , UppTimeSimplify);
REGISTERSIMPLIFIER("Upp::Date" , UppDateSimplify);
REGISTERSIMPLIFIER("Upp::Value" , UppValueSimplify);

View file

@ -1,238 +0,0 @@
#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(const VarItem &v)
{
debugger = v.debugger;
empty = v.empty;
simplifyStep = v.simplifyStep;
varName = v.varName;
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=(const VarItem &v)
{
debugger = v.debugger;
empty = v.empty;
simplifyStep = v.simplifyStep;
varName = v.varName;
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)
{
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 variable name for later cleanup
debugger->StoreVariable(varName);
// 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;
}
MIValue VarItem::EvaluateExpression(String const &exp) const
{
MIValue val = debugger->MICmd("data-evaluate-expression " + exp);
if(!val.IsTuple())
return MIValue();
const MIValue& v = val["value"];
if(v.IsError() || !v.IsString())
return MIValue();
String s = v.ToString();
return MIValue(s);
}

View file

@ -1,100 +0,0 @@
#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);
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);
// get next simplify step
int GetSimplifyStep(void) { return simplifyStep; }
// constructors
VarItem(Gdb_MI2 *dbg);
VarItem(Gdb_MI2 *dbg, String const &expr);
// destructor
~VarItem();
// copy
VarItem(const VarItem &v);
VarItem const &operator=(const 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 EvaluateExpression(String const &exp) const;
};
#endif

View file

@ -483,7 +483,6 @@ void Ide::SetupFormat() {
(ide.mute_sounds, mute_sounds)
(ide.wrap_console_text, wrap_console_text)
(ide.hydra1_threads, hydra1_threads)
(ide.gdbSelector, gdbSelector)
(ide.chstyle, chstyle)
(ide.console, LinuxHostConsole)
(ide.output_per_assembly, output_per_assembly)

View file

@ -430,8 +430,6 @@ public:
int debuglock;
int hydra1_threads;
int gdbSelector;
int chstyle;
One<IdeDesigner> designer;

View file

@ -589,9 +589,7 @@ LAYOUT(SetupIdeLayout, 512, 264)
ITEM(Label, dv___16, SetLabel(t_("HYDRA threads")).LeftPosZ(212, 80).TopPosZ(4, 19))
ITEM(EditIntSpin, hydra1_threads, Max(64).Min(1).LeftPosZ(344, 48).TopPosZ(4, 19))
ITEM(Option, output_per_assembly, SetLabel(t_("Use unique output directory per assembly (append assembly name to output directory)")).HSizePosZ(4, 4).TopPosZ(224, 16))
ITEM(Switch, gdbSelector, SetLabel(t_("Standard\nGDB_MI2 (experimental)")).LeftPosZ(344, 176).TopPosZ(28, 36))
ITEM(Label, dv___20, SetLabel(t_("GDB Debugger interface")).LeftPosZ(212, 188).TopPosZ(24, 20))
ITEM(Label, dv___21, SetLabel(t_("In editor mode, path for .usc files")).LeftPosZ(4, 192).TopPosZ(244, 20))
ITEM(Label, dv___19, SetLabel(t_("In editor mode, path for .usc files")).LeftPosZ(4, 192).TopPosZ(244, 20))
ITEM(EditString, uscpath, LeftPosZ(172, 336).TopPosZ(244, 19))
END_LAYOUT

View file

@ -386,8 +386,6 @@ Ide::Ide()
build_start_time = Null;
hydra1_threads = CPU_Cores();
gdbSelector = 0;
chstyle = 0;
Sizeable().Zoomable();