fd00ff500cd0f9f25e855ddc7803b8c9a69f143d
[sdk] / ide / src / debugger / Debugger.ec
1 #ifdef ECERE_STATIC
2 public import static "ecere"
3 public import static "ec"
4 #else
5 public import "ecere"
6 public import "ec"
7 #endif
8
9 import "ide"
10 import "process"
11 import "debugFindCtx"
12 import "debugTools"
13
14 #ifdef _DEBUG
15 #define GDB_DEBUG_CONSOLE
16 #endif
17
18 extern char * strrchr(const char * s, int c);
19
20 #define uint _uint
21 #define strlen _strlen
22 #include <stdarg.h>
23 #include <unistd.h>
24
25 #ifdef __APPLE__
26 #define __unix__
27 #endif
28
29 #if defined(__unix__)
30 #include <sys/stat.h>
31 #include <sys/time.h> // Required on Apple...
32 #endif
33 #undef uint
34 #undef strlen
35
36 char * PrintNow()
37 {
38    int c;
39    char * s[6];
40    char * time;
41    DateTime now;
42    now.GetLocalTime();
43    for(c=0; c<6; c++)
44       s[c] = new char[8];
45    sprintf(s[0], "%04d", now.year);
46    sprintf(s[1], "%02d", now.month+1);
47    sprintf(s[2], "%02d", now.day);
48    sprintf(s[3], "%02d", now.hour);
49    sprintf(s[4], "%02d", now.minute);
50    sprintf(s[5], "%02d", now.second);
51    time = PrintString("*", s[0], s[1], s[2], "-", s[3], s[4], s[5], "*");
52    for(c=0; c<6; c++)
53       delete s[c];
54    return time;
55 }
56
57 // use =0 to disable printing of specific channels
58 #ifdef _DEBUG
59 static enum dplchan { none, gdbProtoIgnored=0/*1*/, gdbProtoUnknown=2, gdbOutput=3/*3*/, gdbCommand=4/*4*/, debuggerCall=5, debuggerProblem=6, debuggerTemp=7 };
60 #else
61 static enum dplchan { none, gdbProtoIgnored=0, gdbProtoUnknown=0, gdbOutput=0, gdbCommand=0, debuggerCall=0, debuggerProblem=0, debuggerTemp=0 };
62 #endif
63 static char * _dpct[] = {
64    null,
65    "GDB Protocol Ignored",
66    "GDB Protocol ***Unknown***",
67    "GDB Output",
68    "GDB Command",
69    ""/*Debugger Call*/,
70    "Debugger ***Problem***",
71    "-----> Temporary Message",
72    null
73 };
74
75 // TODO if(strlen(item.value) < MAX_F_STRING)
76
77 #define _dpl2(...) __dpl2(__FILE__, __LINE__, ##__VA_ARGS__)
78 static void __dpl2(char * file, int line, char ** channels, int channel, int indent, typed_object object, ...)
79 {
80    bool chan = channel && channels && channels[channel];
81    if(chan || !channels)
82    {
83       char string[MAX_F_STRING];
84       int len;
85       char * time = PrintNow();
86       va_list args;
87       //ide.outputView.debugBox.Logf();
88       Logf("%s %s:% 5d: %s%s", time, file, line, chan ? channels[channel] : "", chan && channels[channel][0] ? ": " : "");
89       va_start(args, object);
90       len = PrintStdArgsToBuffer(string, sizeof(string), object, args);
91       Log(string);
92       va_end(args);
93       Log("\n");
94       delete time;
95    }
96 }
97
98 #define _dpl(...) __dpl(__FILE__, __LINE__, ##__VA_ARGS__)
99 static void __dpl(char * file, int line, int indent, char * format, ...)
100 {
101    va_list args;
102    char string[MAX_F_STRING];
103    int c;
104    char * time = PrintNow();
105    //static File f = null;
106    va_start(args, format);
107    vsnprintf(string, sizeof(string), format, args);
108    string[sizeof(string)-1] = 0;
109    /*if(!f)
110    {
111       char * time = PrintNow();
112       char * logName;
113       logName = PrintString(time, ".log");
114       delete time;
115       f = FileOpen(logName, write);
116       delete logName;
117    }*/
118    /*f.Printf("%s %s:% 5d: ", time, file, line);
119    for(c = 0; c<indent; c++)
120       f.Putc(' ');
121    f.Printf("%s\n", string);*/
122    Logf("%s %s:% 5d: ", time, file, line);
123    for(c = 0; c<indent; c++)
124       Log(" ");
125    Logf("%s\n", string);
126    va_end(args);
127    delete time;
128 }
129
130 public char * StripQuotes2(char * string, char * output)
131 {
132    int c;
133    int d = 0;
134    bool quoted = false, escaped = false;
135    char ch;
136    for(c = 0; ch = string[c]; c++)
137    {
138       if(quoted)
139       {
140          if(escaped || ch != '\"')
141          {
142             output[d++] = ch;
143             escaped = !escaped && ch == '\\';
144          }
145          else
146             quoted = false;
147       }
148       else if(ch == '\"')
149          quoted = true;
150       else
151          output[d++] = ch;
152    }
153    output[d] = '\0';
154    return output;
155 }
156
157 // String Escape Copy
158 static void strescpy(char * d, char * s)
159 {
160    int j, k;
161    j = k = 0;
162    while(s[j])
163    {
164       switch(s[j])
165       {
166          case '\n':
167             d[k] = '\\';
168             d[++k] = 'n';
169             break;
170          case '\t':
171             d[k] = '\\';
172             d[++k] = 't';
173             break;
174          case '\a':
175             d[k] = '\\';
176             d[++k] = 'a';
177             break;
178          case '\b':
179             d[k] = '\\';
180             d[++k] = 'b';
181             break;
182          case '\f':
183             d[k] = '\\';
184             d[++k] = 'f';
185             break;
186          case '\r':
187             d[k] = '\\';
188             d[++k] = 'r';
189             break;
190          case '\v':
191             d[k] = '\\';
192             d[++k] = 'v';
193             break;
194          case '\\':
195             d[k] = '\\';
196             d[++k] = '\\';
197             break;
198          case '\"':
199             d[k] = '\\';
200             d[++k] = '\"';
201             break;
202          default:
203             d[k] = s[j];
204       }
205       ++j;
206       ++k;
207    }
208    d[k] = s[j];
209 }
210
211 static char * CopyUnescapedSystemPath(char * p)
212 {
213    char * d = new char[strlen(p) + 1];
214    struscpy(d, p);
215 #if defined(__WIN32__)
216    ChangeCh(d, '/', '\\');
217 #endif
218    return d;
219 }
220
221 static char * CopyUnescapedUnixPath(char * p)
222 {
223    char * d = new char[strlen(p) + 1];
224    struscpy(d, p);
225 #if defined(__WIN32__)
226    ChangeCh(d, '\\', '/');
227 #endif
228    return d;
229 }
230
231 static char * CopyUnescapedString(char * s)
232 {
233    char * d = new char[strlen(s) + 1];
234    struscpy(d, s);
235    return d;
236 }
237
238 // String Unescape Copy
239
240 // TOFIX: THIS DOESN'T HANDLE NUMERIC ESCAPE CODES (OCTAL/HEXADECIMAL...)?
241 // Seems very similar to ReadString in pass15.ec (which also misses numeric escape codes :) )
242
243 static void struscpy(char * d, char * s)
244 {
245    int j, k;
246    j = k = 0;
247    while(s[j])
248    {
249       switch(s[j])
250       {
251          case '\\':
252             switch(s[++j])
253             {
254                case 'n':
255                   d[k] = '\n';
256                   break;
257                case 't':
258                   d[k] = '\t';
259                   break;
260                case 'a':
261                   d[k] = '\a';
262                   break;
263                case 'b':
264                   d[k] = '\b';
265                   break;
266                case 'f':
267                   d[k] = '\f';
268                   break;
269                case 'r':
270                   d[k] = '\r';
271                   break;
272                case 'v':
273                   d[k] = '\v';
274                   break;
275                case '\\':
276                   d[k] = '\\';
277                   break;
278                case '\"':
279                   d[k] = '\"';
280                   break;
281                default:
282                   d[k] = '\\';
283                   d[++k] = s[j];
284             }
285             break;
286          default:
287             d[k] = s[j];
288       }
289       ++j;
290       ++k;
291    }
292    d[k] = s[j];
293 }
294
295 static char * StripBrackets(char * string)
296 {
297    int length = strlen(string);
298    if(length > 1 && *string == '[' && string[length - 1] == ']')
299    {
300       *string = '\0';
301       string[length - 1] = '\0';
302       return ++string;
303    }
304    else
305       return string;
306 }
307
308 static char * StripCurlies(char * string)
309 {
310    int length = strlen(string);
311    if(length > 1 && *string == '{' && string[length - 1] == '}')
312    {
313       *string = '\0';
314       string[length - 1] = '\0';
315       return ++string;
316    }
317    else
318       return string;
319 }
320
321 static int StringGetInt(char * string, int start)
322 {
323    char number[8];
324    int i, len = strlen(string);
325    number[0] = '\0';
326    for(i = start; i < len && i < start + 8; i++)
327    {
328       if(string[i] == '0' || string[i] == '1' || string[i] == '2' || string[i] == '3' || string[i] == '4' || string[i] == '5' || string[i] == '6' || string[i] == '7' || string[i] == '8' || string[i] == '9')
329          strncat(number, &string[i], 1);
330       else
331          break;
332    }
333    return atoi(number);
334 }
335
336 static int TokenizeList(char * string, const char seperator, Array<char *> tokens)
337 {
338    uint level = 0;
339    
340    bool quoted = false, escaped = false;
341    char * start = string, ch;
342    
343    for(; (ch = *string); string++)
344    {
345       if(!start)
346          start = string;
347
348       if(quoted)
349       {
350          if(escaped || ch != '\"')
351             escaped = !escaped && ch == '\\';
352          else
353             quoted = false;
354       }
355       else if(ch == '\"')
356          quoted = true;
357       else if(ch == '{' || ch == '[' || ch == '(' || ch == '<')
358          level++;
359       else if(ch == '}' || ch == ']' || ch == ')' || ch == '>')
360          level--;
361       else if(ch == seperator && !level)
362       {
363          tokens.Add(start);
364          *string = '\0';
365          start = null;
366       }
367    }
368    if(start)
369    {
370       //tokens[count] = start;
371       //tokens[count++] = start;
372       tokens.Add(start);
373       *string = '\0';
374    }
375    return tokens.count;
376 }
377
378 static bool TokenizeListItem(char * string, DebugListItem item)
379 {
380    char * equal = strstr(string, "=");
381    if(equal)
382    {
383       item.name = string;
384       *equal = '\0';
385       equal++;
386       item.value = equal;
387       equal = null;
388       return true;
389    }
390    else
391       return false;
392 }
393
394 static bool CheckCommandAvailable(const char * command)
395 {
396    bool available = false;
397    int c, count;
398    char * name = new char[MAX_FILENAME];
399    char * pathVar = new char[maxPathLen];
400    char * paths[128];
401    GetEnvironment("PATH", pathVar, maxPathLen);
402    count = TokenizeWith(pathVar, sizeof(paths) / sizeof(char *), paths, pathListSep, false);
403    strcpy(name, command);
404 #ifdef __WIN32__
405    {
406       int e;
407       const char * extensions[] = { "exe", "com", "bat", null };
408       for(e=0; extensions[e]; e++)
409       {
410          ChangeExtension(name, extensions[e], name);
411 #endif
412          for(c=0; c<count; c++)
413          {
414             FileListing fl { paths[c] };
415             while(fl.Find())
416             {
417                if(fl.stats.attribs.isFile && !fstrcmp(fl.name, name))
418                {
419                   available = true;
420                   break;
421                }
422             }
423             if(available) break;
424          }
425 #ifdef __WIN32__
426          if(available) break;
427       }
428    }
429 #endif
430    delete name;
431    delete pathVar;
432    return available;
433 }
434
435 // define GdbGetLineSize = 1638400;
436 define GdbGetLineSize = 5638400;
437 #if defined(__unix__)
438 char progFifoPath[MAX_LOCATION];
439 char progFifoDir[MAX_LOCATION];
440 #endif
441
442 enum DebuggerState { none, prompt, loaded, running, stopped, terminated, error };
443 enum DebuggerEvent
444 {
445    none, hit, breakEvent, signal, stepEnd, functionEnd, exit, valgrindStartPause;
446
447    property bool canBeMonitored { get { return (this == hit || this == breakEvent || this == signal || this == stepEnd || this == functionEnd); } };
448 };
449 enum DebuggerAction { none, internal, restart, stop, selectFrame }; //, bpValidation
450 enum BreakpointType
451 {
452    none, internalMain, internalWinMain, internalModulesLoaded, user, runToCursor, internalModuleLoad;
453
454    property bool isInternal { get { return (this == internalMain || this == internalWinMain || this == internalModulesLoaded || this == internalModuleLoad); } };
455    property bool isUser { get { return (this == user || this == runToCursor); } };
456 };
457 enum DebuggerEvaluationError { none, symbolNotFound, memoryCantBeRead, unknown };
458
459 FileDialog debuggerFileDialog { type = selectDir };
460
461 static DualPipe vgTargetHandle;
462 static File vgLogFile;
463 static char vgLogPath[MAX_LOCATION];
464 static DualPipe gdbHandle;
465 static DebugEvaluationData eval { };
466
467 static int targetProcessId;
468
469 static bool gdbReady;
470 static bool breakpointError;
471
472 class Debugger
473 {
474    Semaphore serialSemaphore { };
475    bool waitingForPID;
476    bool targeted;
477    bool symbols;
478    bool modules;
479    bool sentKill;
480    bool sentBreakInsert;
481    bool ignoreBreakpoints;
482    bool userBreakOnInternalBreakpoint;
483    //bool runToCursorDebugStart;
484    bool signalOn;
485    bool needReset;
486    //bool watchesInit;
487    bool usingValgrind;
488
489    int ideProcessId;
490    int gdbProcessId;
491
492    int activeFrameLevel;
493    int activeThread;
494    int hitThread;
495    int signalThread;
496    int frameCount;
497
498    char * targetDir;
499    char * targetFile;
500    
501    DebuggerState state;
502    DebuggerEvent event;
503    DebuggerAction breakType;
504    //DebuggerCommand lastCommand;    // THE COMPILER COMPILES STUFF THAT DOES NOT EXIST???
505
506    GdbDataStop stopItem;
507    GdbDataBreakpoint bpItem;
508    Frame activeFrame;
509    
510    List<Breakpoint> sysBPs { };
511    Breakpoint bpRunToCursor;
512
513    OldList stackFrames;
514
515    CompilerConfig currentCompiler;
516    ProjectConfig prjConfig;
517    int bitDepth;
518
519    CodeEditor codeEditor;
520
521    ValgrindLogThread vgLogThread { debugger = this };
522    ValgrindTargetThread vgTargetThread { debugger = this };
523    GdbThread gdbThread { debugger = this };
524    Timer gdbTimer
525    {
526       delay = 0.0, userData = this;
527
528       bool DelayExpired()
529       {
530          bool monitor = false;
531          DebuggerEvent curEvent = event;
532          GdbDataStop stopItem = this.stopItem;
533          Breakpoint bpUser = null;
534          Breakpoint bpInternal = null;
535
536          if(!gdbReady)
537             return false;
538
539          event = none;
540          if(this.stopItem)
541             this.stopItem = null;
542          else
543          {
544             if(curEvent && curEvent != exit)
545             {
546 #ifdef _DEBUG
547                _dpl(0, "No stop item");
548 #endif
549             }
550          }
551          switch(breakType)
552          {
553             case restart:
554                breakType = none;
555                Restart(currentCompiler, prjConfig, bitDepth, usingValgrind);
556                break;
557             case stop:
558                breakType = none;
559                Stop();
560                break;
561             case selectFrame:
562             {
563                breakType = none;
564                GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
565                for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
566                   if(activeFrame.level == activeFrameLevel)
567                      break;
568                break;
569             }
570             //case bpValidation:
571             //   breakType = none;
572             //   GdbCommand(false, "-break-info %s", bpItem.number);
573             //   break;
574          }
575
576          if(curEvent == none)
577             return false;
578
579          switch (curEvent)
580          {
581             case breakEvent:
582                activeThread = stopItem.threadid;
583                GdbCommand(false, "-thread-list-ids");
584                GdbGetStack();
585                break;
586             case hit:
587                {
588                   bool isInternal;
589                   Breakpoint bp = stopItem ? GetBreakpointById(stopItem.bkptno, &isInternal) : null;
590                   if(bp && bp.inserted && bp.bp.addr)
591                   {
592                      if(bp.type.isInternal)
593                         bpInternal = bp;
594                      else
595                         bpUser = bp;
596                      if(stopItem && stopItem.frame)
597                      {
598                         if(bpInternal && bpRunToCursor && bpRunToCursor.inserted && !strcmp(bpRunToCursor.bp.addr, bp.bp.addr))
599                            bpUser =  bpRunToCursor;
600                         else
601                         {
602                            for(item : (bpInternal ? ide.workspace.breakpoints : sysBPs); item.inserted)
603                            {
604                               if(item.bp && item.bp.addr && !strcmp(item.bp.addr, bp.bp.addr))
605                               {
606                                  if(bpInternal)
607                                     bpUser = item;
608                                  else
609                                     bpInternal = item;
610                                  break;
611                               }
612                            }
613                         }
614                      }
615                      else
616                         _dpl2(_dpct, dplchan::debuggerProblem, 0, "Invalid stopItem!");
617                      if(bpUser && strcmp(stopItem.frame.addr, bpUser.bp.addr))
618                         bpUser = null;
619                   }
620                   else
621                      _dpl2(_dpct, dplchan::debuggerProblem, 0, "Breakpoint bkptno(", stopItem.bkptno, ") invalid or not found!");
622                   if(bpUser && bpUser.type == runToCursor)
623                      ignoreBreakpoints = false;
624                   if((bpUser && !ignoreBreakpoints) || (bpInternal && userBreakOnInternalBreakpoint))
625                      monitor = true;
626                   hitThread = stopItem.threadid;
627                }
628                break;
629             case signal:
630                signalThread = stopItem.threadid;
631             case stepEnd:
632             case functionEnd:
633                monitor = true;
634                ignoreBreakpoints = false;
635                break;
636             case valgrindStartPause:
637                GdbExecContinue(true);
638                break;
639             case exit:
640                HideDebuggerViews();
641                break;
642          }
643
644          if(monitor)
645          {
646             activeThread = stopItem.threadid;
647             GdbCommand(false, "-thread-list-ids");
648             GdbGetStack();
649             if(activeFrameLevel > 0)
650                GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
651
652             WatchesCodeEditorLinkInit();
653             EvaluateWatches();
654          }
655
656          if(curEvent == signal)
657          {
658             char * s;
659             signalOn = true;
660             ide.outputView.debugBox.Logf($"Signal received: %s - %s\n", stopItem.name, stopItem.meaning);
661             ide.outputView.debugBox.Logf("    %s:%d\n", (s = CopySystemPath(stopItem.frame.file)), stopItem.frame.line);
662             ide.outputView.Show();
663             ide.callStackView.Show();
664             delete s;
665          }
666          else if(curEvent == breakEvent)
667          {
668             ide.threadsView.Show();
669             ide.callStackView.Show();
670             ide.callStackView.Activate();
671          }
672
673          if(monitor && curEvent.canBeMonitored)
674          {
675             SelectFrame(activeFrameLevel);
676             GoToStackFrameLine(activeFrameLevel, true);
677             ide.ShowCodeEditor();
678             ideMainFrame.Activate();   // TOFIX: ide.Activate() is not reliable (app inactive)
679             ide.Update(null);
680          }
681
682          if(curEvent == hit)
683             EventHit(stopItem, bpInternal, bpUser);
684
685          if(stopItem)
686          {
687             stopItem.Free();
688             delete stopItem;
689          }
690          if(userBreakOnInternalBreakpoint)
691             userBreakOnInternalBreakpoint = false;
692          return false;
693       }
694    };
695
696 #ifdef GDB_DEBUG_CONSOLE
697    char lastGdbOutput[GdbGetLineSize];
698 #endif
699 #if defined(__unix__)
700    ProgramThread progThread { };
701 #endif
702
703    void ChangeState(DebuggerState value)
704    {
705       bool same = value == state;
706       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ChangeState (", state, same ? " *** == *** " : " -> ", value, ")");
707       state = value;
708       if(!same && ide) ide.AdjustDebugMenus();
709    }
710
711    void CleanUp()
712    {
713       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::CleanUp");
714
715       stackFrames.Free(Frame::Free);
716
717       delete targetDir;
718       delete targetFile;
719
720       ClearBreakDisplay();
721
722       // Clear Stuff up
723       gdbProcessId = 0;
724
725       waitingForPID = false;
726       targeted = false;
727       symbols = false;
728       modules = false;
729       sentKill = false;
730       sentBreakInsert = false;
731       ignoreBreakpoints = false;
732       userBreakOnInternalBreakpoint = false;
733       //runToCursorDebugStart = false;
734       signalOn = false;
735
736       activeFrameLevel = 0;
737       activeThread = 0;
738       hitThread = 0;
739       signalThread = 0;
740       frameCount = 0;
741
742       targetDir = null;
743       targetFile = null;
744       
745       ChangeState(none);
746       event = none;
747       breakType = none;
748
749       stopItem = null;
750       bpItem = null;
751       activeFrame = 0;
752       
753       bpRunToCursor = null;
754
755       delete currentCompiler;
756       prjConfig = null;
757       codeEditor = null;
758
759       /*GdbThread gdbThread
760       Timer gdbTimer*/
761    }
762    
763    Debugger()
764    {
765       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::constructor");
766       ideProcessId = Process_GetCurrentProcessId();
767
768       sysBPs.Add(Breakpoint { type = internalMain, function = "main", enabled = true, level = -1 });
769 #if defined(__WIN32__)
770       sysBPs.Add(Breakpoint { type = internalWinMain, function = "WinMain", enabled = true, level = -1 });
771 #endif
772       sysBPs.Add(Breakpoint { type = internalModulesLoaded, enabled = true, level = -1 });
773       sysBPs.Add(Breakpoint { type = internalModuleLoad, function = "InternalModuleLoadBreakpoint", enabled = true, level = -1 });
774    }
775
776    ~Debugger()
777    {
778       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::destructor");
779       sysBPs.Free();
780       Stop();
781       CleanUp();
782    }
783
784    // PUBLIC MEMBERS
785
786    property bool isActive { get { return state == running || state == stopped; } }
787    property bool isPrepared  { get { return state == loaded || state == running || state == stopped; } }
788
789    void Resume()
790    {
791       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Resume");
792       GdbExecContinue(true);
793    }
794
795    void Break()
796    {
797       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Break");
798       if(state == running)
799       {
800          if(targetProcessId)
801             GdbDebugBreak(false);
802       }
803    }
804
805    void Stop()
806    {
807       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Stop");
808       switch(state)
809       {
810          case running:
811             if(targetProcessId)
812             {
813                breakType = stop;
814                GdbDebugBreak(false);
815             }
816             break;
817          case stopped:
818             GdbAbortExec();
819             HideDebuggerViews();
820             GdbExit();
821             break;
822          case loaded:
823             GdbExit();
824             break;
825       }
826    }
827
828    void Restart(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
829    {
830       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Restart");
831       if(StartSession(compiler, config, bitDepth, useValgrind, true, false, false/*, false*/) == loaded)
832          GdbExecRun();
833    }
834
835    bool GoToCodeLine(char * location)
836    {
837       CodeLocation codloc;
838       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GoToCodeLine(", location, ")");
839       codloc = CodeLocation::ParseCodeLocation(location);
840       if(codloc)
841       {
842          CodeEditor editor = (CodeEditor)ide.OpenFile(codloc.absoluteFile, normal, true, null, no, normal, false);
843          if(editor)
844          {
845             EditBox editBox = editor.editBox;
846             editBox.GoToLineNum(codloc.line - 1);
847             editBox.GoToPosition(editBox.line, codloc.line - 1, 0);
848             return true;
849          }
850       }
851       return false;
852    }
853
854    bool GoToStackFrameLine(int stackLevel, bool askForLocation)
855    {
856       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GoToStackFrameLine(", stackLevel, ", ", askForLocation, ")");
857       if(ide)
858       {
859          char filePath[MAX_LOCATION];
860          char sourceDir[MAX_LOCATION];
861          Frame frame;
862          CodeEditor editor = null;
863          if(stackLevel == -1)  // this (the two lines) is part of that fix that I would not put in for some time
864             return false;
865          for(frame = stackFrames.first; frame; frame = frame.next)
866             if(frame.level == stackLevel)
867                break;
868          if(frame)
869          {
870             ide.callStackView.Show();
871
872             if(!frame.absoluteFile && frame.file)
873                frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
874             if(!frame.absoluteFile && askForLocation && frame.file)
875             {
876                char * s;
877                char title[MAX_LOCATION];
878                snprintf(title, sizeof(title), $"Provide source file location for %s", (s = CopySystemPath(frame.file)));
879                title[sizeof(title)-1] = 0;
880                delete s;
881                if(SourceDirDialog(title, ide.workspace.projectDir, frame.file, sourceDir))
882                {
883                   AddSourceDir(sourceDir);
884                   frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
885                }
886             }
887             if(frame.absoluteFile)
888                editor = (CodeEditor)ide.OpenFile(frame.absoluteFile, normal, true, null, no, normal, false);
889             ide.Update(null);
890             if(editor && frame.line)
891             {
892                EditBox editBox = editor.editBox;
893                editBox.GoToLineNum(frame.line - 1);
894                editBox.GoToPosition(editBox.line, frame.line - 1, 0);
895                return true;
896             }
897          }
898       }
899       return false;
900    }
901
902    void SelectThread(int thread)
903    {
904       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SelectThread(", thread, ")");
905       if(state == stopped)
906       {
907          if(thread != activeThread)
908          {
909             activeFrameLevel = -1;
910             ide.callStackView.Clear();
911             GdbCommand(false, "-thread-select %d", thread);
912             GdbGetStack();
913             // Why was SelectFrame missing here?
914             SelectFrame(activeFrameLevel);
915             GoToStackFrameLine(activeFrameLevel, true);
916             WatchesCodeEditorLinkRelease();
917             WatchesCodeEditorLinkInit();
918             EvaluateWatches();
919             ide.Update(null);
920          }
921          ide.callStackView.Show();
922       }
923    }
924
925    void SelectFrame(int frame)
926    {
927       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SelectFrame(", frame, ")");
928       if(state == stopped)
929       {
930          if(frame != activeFrameLevel || !codeEditor || !codeEditor.visible)
931          {
932             activeFrameLevel = frame;  // there is no active frame number in the gdb reply
933             GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
934             for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
935                if(activeFrame.level == activeFrameLevel)
936                   break;
937
938             WatchesCodeEditorLinkRelease();
939             WatchesCodeEditorLinkInit();
940             EvaluateWatches();
941             ide.Update(null);
942          }
943       }
944    }
945
946    void HandleExit(char * reason, char * code)
947    {
948       bool returnedExitCode = false;
949       char verboseExitCode[128];
950       
951       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::HandleExit(", reason, ", ", code, ")");
952       ChangeState(loaded); // this state change seems to be superfluous, might be in case of gdb crash
953       targetProcessId = 0;
954
955       if(code)
956       {
957          snprintf(verboseExitCode, sizeof(verboseExitCode), $" with exit code %s", code);
958          verboseExitCode[sizeof(verboseExitCode)-1] = 0;
959       }
960       else
961          verboseExitCode[0] = '\0';
962       
963       event = exit;
964
965       // ClearBreakDisplay();
966
967       if(ide.workspace)
968       {
969          for(wh : ide.workspace.watches)
970          {
971             if(wh.type) FreeType(wh.type);
972             wh.type = null;
973             delete wh.value;
974             ide.watchesView.UpdateWatch(wh);
975          }
976       }
977
978 #if defined(__unix__)
979       if(!usingValgrind)
980       {
981          progThread.terminate = true;
982          if(fifoFile)
983          {
984             fifoFile.CloseInput();
985             app.Unlock();
986             progThread.Wait();
987             app.Lock();
988             delete fifoFile;
989          }
990       }
991 #endif
992
993       {
994          char program[MAX_LOCATION];
995          GetSystemPathBuffer(program, targetFile);
996          if(!reason)
997             ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
998          else if(!strcmp(reason, "exited-normally"))
999             ide.outputView.debugBox.Logf($"The program %s has exited normally%s.\n", program, verboseExitCode);
1000          else if(!strcmp(reason, "exited"))
1001             ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
1002          else if(!strcmp(reason, "exited-signalled"))
1003             ide.outputView.debugBox.Logf($"The program %s has exited with a signal%s.\n", program, verboseExitCode);
1004          else
1005             ide.outputView.debugBox.Logf($"The program %s has exited (gdb provided an unknown reason)%s.\n", program, verboseExitCode);
1006       }
1007       ide.Update(null);
1008    }
1009       
1010    DebuggerState StartSession(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool restart, bool userBreakOnInternalBreakpoint, bool ignoreBreakpoints/*, bool runToCursorDebugStart*/)
1011    {
1012       DebuggerState result = none;
1013       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StartSession(restart(", restart, "), userBreakOnInternalBreakpoint(", userBreakOnInternalBreakpoint, "), ignoreBreakpoints(", ignoreBreakpoints, ")"/*, runToCursorDebugStart(", runToCursorDebugStart, ")"*/);
1014       if(restart && state == running && targetProcessId)
1015       {
1016          breakType = DebuggerAction::restart;
1017          GdbDebugBreak(false);
1018       }
1019       else
1020       {
1021          if(restart && state == stopped)
1022             GdbAbortExec();
1023          if(needReset && state == loaded)
1024             GdbExit(); // this reset is to get a clean state with all the breakpoints until a better state can be maintained on program exit
1025          result = state;
1026          if(result == none || result == terminated)
1027          {
1028             ide.outputView.ShowClearSelectTab(debug);
1029             ide.outputView.debugBox.Logf($"Starting debug mode\n");
1030
1031             for(bp : sysBPs)
1032             {
1033                bp.hits = 0;
1034                bp.breaks = 0;
1035             }
1036             for(bp : ide.workspace.breakpoints)
1037             {
1038                bp.hits = 0;
1039                bp.breaks = 0;
1040             }
1041
1042             //this.runToCursorDebugStart = runToCursorDebugStart;
1043
1044             if(GdbInit(compiler, config, bitDepth, useValgrind))
1045                result = state;
1046             else
1047                result = error;
1048          }
1049          this.ignoreBreakpoints = ignoreBreakpoints;
1050          this.userBreakOnInternalBreakpoint = userBreakOnInternalBreakpoint;
1051          if(ignoreBreakpoints && (result == loaded || result == stopped))
1052             GdbBreakpointsDelete(false, false);
1053       }
1054       return result;
1055    }
1056
1057    void Start(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
1058    {
1059       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Start()");
1060       if(StartSession(compiler, config, bitDepth, useValgrind, true, false, false/*, false*/) == loaded)
1061          GdbExecRun();
1062    }
1063
1064    void StepInto(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
1065    {
1066       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepInto()");
1067       switch(StartSession(compiler, config, bitDepth, useValgrind, false, true, false/*, false*/))
1068       {
1069          case loaded:  GdbExecRun();  break;
1070          case stopped: GdbExecStep(); break;
1071       }
1072    }
1073
1074    void StepOver(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool ignoreBreakpoints)
1075    {
1076       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepOver()");
1077       switch(StartSession(compiler, config, bitDepth, useValgrind, false, true, ignoreBreakpoints/*, false*/))
1078       {
1079          case loaded:  GdbExecRun();  break;
1080          case stopped: GdbExecNext(); break;
1081       }
1082    }
1083
1084    void StepOut(bool ignoreBreakpoints)
1085    {
1086       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepOut()");
1087       if(state == stopped)
1088       {
1089          this.ignoreBreakpoints = ignoreBreakpoints;
1090          if(ignoreBreakpoints)
1091             GdbBreakpointsDelete(true, false);
1092          GdbExecFinish();
1093       }
1094    }
1095
1096    void RunToCursor(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, char * absoluteFilePath, int lineNumber, bool ignoreBreakpoints, bool atSameLevel)
1097    {
1098       char relativeFilePath[MAX_LOCATION];
1099       DebuggerState st = state;
1100       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::RunToCursor()");
1101       //if(st == loaded)
1102       //{
1103       //   ide.outputView.ShowClearSelectTab(debug);
1104       //   ide.outputView.debugBox.Logf($"Starting debug mode\n");
1105       //}
1106       if(!ide.projectView.project.GetRelativePath(absoluteFilePath, relativeFilePath))
1107          strcpy(relativeFilePath, absoluteFilePath);
1108
1109       if(bpRunToCursor && bpRunToCursor.inserted && symbols)
1110       {
1111          UnsetBreakpoint(bpRunToCursor);
1112          delete bpRunToCursor;
1113       }
1114
1115       bpRunToCursor = Breakpoint { };
1116       bpRunToCursor.absoluteFilePath = absoluteFilePath;
1117       bpRunToCursor.relativeFilePath = relativeFilePath;
1118       bpRunToCursor.line = lineNumber;
1119       bpRunToCursor.type = runToCursor;
1120       bpRunToCursor.enabled = true;
1121       bpRunToCursor.level = atSameLevel ? frameCount - activeFrameLevel -1 : -1;
1122
1123       switch(StartSession(compiler, config, bitDepth, useValgrind, false, false, ignoreBreakpoints/*, true*/))
1124       {
1125          case loaded:
1126             GdbExecRun();
1127             break;
1128          case stopped:
1129             GdbExecContinue(true);
1130             break;
1131       }
1132    }
1133
1134    void GetCallStackCursorLine(bool * error, int * lineCursor, int * lineTopFrame)
1135    {
1136       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetCallStackCursorLine()");
1137       if(activeFrameLevel == -1)
1138       {
1139          *error = false;
1140          *lineCursor = 0;
1141          *lineTopFrame = 0;
1142       }
1143       else
1144       {
1145          *error = signalOn && activeThread == signalThread;
1146          *lineCursor = activeFrameLevel - ((frameCount > 192 && activeFrameLevel > 191) ? frameCount - 192 - 1 : 0) + 1;
1147          *lineTopFrame = activeFrameLevel ? 1 : 0;
1148       }
1149    }
1150
1151    int GetMarginIconsLineNumbers(char * fileName, int lines[], bool enabled[], int max, bool * error, int * lineCursor, int * lineTopFrame)
1152    {
1153       char winFilePath[MAX_LOCATION];
1154       char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1155       int count = 0;
1156       Iterator<Breakpoint> it { ide.workspace.breakpoints };
1157       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetMarginIconsLineNumbers()");
1158       while(it.Next() && count < max)
1159       {
1160          Breakpoint bp = it.data;
1161          if(bp.type == user)
1162          {
1163             if(bp.absoluteFilePath && bp.absoluteFilePath[0] && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1164             {
1165                lines[count] = bp.line;
1166                enabled[count] = bp.enabled;
1167                count++;
1168             }
1169          }
1170       }
1171       if(activeFrameLevel == -1)
1172       {
1173          *error = false;
1174          *lineCursor = 0;
1175          *lineTopFrame = 0;
1176       }
1177       else
1178       {
1179          *error = signalOn && activeThread == signalThread;
1180          if(activeFrame && activeFrame.absoluteFile && !fstrcmp(absoluteFilePath, activeFrame.absoluteFile))
1181             *lineCursor = activeFrame.line;
1182          else
1183             *lineCursor = 0;
1184          if(activeFrame && stopItem && stopItem.frame && activeFrame.level == stopItem.frame.level)
1185             *lineTopFrame = 0;
1186          else if(stopItem && stopItem.frame && stopItem.frame.absoluteFile && !fstrcmp(absoluteFilePath, stopItem.frame.absoluteFile))
1187             *lineTopFrame = stopItem.frame.line;
1188          else
1189             *lineTopFrame = 0;
1190          
1191          if(*lineTopFrame == *lineCursor && *lineTopFrame)
1192             *lineTopFrame = 0;
1193       }
1194       return count;
1195    }
1196
1197    void ChangeWatch(DataRow row, char * expression)
1198    {
1199       Watch wh = (Watch)row.tag;
1200       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ChangeWatch(", expression, ")");
1201       if(wh)
1202       {
1203          delete wh.expression;
1204          if(expression)
1205             wh.expression = CopyString(expression);
1206          else
1207          {
1208             Iterator<Watch> it { ide.workspace.watches };
1209             if(it.Find(wh))
1210                ide.workspace.watches.Delete(it.pointer);
1211          }
1212       }
1213       else if(expression)
1214       {
1215          wh = Watch { };
1216          row.tag = (int64)wh;
1217          ide.workspace.watches.Add(wh);
1218          wh.row = row;
1219          wh.expression = CopyString(expression);
1220       }
1221       ide.workspace.Save();
1222       //if(expression && state == stopped)
1223       if(expression)
1224          ResolveWatch(wh);
1225    }
1226
1227    void MoveIcons(char * fileName, int lineNumber, int move, bool start)
1228    {
1229       char winFilePath[MAX_LOCATION];
1230       char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1231
1232       Link bpLink, next;
1233       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::MoveIcons()");
1234       for(bpLink = ide.workspace.breakpoints.first; bpLink; bpLink = next)
1235       {
1236          Breakpoint bp = (Breakpoint)bpLink.data;
1237          next = bpLink.next;
1238
1239          if(bp.type == user && bp.absoluteFilePath && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1240          {
1241             if(bp.line > lineNumber || (bp.line == lineNumber && start))
1242             {
1243                if(move < 0 && (bp.line < lineNumber - move))
1244                   ide.workspace.RemoveBreakpoint(bp);
1245                else
1246                {
1247                   bp.line += move;
1248                   ide.breakpointsView.UpdateBreakpoint(bp.row);
1249                   ide.workspace.Save();
1250                }
1251             }
1252          }
1253       }
1254       
1255       // moving code cursors is futile, on next step, stop, hit, cursors will be offset anyways
1256    }
1257
1258    bool SourceDirDialog(char * title, char * startDir, char * test, char * sourceDir)
1259    {
1260       bool result;
1261       bool retry;
1262       String srcDir = null;
1263
1264       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SourceDirDialog()");
1265       debuggerFileDialog.text = title;
1266       debuggerFileDialog.currentDirectory = startDir;
1267       debuggerFileDialog.master = ide;
1268
1269       while(debuggerFileDialog.Modal())
1270       {
1271          strcpy(sourceDir, debuggerFileDialog.filePath);
1272          if(!fstrcmp(ide.workspace.projectDir, sourceDir) && 
1273                   MessageBox { type = yesNo, master = ide, 
1274                               contents = $"This is the project directory.\nWould you like to try again?", 
1275                               text = $"Invalid Source Directory" }.Modal() == no)
1276             return false;
1277          else
1278          {
1279             for(dir : ide.workspace.sourceDirs)
1280             {
1281                if(!fstrcmp(dir, sourceDir))
1282                {
1283                   srcDir = dir;
1284                   break;
1285                }
1286             }
1287             
1288             if(srcDir && 
1289                   MessageBox { type = yesNo, master = ide, 
1290                               contents = $"This source directory is already specified.\nWould you like to try again?", 
1291                               text = $"Invalid Source Directory" }.Modal() == no)
1292                return false;
1293             else
1294             {
1295                if(test)
1296                {
1297                   char file[MAX_LOCATION];
1298                   strcpy(file, sourceDir);
1299                   PathCat(file, test);
1300                   result = FileExists(file);
1301                   if(!result && 
1302                         MessageBox { type = yesNo, master = ide, 
1303                                     contents = $"Unable to locate source file.\nWould you like to try again?", 
1304                                     text = $"Invalid Source Directory" }.Modal() == no)
1305                         return false;
1306                }
1307                else
1308                   result = true;
1309                
1310                if(result)
1311                   return true;
1312             }
1313          }
1314       }
1315       return false;
1316    }
1317
1318    void AddSourceDir(char * sourceDir)
1319    {
1320       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::AddSourceDir(", sourceDir, ")");
1321       ide.workspace.sourceDirs.Add(CopyString(sourceDir));
1322       ide.workspace.Save();
1323       
1324       if(targeted)
1325       {
1326          DebuggerState oldState = state;
1327          switch(state)
1328          {
1329             case running:
1330                if(targetProcessId)
1331                   GdbDebugBreak(true);
1332             case stopped:
1333             case loaded:
1334                GdbCommand(false, "-environment-directory \"%s\"", sourceDir);
1335                break;
1336          }
1337          if(oldState == running)
1338             GdbExecContinue(false);
1339       }
1340    }
1341
1342    void ToggleBreakpoint(char * fileName, int lineNumber, Project prj)
1343    {
1344       char winFilePath[MAX_LOCATION];
1345       char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1346       char absolutePath[MAX_LOCATION];
1347       char relativePath[MAX_LOCATION];
1348       char sourceDir[MAX_LOCATION];
1349       Breakpoint bp = null;
1350
1351       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ToggleBreakpoint(", fileName, ":", lineNumber, ")");
1352       strcpy(absolutePath, absoluteFilePath);
1353       for(i : ide.workspace.breakpoints; i.type == user && i.absoluteFilePath && !fstrcmp(i.absoluteFilePath, absolutePath) && i.line == lineNumber)
1354       {
1355          bp = i;
1356          break;
1357       }
1358       if(bp)
1359       {
1360          if(bp.enabled)
1361          {
1362             ide.workspace.RemoveBreakpoint(bp);
1363             bp = null;
1364          }
1365          else
1366             bp.enabled = true;
1367       }
1368       else
1369       {
1370          // FIXED: This is how it should have been... Source locations are only for files not in project
1371          // if(IsPathInsideOf(absolutePath, ide.workspace.projectDir))
1372          //   MakePathRelative(absolutePath, ide.workspace.projectDir, relativePath);
1373          bool result = false;
1374          if(prj)
1375             result = prj.GetRelativePath(absolutePath, relativePath);
1376          else
1377             result = ide.projectView.project.GetRelativePath(absolutePath, relativePath);
1378          //if(ide.projectView.project.GetRelativePath(absolutePath, relativePath));
1379          //else
1380          if(!result)
1381          {
1382             char title[MAX_LOCATION];
1383             char directory[MAX_LOCATION];
1384             StripLastDirectory(absolutePath, directory);
1385             snprintf(title, sizeof(title), $"Provide source files location directory for %s", absolutePath);
1386             title[sizeof(title)-1] = 0;
1387             while(true)
1388             {
1389                String srcDir = null;
1390                for(dir : ide.workspace.sourceDirs)
1391                {
1392                   if(IsPathInsideOf(absolutePath, dir))
1393                   {
1394                      MakePathRelative(absoluteFilePath, dir, relativePath);
1395                      srcDir = dir;
1396                      break;
1397                   }
1398                }
1399                if(srcDir)
1400                   break;
1401                
1402                if(SourceDirDialog(title, directory, null, sourceDir))
1403                {
1404                   if(IsPathInsideOf(absolutePath, sourceDir))
1405                   {
1406                      AddSourceDir(sourceDir);
1407                      MakePathRelative(absoluteFilePath, sourceDir, relativePath);
1408                      break;
1409                   }
1410                   else if(MessageBox { type = yesNo, master = ide, 
1411                                  contents = $"You must provide a valid source directory in order to place a breakpoint in this file.\nWould you like to try again?", 
1412                                  text = $"Invalid Source Directory" }.Modal() == no)
1413                      return;
1414                }
1415                else if(MessageBox { type = yesNo, master = ide, 
1416                                  contents = $"You must provide a source directory in order to place a breakpoint in this file.\nWould you like to try again?", 
1417                                  text = $"No Source Directory Provided" }.Modal() == no)
1418                   return;
1419             }
1420          }
1421          ide.workspace.bpCount++;
1422          bp = { line = lineNumber, type = user, enabled = true, level = -1 };
1423          ide.workspace.breakpoints.Add(bp);
1424          bp.absoluteFilePath = absolutePath;
1425          bp.relativeFilePath = relativePath;
1426          ide.breakpointsView.AddBreakpoint(bp);
1427       }
1428
1429       if(bp && targeted)
1430       {
1431          DebuggerState oldState = state;
1432          switch(state)
1433          {
1434             case running:
1435                if(targetProcessId)
1436                   GdbDebugBreak(true);
1437             case stopped:
1438             case loaded:
1439                SetBreakpoint(bp, false);
1440                break;
1441          }
1442          if(oldState == running)
1443             GdbExecContinue(false);
1444       }
1445
1446       ide.workspace.Save();
1447    }
1448
1449    void UpdateRemovedBreakpoint(Breakpoint bp)
1450    {
1451       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::UpdateRemovedBreakpoint()");
1452       if(targeted && bp.inserted)
1453       {
1454          DebuggerState oldState = state;
1455          switch(state)
1456          {
1457             case running:
1458                if(targetProcessId)
1459                   GdbDebugBreak(true);
1460             case stopped:
1461             case loaded:
1462                UnsetBreakpoint(bp);
1463                break;
1464          }
1465          if(oldState == running)
1466             GdbExecContinue(false);
1467       }
1468    }
1469
1470    // PRIVATE MEMBERS
1471
1472    void ParseFrame(Frame frame, char * string)
1473    {
1474       int i, j, k;
1475       Array<char *> frameTokens { minAllocSize = 50 };
1476       Array<char *> argsTokens { minAllocSize = 50 };
1477       Array<char *> argumentTokens { minAllocSize = 50 };
1478       DebugListItem item { };
1479       Argument arg;
1480       
1481       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ParseFrame()");
1482       TokenizeList(string, ',', frameTokens);
1483       for(i = 0; i < frameTokens.count; i++)
1484       {
1485          if(TokenizeListItem(frameTokens[i], item))
1486          {
1487             StripQuotes(item.value, item.value);
1488             if(!strcmp(item.name, "level"))
1489                frame.level = atoi(item.value);
1490             else if(!strcmp(item.name, "addr"))
1491                frame.addr = item.value;
1492             else if(!strcmp(item.name, "func"))
1493                frame.func = item.value;
1494             else if(!strcmp(item.name, "args"))
1495             {
1496                if(!strcmp(item.value, "[]"))
1497                   frame.argsCount = 0;
1498                else
1499                {
1500                   item.value = StripBrackets(item.value);
1501                   TokenizeList(item.value, ',', argsTokens);
1502                   for(j = 0; j < argsTokens.count; j++)
1503                   {
1504                      argsTokens[j] = StripCurlies(argsTokens[j]);
1505                      TokenizeList(argsTokens[j], ',', argumentTokens);
1506                      for(k = 0; k < argumentTokens.count; k++)
1507                      {
1508                         arg = Argument { };
1509                         frame.args.Add(arg);
1510                         if(TokenizeListItem(argumentTokens[k], item))
1511                         {
1512                            if(!strcmp(item.name, "name"))
1513                            {
1514                               StripQuotes(item.value, item.value);
1515                               arg.name = item.value;
1516                            }
1517                            else if(!strcmp(item.name, "value"))
1518                            {
1519                               StripQuotes(item.value, item.value);
1520                               arg.val = item.value;
1521                            }
1522                            else
1523                               _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "frame args item (", item.name, "=", item.value, ") is unheard of");
1524                         }
1525                         else
1526                            _dpl(0, "Bad frame args item");
1527                      }
1528                      argumentTokens.RemoveAll();
1529                   }
1530                   frame.argsCount = argsTokens.count;
1531                   argsTokens.RemoveAll();
1532                }
1533             }
1534             else if(!strcmp(item.name, "from"))
1535                frame.from = item.value;
1536             else if(!strcmp(item.name, "file"))
1537                frame.file = item.value;
1538             else if(!strcmp(item.name, "line"))
1539                frame.line = atoi(item.value);
1540             else if(!strcmp(item.name, "fullname"))
1541                frame.absoluteFile = item.value;
1542             /*{
1543                // GDB 6.3 on OS X is giving "fullname" and "dir", all in absolute, but file name only in 'file'
1544                String path = ide.workspace.GetPathWorkspaceRelativeOrAbsolute(item.value);
1545                if(strcmp(frame.file, path))
1546                {
1547                   frame.file = path;
1548                   frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1549                }
1550                delete path;
1551             }*/
1552             else
1553                _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "frame member (", item.name, "=", item.value, ") is unheard of");
1554          }
1555          else
1556             _dpl(0, "Bad frame");
1557       }
1558       
1559       delete frameTokens;
1560       delete argsTokens;
1561       delete argumentTokens;
1562       delete item;
1563    }
1564
1565    Breakpoint GetBreakpointById(int id, bool * isInternal)
1566    {
1567       Breakpoint bp = null;
1568       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetBreakpointById(", id, ")");
1569       if(isInternal)
1570          *isInternal = false;
1571       if(id)
1572       {
1573          for(i : sysBPs; i.bp && i.bp.id == id)
1574          {
1575             if(isInternal)
1576                *isInternal = true;
1577             bp = i;
1578             break;
1579          }
1580          if(!bp && bpRunToCursor && bpRunToCursor.bp && bpRunToCursor.bp.id == id)
1581             bp = bpRunToCursor;
1582          if(!bp)
1583          {
1584             for(i : ide.workspace.breakpoints; i.bp && i.bp.id == id)
1585             {
1586                bp = i;
1587                break;
1588             }
1589          }
1590       }
1591       return bp;
1592    }
1593
1594    GdbDataBreakpoint ParseBreakpoint(char * string, Array<char *> outTokens)
1595    {
1596       int i;
1597       GdbDataBreakpoint bp { };
1598       DebugListItem item { };
1599       Array<char *> bpTokens { minAllocSize = 16 };
1600       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ParseBreakpoint()");
1601       string = StripCurlies(string);
1602       TokenizeList(string, ',', bpTokens);
1603       for(i = 0; i < bpTokens.count; i++)
1604       {
1605          if(TokenizeListItem(bpTokens[i], item))
1606          {
1607             StripQuotes(item.value, item.value);
1608             if(!strcmp(item.name, "number"))
1609             {
1610                if(!strchr(item.value, '.'))
1611                   bp.id = atoi(item.value);
1612                bp.number = item.value;
1613             }
1614             else if(!strcmp(item.name, "type"))
1615                bp.type = item.value;
1616             else if(!strcmp(item.name, "disp"))
1617                bp.disp = item.value;
1618             else if(!strcmp(item.name, "enabled"))
1619                bp.enabled = (!strcmpi(item.value, "y"));
1620             else if(!strcmp(item.name, "addr"))
1621             {
1622                if(outTokens && !strcmp(item.value, "<MULTIPLE>"))
1623                {
1624                   int c = 1;
1625                   Array<GdbDataBreakpoint> bpArray = bp.multipleBPs = { };
1626                   while(outTokens.count > ++c)
1627                   {
1628                      GdbDataBreakpoint multBp = ParseBreakpoint(outTokens[c], null);
1629                      bpArray.Add(multBp);
1630                   }
1631                }
1632                else
1633                   bp.addr = item.value;
1634             }
1635             else if(!strcmp(item.name, "func"))
1636                bp.func = item.value;
1637             else if(!strcmp(item.name, "file"))
1638                bp.file = item.value;
1639             else if(!strcmp(item.name, "fullname"))
1640                bp.fullname = item.value;
1641             else if(!strcmp(item.name, "line"))
1642                bp.line = atoi(item.value);
1643             else if(!strcmp(item.name, "at"))
1644                bp.at = item.value;
1645             else if(!strcmp(item.name, "times"))
1646                bp.times = atoi(item.value);
1647             else if(!strcmp(item.name, "original-location") || !strcmp(item.name, "thread-groups"))
1648                _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "breakpoint member (", item.name, "=", item.value, ") is ignored");
1649             else
1650                _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "breakpoint member (", item.name, "=", item.value, ") is unheard of");
1651          }
1652       }
1653       return bp;
1654    }
1655
1656    void ShowDebuggerViews()
1657    {
1658       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ShowDebuggerViews()");
1659       ide.outputView.Show();
1660       ide.outputView.SelectTab(debug);
1661       ide.threadsView.Show();
1662       ide.callStackView.Show();
1663       ide.watchesView.Show();
1664       ide.Update(null);
1665    }
1666
1667    void HideDebuggerViews()
1668    {
1669       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::HideDebuggerViews()");
1670       ide.RepositionWindows(true);
1671    }
1672
1673    void ::GdbCommand(bool focus, char * format, ...)
1674    {
1675       if(gdbHandle)
1676       {
1677          // TODO: Improve this limit
1678          static char string[MAX_F_STRING*4];
1679          va_list args;
1680          va_start(args, format);
1681          vsnprintf(string, sizeof(string), format, args);
1682          string[sizeof(string)-1] = 0;
1683          va_end(args);
1684          
1685          gdbReady = false;
1686          ide.debugger.serialSemaphore.TryWait();
1687
1688 #ifdef GDB_DEBUG_CONSOLE
1689          _dpl2(_dpct, dplchan::gdbCommand, 0, string);
1690 #endif
1691 #ifdef GDB_DEBUG_OUTPUT
1692          ide.outputView.gdbBox.Logf("cmd: %s\n", string);
1693 #endif
1694 #ifdef GDB_DEBUG_GUI
1695          if(ide.gdbDialog)
1696             ide.gdbDialog.AddCommand(string);
1697 #endif
1698
1699          strcat(string,"\n");
1700          gdbHandle.Puts(string);
1701
1702          if(focus)
1703             Process_ShowWindows(targetProcessId);
1704
1705          app.Unlock();
1706          ide.debugger.serialSemaphore.Wait();
1707          app.Lock();
1708       } 
1709    }
1710
1711    bool ValidateBreakpoint(Breakpoint bp)
1712    {
1713       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ValidateBreakpoint()");
1714       if(modules && bp.line && bp.bp)
1715       {
1716          if(bp.bp.line != bp.line)
1717          {
1718             if(!bp.bp.line)
1719             {
1720 #ifdef _DEBUG
1721                //here
1722                ide.outputView.debugBox.Logf("WOULD HAVE -- Invalid breakpoint disabled: %s:%d\n", bp.relativeFilePath, bp.line);
1723 #endif
1724                //UnsetBreakpoint(bp);
1725                //bp.enabled = false;
1726                return false;
1727             }
1728             else
1729             {
1730                //here
1731                ide.outputView.debugBox.Logf("Debugger Error: ValidateBreakpoint error\n");
1732                bp.line = bp.bp.line;
1733             }
1734          }
1735       }
1736       return true;
1737    }
1738
1739    void GdbBreakpointsInsert()
1740    {
1741       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbBreakpointsInsert()");
1742       if(symbols)
1743       {
1744          DirExpression objDir = ide.project.GetObjDir(currentCompiler, prjConfig, bitDepth);
1745          for(bp : sysBPs; !bp.inserted)
1746          {
1747             bool insert = false;
1748             if(bp.type == internalModulesLoaded)
1749             {
1750                char path[MAX_LOCATION];
1751                char name[MAX_LOCATION];
1752                char fixedModuleName[MAX_FILENAME];
1753                char line[16384];
1754                int lineNumber;
1755                bool moduleLoadBlock = false;
1756                File f;
1757                ReplaceSpaces(fixedModuleName, ide.project.moduleName);
1758                snprintf(name, sizeof(name),"%s.main.ec", fixedModuleName);
1759                name[sizeof(name)-1] = 0;
1760                strcpy(path, ide.workspace.projectDir);
1761                PathCatSlash(path, objDir.dir);
1762                PathCatSlash(path, name);
1763                f = FileOpen(path, read);
1764                if(f)
1765                {
1766                   for(lineNumber = 1; !f.Eof(); lineNumber++)
1767                   {
1768                      if(f.GetLine(line, sizeof(line) - 1))
1769                      {
1770                         bool moduleLoadLine;
1771                         TrimLSpaces(line, line);
1772                         moduleLoadLine = !strncmp(line, "eModule_Load", strlen("eModule_Load"));
1773                         if(!moduleLoadBlock && moduleLoadLine)
1774                            moduleLoadBlock = true;
1775                         else if(moduleLoadBlock && !moduleLoadLine && strlen(line) > 0)
1776                            break;
1777                      }
1778                   }
1779                   if(!f.Eof())
1780                   {
1781                      char relative[MAX_LOCATION];
1782                      bp.absoluteFilePath = path;
1783                      MakePathRelative(path, ide.workspace.projectDir, relative);
1784                      bp.relativeFilePath = relative;
1785                      bp.line = lineNumber;
1786                      insert = true;
1787                   }
1788                   delete f;
1789                }
1790             }
1791             else if(bp.type == internalModuleLoad)
1792             {
1793                if(modules)
1794                {
1795                   for(prj : ide.workspace.projects)
1796                   {
1797                      if(!strcmp(prj.moduleName, "ecere"))
1798                      {
1799                         ProjectNode node = prj.topNode.Find("instance.c", false);
1800                         if(node)
1801                         {
1802                            char path[MAX_LOCATION];
1803                            char relative[MAX_LOCATION];
1804                            node.GetFullFilePath(path);
1805                            bp.absoluteFilePath = path;
1806                            MakePathRelative(path, prj.topNode.path, relative);
1807                            bp.relativeFilePath = relative;
1808                            insert = true;
1809                            break;
1810                         }
1811                      }
1812                   }
1813                }
1814             }
1815             else
1816                insert = true;
1817             if(insert)
1818                SetBreakpoint(bp, false);
1819          }
1820          delete objDir;
1821
1822          if(bpRunToCursor && !bpRunToCursor.inserted)
1823             SetBreakpoint(bpRunToCursor, false);
1824
1825          if(!ignoreBreakpoints)
1826          {
1827             for(bp : ide.workspace.breakpoints; !bp.inserted && bp.type == user)
1828             {
1829                if(bp.enabled)
1830                {
1831                   if(!SetBreakpoint(bp, false))
1832                      SetBreakpoint(bp, true);
1833                }
1834                else
1835                {
1836 #ifdef _DEBUG
1837                   if(bp.bp)
1838                      _dpl(0, "problem");
1839 #endif
1840                   bp.bp = GdbDataBreakpoint { };
1841                }
1842             }
1843          }
1844       }
1845    }
1846
1847    void UnsetBreakpoint(Breakpoint bp)
1848    {
1849       char * s; _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::UnsetBreakpoint(", s=bp.CopyLocationString(false), ")"); delete s;
1850       if(symbols && bp.inserted)
1851       {
1852          GdbCommand(false, "-break-delete %s", bp.bp.number);
1853          bp.inserted = false;
1854          bp.bp = { };
1855       }
1856    }
1857
1858    bool SetBreakpoint(Breakpoint bp, bool removePath)
1859    {
1860       char * s; _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SetBreakpoint(", s=bp.CopyLocationString(false), ", ", removePath ? "**** removePath(true) ****" : "", ")"); delete s;
1861       breakpointError = false;
1862       if(symbols)
1863       {
1864          char * location = bp.CopyLocationString(removePath);
1865          sentBreakInsert = true;
1866          GdbCommand(false, "-break-insert %s", location);
1867          delete location;
1868          if(!breakpointError)
1869          {
1870             if(bpItem.multipleBPs && bpItem.multipleBPs.count)
1871             {
1872                int count = 0;
1873                GdbDataBreakpoint first = null;
1874                for(n : bpItem.multipleBPs)
1875                {
1876                   if(!fstrcmp(n.fullname, bp.absoluteFilePath))
1877                   {
1878                      count++;
1879                      if(!first)
1880                         first = n;
1881                   }
1882                   else
1883                   {
1884                      if(n.enabled)
1885                      {
1886                         GdbCommand(false, "-break-disable %s", n.number);
1887                         n.enabled = false;
1888                      }
1889                      else
1890                         _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error breakpoint already disabled.");
1891                   }
1892                }
1893                if(first)
1894                {
1895                   bpItem.addr = first.addr;
1896                   bpItem.func = first.func;
1897                   bpItem.file = first.file;
1898                   bpItem.fullname = first.fullname;
1899                   bpItem.line = first.line;
1900                   //bpItem.thread-groups = first.thread-groups;
1901                   bpItem.multipleBPs.Free();
1902                   delete bpItem.multipleBPs;
1903                }
1904                else if(count == 0)
1905                   _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error multiple breakpoints all disabled.");
1906                else
1907                   _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error multiple breakpoints in exact same file not supported.");
1908             }
1909             bp.bp = bpItem;
1910             bpItem = null;
1911             bp.inserted = (bp.bp && bp.bp.number && strcmp(bp.bp.number, "0"));
1912             if(bp.inserted)
1913                ValidateBreakpoint(bp);
1914             /*if(bp == bpRunToCursor)
1915                runToCursorDebugStart = false;*/
1916          }
1917       }
1918       return !breakpointError;
1919    }
1920
1921    void GdbBreakpointsDelete(bool deleteRunToCursor, bool deleteInternalBreakpoints)
1922    {
1923       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbBreakpointsDelete(deleteRunToCursor(", deleteRunToCursor, "))");
1924       if(symbols)
1925       {
1926          if(deleteInternalBreakpoints)
1927          {
1928             for(bp : sysBPs)
1929                UnsetBreakpoint(bp);
1930          }
1931          for(bp : ide.workspace.breakpoints)
1932             UnsetBreakpoint(bp);
1933          if(deleteRunToCursor && bpRunToCursor)
1934             UnsetBreakpoint(bpRunToCursor);
1935       }
1936    }
1937
1938    void GdbGetStack()
1939    {
1940       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbGetStack()");
1941       activeFrame = null;
1942       stackFrames.Free(Frame::Free);
1943       GdbCommand(false, "-stack-info-depth");
1944       if(!frameCount)
1945          GdbCommand(false, "-stack-info-depth 192");
1946       if(frameCount && frameCount <= 192)
1947          GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 191));
1948       else
1949       {
1950          GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 95));
1951          GdbCommand(false, "-stack-list-frames %d %d", Max(frameCount - 96, 96), frameCount - 1);
1952       }
1953       GdbCommand(false, "");
1954    }
1955
1956    bool GdbTargetSet()
1957    {
1958       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbTargetSet()");
1959       if(!targeted)
1960       {
1961          char escaped[MAX_LOCATION];
1962          strescpy(escaped, targetFile);
1963          GdbCommand(false, "file \"%s\"", escaped);  //GDB/MI Missing Implementation -symbol-file, -target-attach
1964
1965          if(!symbols)
1966             return true;
1967
1968          if(usingValgrind)
1969          {
1970             const char *vgdbCommand = "/usr/bin/vgdb"; // TODO: vgdb command config option
1971             //GdbCommand(false, "-target-select remote | %s --pid=%d", "vgdb", targetProcessId);
1972             printf("target remote | %s --pid=%d\n", vgdbCommand, targetProcessId);
1973             GdbCommand(false, "target remote | %s --pid=%d", vgdbCommand, targetProcessId); // TODO: vgdb command config option
1974          }
1975
1976          /*for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
1977             GdbCommand(false, "-environment-directory \"%s\"", prj.topNode.path);*/
1978
1979          for(dir : ide.workspace.sourceDirs; dir && dir[0])
1980          {
1981            bool interference = false;
1982            for(prj : ide.workspace.projects)
1983            {
1984               if(!fstrcmp(prj.topNode.path, dir))
1985               {
1986                  interference = true;
1987                  break;
1988               }
1989            }
1990            if(!interference && dir[0])
1991               GdbCommand(false, "-environment-directory \"%s\"", dir);
1992          }
1993
1994          targeted = true;
1995       }
1996       return true;
1997    }
1998
1999    /*void GdbTargetRelease()
2000    {
2001       if(targeted)
2002       {
2003          GdbBreakpointsDelete(true, true);
2004          GdbCommand(false, "file");  //GDB/MI Missing Implementation -target-detach
2005          targeted = false;
2006          symbols = true;
2007       }
2008    }*/
2009
2010    void GdbDebugBreak(bool internal)
2011    {
2012       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbDebugBreak()");
2013       if(targetProcessId)
2014       {
2015          if(internal)
2016             breakType = DebuggerAction::internal;
2017
2018          if(ide) ide.Update(null);
2019          app.Unlock();
2020          if(Process_Break(targetProcessId))  //GdbCommand(false, "-exec-interrupt");
2021             serialSemaphore.Wait();
2022          else
2023          {
2024             ChangeState(loaded);
2025             targetProcessId = 0;
2026          }
2027          app.Lock();
2028       }
2029       else
2030          ide.outputView.debugBox.Logf("Debugger Error: GdbDebugBreak with not target id should never happen\n");
2031    }
2032
2033    void GdbExecRun()
2034    {
2035       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecRun()");
2036       GdbTargetSet();
2037       GdbExecCommon();
2038       ShowDebuggerViews();
2039       if(usingValgrind)
2040          GdbCommand(true, "-exec-continue");
2041       else
2042          GdbCommand(true, "-exec-run");
2043    }
2044
2045    void GdbExecContinue(bool focus)
2046    {
2047       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecContinue()");
2048       GdbExecCommon();
2049       GdbCommand(focus, "-exec-continue");
2050    }
2051
2052    void GdbExecNext()
2053    {
2054       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecNext()");
2055       GdbExecCommon();
2056       GdbCommand(true, "-exec-next");
2057    }
2058
2059    void GdbExecStep()
2060    {
2061       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecStep()");
2062       GdbExecCommon();
2063       GdbCommand(true, "-exec-step");
2064    }
2065
2066    void GdbExecFinish()
2067    {
2068       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecFinish()");
2069       GdbExecCommon();
2070       GdbCommand(true, "-exec-finish");
2071    }
2072
2073    void GdbExecCommon()
2074    {
2075       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecCommon()");
2076       ClearBreakDisplay();
2077       GdbBreakpointsInsert();
2078    }
2079
2080 #ifdef GDB_DEBUG_GUI
2081    void SendGDBCommand(char * command)
2082    {
2083       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SendGDBCommand()");
2084       DebuggerState oldState = state;
2085       switch(state)
2086       {
2087          case running:
2088             if(targetProcessId)
2089                GdbDebugBreak(true);
2090          case stopped:
2091          case loaded:
2092             GdbCommand(false, command);
2093             break;
2094       }
2095       if(oldState == running)
2096          GdbExecContinue(false);
2097    }
2098 #endif
2099
2100    void ClearBreakDisplay()
2101    {
2102       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ClearBreakDisplay()");
2103       activeThread = 0;
2104       activeFrameLevel = -1;
2105       hitThread = 0;
2106       signalThread = 0;
2107       signalOn = false;
2108       frameCount = 0;
2109       if(stopItem)
2110          stopItem.Free();
2111       delete stopItem;
2112       event = none;
2113       activeFrame = null;
2114       stackFrames.Free(Frame::Free);
2115       WatchesCodeEditorLinkRelease();
2116       ide.callStackView.Clear();
2117       ide.threadsView.Clear();
2118       ide.Update(null);
2119    }
2120
2121    bool GdbAbortExec()
2122    {
2123       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbAbortExec()");
2124       sentKill = true;
2125       GdbCommand(false, "-interpreter-exec console \"kill\""); // should use -exec-abort -- GDB/MI implementation incomplete
2126       return true;
2127    }
2128
2129    bool GdbInit(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
2130    {
2131       bool result = true;
2132       char oldDirectory[MAX_LOCATION];
2133       char tempPath[MAX_LOCATION];
2134       char command[MAX_F_STRING*4];
2135       Project project = ide.project;
2136       DirExpression targetDirExp = project.GetTargetDir(compiler, config, bitDepth);
2137       PathBackup pathBackup { };
2138
2139       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbInit()");
2140       if(currentCompiler != compiler)
2141       {
2142          delete currentCompiler;
2143          currentCompiler = compiler;
2144          incref currentCompiler;
2145       }
2146       prjConfig = config;
2147       this.bitDepth = bitDepth;
2148       usingValgrind = useValgrind;
2149
2150       ChangeState(loaded);
2151       sentKill = false;
2152       sentBreakInsert = false;
2153       breakpointError = false;
2154       ignoreBreakpoints = false;
2155       symbols = true;
2156       targeted = false;
2157       modules = false;
2158       needReset = false;
2159       
2160       ide.outputView.ShowClearSelectTab(debug);
2161       ide.outputView.debugBox.Logf($"Starting debug mode\n");
2162
2163 #ifdef GDB_DEBUG_OUTPUT
2164       ide.outputView.gdbBox.Logf("run: Starting GDB\n");
2165 #endif
2166
2167       strcpy(tempPath, ide.workspace.projectDir);
2168       PathCatSlash(tempPath, targetDirExp.dir);
2169       delete targetDir;
2170       targetDir = CopyString(tempPath);
2171       project.CatTargetFileName(tempPath, compiler, config);
2172       delete targetFile;
2173       targetFile = CopyString(tempPath);
2174
2175       GetWorkingDir(oldDirectory, MAX_LOCATION);
2176       if(ide.workspace.debugDir && ide.workspace.debugDir[0])
2177       {
2178          char temp[MAX_LOCATION];
2179          strcpy(temp, ide.workspace.projectDir);
2180          PathCatSlash(temp, ide.workspace.debugDir);
2181          ChangeWorkingDir(temp);
2182       }
2183       else
2184          ChangeWorkingDir(ide.workspace.projectDir);
2185       
2186       ide.SetPath(true, compiler, config, bitDepth);
2187
2188       // TODO: This pollutes the environment, but at least it works
2189       // It shouldn't really affect the IDE as the PATH gets restored and other variables set for testing will unlikely cause problems
2190       // What is the proper solution for this? DualPipeOpenEnv?
2191       // gdb set environment commands don't seem to take effect
2192       for(e : ide.workspace.environmentVars)
2193       {
2194          SetEnvironment(e.name, e.string);
2195       }
2196
2197       if(usingValgrind)
2198       {
2199          char * clArgs = ide.workspace.commandLineArgs;
2200          const char *valgrindCommand = "valgrind"; // TODO: valgrind command config option //TODO: valgrind options
2201          ValgrindLeakCheck vgLeakCheck = ide.workspace.vgLeakCheck;
2202          int vgRedzoneSize = ide.workspace.vgRedzoneSize;
2203          bool vgTrackOrigins = ide.workspace.vgTrackOrigins;
2204          vgLogFile = CreateTemporaryFile(vgLogPath, "ecereidevglog");
2205          if(vgLogFile)
2206          {
2207             incref vgLogFile;
2208             vgLogThread.Create();
2209          }
2210          else
2211          {
2212             ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't open temporary log file for Valgrind output\n");
2213             result = false;
2214          }
2215          if(result && !CheckCommandAvailable(valgrindCommand))
2216          {
2217             ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for Valgrind is not available.\n", valgrindCommand);
2218             result = false;
2219          }
2220          if(result)
2221          {
2222             sprintf(command, "%s --vgdb=yes --vgdb-error=0 --log-file=%s --leak-check=%s --redzone-size=%d --track-origins=%s %s%s%s",
2223                   valgrindCommand, vgLogPath, (char*)vgLeakCheck, vgRedzoneSize, vgTrackOrigins ? "yes" : "no", targetFile, clArgs ? " " : "", clArgs ? clArgs : "");
2224             vgTargetHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
2225             if(!vgTargetHandle)
2226             {
2227                ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start Valgrind\n");
2228                result = false;
2229             }
2230          }
2231          if(result)
2232          {
2233             incref vgTargetHandle;
2234             vgTargetThread.Create();
2235
2236             targetProcessId = vgTargetHandle.GetProcessID();
2237             waitingForPID = false;
2238             if(!targetProcessId)
2239             {
2240                ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get Valgrind process ID\n");
2241                result = false;
2242             }
2243          }
2244          if(result)
2245          {
2246             app.Unlock();
2247             serialSemaphore.Wait();
2248             app.Lock();
2249          }
2250       }
2251
2252       if(result)
2253       {
2254          strcpy(command,
2255             (compiler.targetPlatform == win32 && bitDepth == 64) ? "x86_64-w64-mingw32-gdb" :
2256             (compiler.targetPlatform == win32 && bitDepth == 32) ? "i686-w64-mingw32-gdb" :
2257             "gdb");
2258          if(!CheckCommandAvailable(command))
2259          {
2260             ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for GDB is not available.\n", command);
2261             result = false;
2262          }
2263          else
2264          {
2265             strcat(command, " -n -silent --interpreter=mi2"); //-async //\"%s\"
2266             gdbTimer.Start();
2267             gdbHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
2268             if(!gdbHandle)
2269             {
2270                ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start GDB\n");
2271                result = false;
2272             }
2273          }
2274       }
2275       if(result)
2276       {
2277          incref gdbHandle;
2278          gdbThread.Create();
2279
2280          gdbProcessId = gdbHandle.GetProcessID();
2281          if(!gdbProcessId)
2282          {
2283             ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get GDB process ID\n");
2284             result = false;
2285          }
2286       }
2287       if(result)
2288       {
2289          app.Unlock();
2290          serialSemaphore.Wait();
2291          app.Lock();
2292
2293          GdbCommand(false, "-gdb-set verbose off");
2294          //GdbCommand(false, "-gdb-set exec-done-display on");
2295          GdbCommand(false, "-gdb-set step-mode off");
2296          GdbCommand(false, "-gdb-set unwindonsignal on");
2297          //GdbCommand(false, "-gdb-set shell on");
2298          GdbCommand(false, "set print elements 992");
2299          GdbCommand(false, "-gdb-set backtrace limit 100000");
2300
2301          if(!GdbTargetSet())
2302          {
2303             //ChangeState(terminated);
2304             result = false;
2305          }
2306       }
2307       if(result)
2308       {
2309 #if defined(__unix__)
2310          {
2311             CreateTemporaryDir(progFifoDir, "ecereide");
2312             strcpy(progFifoPath, progFifoDir);
2313             PathCat(progFifoPath, "ideprogfifo");
2314             if(!mkfifo(progFifoPath, 0600))
2315             {
2316                //fileCreated = true;
2317             }
2318             else
2319             {
2320                //app.Lock();
2321                ide.outputView.debugBox.Logf(createFIFOMsg, progFifoPath);
2322                //app.Unlock();
2323             }
2324          }
2325
2326          if(!usingValgrind)
2327          {
2328             progThread.terminate = false;
2329             progThread.Create();
2330          }
2331 #endif
2332
2333 #if defined(__WIN32__)
2334          GdbCommand(false, "-gdb-set new-console on");
2335 #endif
2336
2337 #if defined(__unix__)
2338          if(!usingValgrind)
2339             GdbCommand(false, "-inferior-tty-set %s", progFifoPath);
2340 #endif
2341
2342          if(!usingValgrind)
2343             GdbCommand(false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
2344          /*
2345          for(e : ide.workspace.environmentVars)
2346          {
2347             GdbCommand(false, "set environment %s=%s", e.name, e.string);
2348          }
2349          */
2350       }
2351
2352       ChangeWorkingDir(oldDirectory);
2353
2354       delete pathBackup;
2355
2356       if(!result)
2357          GdbExit();
2358       delete targetDirExp;
2359       return result;
2360    }
2361
2362    void GdbExit()
2363    {
2364       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExit()");
2365       if(gdbHandle && gdbProcessId)
2366       {
2367          GdbCommand(false, "-gdb-exit");
2368
2369          if(gdbThread)
2370          {
2371             app.Unlock();
2372             gdbThread.Wait();
2373             app.Lock();
2374          }
2375          if(gdbHandle)
2376          {
2377             gdbHandle.Wait();
2378             delete gdbHandle;
2379          }
2380       }
2381       gdbTimer.Stop();
2382       ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
2383       prjConfig = null;
2384       needReset = false;
2385
2386       if(ide.workspace)
2387       {
2388          for(bp : ide.workspace.breakpoints)
2389          {
2390             bp.inserted = false;
2391             delete bp.bp;
2392          }
2393       }
2394       for(bp : sysBPs)
2395       {
2396          bp.inserted = false;
2397          delete bp.bp;
2398       }
2399       if(bpRunToCursor)
2400       {
2401          bpRunToCursor.inserted = false;
2402          delete bpRunToCursor.bp;
2403       }
2404       
2405       ide.outputView.debugBox.Logf($"Debugging stopped\n");
2406       ClearBreakDisplay();
2407       ide.Update(null);
2408
2409 #if defined(__unix__)
2410       if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
2411       {
2412          progThread.terminate = true;
2413          if(fifoFile)
2414          {
2415             fifoFile.CloseInput();
2416             app.Unlock();
2417             progThread.Wait();
2418             app.Lock();
2419             delete fifoFile;
2420          }         
2421          DeleteFile(progFifoPath);
2422          progFifoPath[0] = '\0';
2423          rmdir(progFifoDir);
2424       }
2425 #endif
2426    }
2427
2428    void WatchesCodeEditorLinkInit()
2429    {
2430       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkInit()");
2431       /*
2432       char tempPath[MAX_LOCATION];
2433       char path[MAX_LOCATION];
2434       
2435       //void MakeFilePathProjectRelative(char * path, char * relativePath)
2436       if(!ide.projectView.project.GetRelativePath(activeFrame.file, tempPath))
2437          strcpy(tempPath, activeFrame.file);
2438       
2439       strcpy(path, ide.workspace.projectDir);
2440       PathCat(path, tempPath);
2441       codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2442       if(!codeEditor)
2443       {
2444          for(srcDir : ide.workspace.sourceDirs)
2445          {
2446             strcpy(path, srcDir);
2447             PathCat(path, tempPath);
2448             codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2449             if(codeEditor) break;
2450          }
2451       }
2452       */
2453
2454       /*if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2455          activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);*/
2456       if(!activeFrame || !activeFrame.absoluteFile)
2457          codeEditor = null;
2458       else
2459          codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal, false);
2460       if(codeEditor)
2461       {
2462          codeEditor.inUseDebug = true;
2463          incref codeEditor;
2464       }
2465       //watchesInit = true;
2466    }
2467
2468    void WatchesCodeEditorLinkRelease()
2469    {
2470       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkRelease()");
2471       //if(watchesInit)
2472       {
2473          if(codeEditor)
2474          {
2475             codeEditor.inUseDebug = false;
2476             if(!codeEditor.visible)
2477                codeEditor.Destroy(0);
2478             delete codeEditor;
2479          }
2480       }
2481    }
2482
2483    bool ResolveWatch(Watch wh)
2484    {
2485       bool result = false;
2486       
2487       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ResolveWatch()");
2488       wh.Reset();
2489
2490       /*delete wh.value;
2491       if(wh.type) 
2492       {
2493          FreeType(wh.type);
2494          wh.type = null;
2495       }*/
2496
2497       if(wh.expression)
2498       {
2499          char watchmsg[MAX_F_STRING];
2500          if(state == stopped && !codeEditor)
2501             wh.value = CopyString($"No source file found for selected frame");
2502          //if(codeEditor && state == stopped || state != stopped)
2503          else
2504          {
2505             Module backupPrivateModule;
2506             Context backupContext;
2507             Class backupThisClass;
2508             Expression exp;
2509             parseError = false;
2510
2511             backupPrivateModule = GetPrivateModule();
2512             backupContext = GetCurrentContext();
2513             backupThisClass = GetThisClass();
2514             if(codeEditor)
2515             {
2516                SetPrivateModule(codeEditor.privateModule);
2517                SetCurrentContext(codeEditor.globalContext);
2518                SetTopContext(codeEditor.globalContext);
2519                SetGlobalContext(codeEditor.globalContext);
2520                SetGlobalData(&codeEditor.globalData);
2521             }
2522          
2523             exp = ParseExpressionString(wh.expression);
2524             
2525             if(exp && !parseError)
2526             {
2527                char expString[4096];
2528                expString[0] = 0;
2529                PrintExpression(exp, expString);
2530
2531                if(GetPrivateModule())
2532                {
2533                   if(codeEditor)
2534                      DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2535                   ProcessExpressionType(exp);
2536                }
2537                wh.type = exp.expType;
2538                if(wh.type)
2539                   wh.type.refCount++;
2540                DebugComputeExpression(exp);
2541                if(ExpressionIsError(exp))
2542                {
2543                   GDBFallBack(exp, expString);
2544                }
2545
2546                /*if(exp.hasAddress)
2547                {
2548                   char temp[MAX_F_STRING];
2549                   sprintf(temp, "0x%x", exp.address);
2550                   wh.address = CopyString(temp);
2551                   // wh.address = CopyStringf("0x%x", exp.address);
2552                }*/
2553 /*
2554 //#ifdef _DEBUG
2555                {
2556                   Type dataType = exp.expType;
2557                   if(dataType)
2558                   {
2559                      char temp[MAX_F_STRING];
2560                      switch(dataType.kind)
2561                      {
2562                         case charType:
2563                            sprintf(temp, "%i", exp.val.c);
2564                            break;
2565                         case shortType:
2566                            sprintf(temp, "%i", exp.val.s);
2567                            break;
2568                         case intType:
2569                         case longType:
2570                         case enumType:
2571                            sprintf(temp, "%i", exp.val.i);
2572                            break;
2573                         case int64Type:
2574                            sprintf(temp, "%i", exp.val.i64);
2575                            break;
2576                         case pointerType:
2577                            sprintf(temp, "%i", exp.val.p);
2578                            break;
2579
2580                         case floatType:
2581                         {
2582                            long v = (long)exp.val.f;
2583                            sprintf(temp, "%i", v);
2584                            break;
2585                         } 
2586                         case doubleType:
2587                         {
2588                            long v = (long)exp.val.d;
2589                            sprintf(temp, "%i", v);
2590                            break;
2591                         }
2592                      }
2593                      if(temp)
2594                         wh.intVal = CopyString(temp);
2595                      switch(dataType.kind)
2596                      {
2597                         case charType:
2598                            sprintf(temp, "0x%x", exp.val.c);
2599                            break;
2600                         case shortType:
2601                            sprintf(temp, "0x%x", exp.val.s);
2602                            break;
2603                         case enumType:
2604                         case intType:
2605                            sprintf(temp, "0x%x", exp.val.i);
2606                            break;
2607                         case int64Type:
2608                            sprintf(temp, "0x%x", exp.val.i64);
2609                            break;
2610                         case longType:
2611                            sprintf(temp, "0x%x", exp.val.i64);
2612                            break;
2613                         case pointerType:
2614                            sprintf(temp, "0x%x", exp.val.p);
2615                            break;
2616
2617                         case floatType:
2618                         {
2619                            long v = (long)exp.val.f;
2620                            sprintf(temp, "0x%x", v);
2621                            break;
2622                         } 
2623                         case doubleType:
2624                         {
2625                            long v = (long)exp.val.d;
2626                            sprintf(temp, "0x%x", v);
2627                            break;
2628                         }
2629                      }
2630                      if(temp)
2631                         wh.hexVal = CopyString(temp);
2632                      switch(dataType.kind)
2633                      {
2634                         case charType:
2635                            sprintf(temp, "0o%o", exp.val.c);
2636                            break;
2637                         case shortType:
2638                            sprintf(temp, "0o%o", exp.val.s);
2639                            break;
2640                         case enumType:
2641                         case intType:
2642                            sprintf(temp, "0o%o", exp.val.i);
2643                            break;
2644                         case int64Type:
2645                            sprintf(temp, "0o%o", exp.val.i64);
2646                            break;
2647                         case longType:
2648                            sprintf(temp, "0o%o", exp.val.i64);
2649                            break;
2650                         case pointerType:
2651                            sprintf(temp, "0o%o", exp.val.p);
2652                            break;
2653
2654                         case floatType:
2655                         {
2656                            long v = (long)exp.val.f;
2657                            sprintf(temp, "0o%o", v);
2658                            break;
2659                         } 
2660                         case doubleType:
2661                         {
2662                            long v = (long)exp.val.d;
2663                            sprintf(temp, "0o%o", v);
2664                            break;
2665                         }
2666                      }
2667                      if(temp)
2668                         wh.octVal = CopyString(temp);
2669                   }
2670                }
2671                // WHATS THIS HERE ?
2672                if(exp.type == constantExp && exp.constant)
2673                   wh.constant = CopyString(exp.constant);
2674 //#endif
2675 */
2676
2677                switch(exp.type)
2678                {
2679                   case symbolErrorExp:
2680                      snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2681                      break;
2682                   case structMemberSymbolErrorExp:
2683                      // todo get info as in next case (ExpClassMemberSymbolError)
2684                      snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2685                      break;
2686                   case classMemberSymbolErrorExp:
2687                      {
2688                         Class _class;
2689                         Expression memberExp = exp.member.exp;
2690                         Identifier memberID = exp.member.member;
2691                         Type type = memberExp.expType;
2692                         if(type)
2693                         {
2694                            _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2695                            if(!_class)
2696                            {
2697                               char string[256] = "";
2698                               Symbol classSym;
2699                               PrintTypeNoConst(type, string, false, true);
2700                               classSym = FindClass(string);
2701                               _class = classSym ? classSym.registered : null;
2702                            }
2703                            if(_class)
2704                               snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2705                            else
2706                               snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2707                         }
2708                         else
2709                            snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2710                      }
2711                      break;
2712                   case memoryErrorExp:
2713                      // Need to ensure when set to memoryErrorExp, constant is set
2714                      snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2715                      break;
2716                   case dereferenceErrorExp:
2717                      snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2718                      break;
2719                   case unknownErrorExp:
2720                      snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2721                      break;
2722                   case noDebuggerErrorExp:
2723                      snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2724                      break;
2725                   case debugStateErrorExp:
2726                      snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2727                      break;
2728                   case 0:
2729                      snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2730                      break;
2731                   case constantExp:
2732                   case stringExp:
2733                      // Temporary Code for displaying Strings
2734                      if((exp.expType && ((exp.expType.kind == pointerType || 
2735                               exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) || 
2736                            (wh.type && wh.type.kind == classType && wh.type._class && 
2737                               wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2738                               !strcmp(wh.type._class.registered.name, "String")))
2739                      {
2740
2741                         if(exp.expType.kind != arrayType || exp.hasAddress)
2742                         {
2743                            uint64 address;
2744                            char * string;
2745                            char value[4196];
2746                            int len;
2747                            //char temp[MAX_F_STRING * 32];
2748
2749                            ExpressionType evalError = dummyExp;
2750                            /*if(exp.expType.kind == arrayType)
2751                               sprintf(temp, "(char*)0x%x", exp.address);
2752                            else
2753                               sprintf(temp, "(char*)%s", exp.constant);*/
2754
2755                            //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2756                            // address = strtoul(exp.constant, null, 0);
2757                            address = _strtoui64(exp.constant, null, 0);
2758                            //_dpl(0, "0x", address);
2759                            // snprintf(value, sizeof(value), "0x%08x ", address);
2760
2761                            if(address > 0xFFFFFFFFLL)
2762                               snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2763                            else
2764                               snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2765                            value[sizeof(value)-1] = 0;
2766                            
2767                            if(!address)
2768                               strcat(value, $"Null string");
2769                            else
2770                            {
2771                               int size = 4096;
2772                               len = strlen(value);
2773                               string = null;
2774                               while(!string && size > 2)
2775                               {
2776                                  string = GdbReadMemory(address, size);
2777                                  size /= 2;
2778                               }
2779                               if(string && string[0])
2780                               {
2781                                  value[len++] = '(';
2782                                  if(UTF8Validate(string))
2783                                  {
2784                                     int c;
2785                                     char ch;
2786                                     
2787                                     for(c = 0; (ch = string[c]) && c<4096; c++)
2788                                        value[len++] = ch;                                 
2789                                     value[len++] = ')';
2790                                     value[len++] = '\0';
2791                                     
2792                                  }
2793                                  else
2794                                  {
2795                                     ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2796                                     strcat(value, ") (ISO8859-1)");
2797                                  }
2798
2799                                  delete string;
2800                               }
2801                               else if(string)
2802                               {
2803                                  strcat(value, $"Empty string");
2804                                  delete string;
2805                               }
2806                               else
2807                                  strcat(value, $"Couldn't read memory");
2808                            }
2809                            wh.value = CopyString(value);
2810                         }
2811                      }
2812                      else if(wh.type && wh.type.kind == classType && wh.type._class && 
2813                               wh.type._class.registered && wh.type._class.registered.type == enumClass)
2814                      {
2815                         uint64 value = strtoul(exp.constant, null, 0);
2816                         Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2817                         EnumClassData enumeration = (EnumClassData)enumClass.data;
2818                         NamedLink item;
2819                         for(item = enumeration.values.first; item; item = item.next)
2820                            if((int)item.data == value)
2821                               break;
2822                         if(item)
2823                            wh.value = CopyString(item.name);
2824                         else
2825                            wh.value = CopyString($"Invalid Enum Value");
2826                         result = true;
2827                      }
2828                      else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class && 
2829                               wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2830                      {
2831                         unichar value;
2832                         int signedValue;
2833                         char charString[5];
2834                         char string[256];
2835
2836                         if(exp.constant[0] == '\'')
2837                         {
2838                            if((int)((byte *)exp.constant)[1] > 127)
2839                            {
2840                               int nb;
2841                               value = UTF8GetChar(exp.constant + 1, &nb);
2842                               if(nb < 2) value = exp.constant[1];
2843                               signedValue = value;
2844                            }
2845                            else
2846                            {
2847                               signedValue = exp.constant[1];
2848                               {
2849                                  // Precomp Syntax error with boot strap here:
2850                                  byte b = (byte)(char)signedValue;
2851                                  value = (unichar) b;
2852                               }
2853                            }
2854                         }
2855                         else
2856                         {
2857                            if(wh.type.kind == charType && wh.type.isSigned)
2858                            {
2859                               signedValue = (int)(char)strtol(exp.constant, null, 0);
2860                               {
2861                                  // Precomp Syntax error with boot strap here:
2862                                  byte b = (byte)(char)signedValue;
2863                                  value = (unichar) b;
2864                               }
2865                            }
2866                            else
2867                            {
2868                               value = (uint)strtoul(exp.constant, null, 0);
2869                               signedValue = (int)value;
2870                            }
2871                         }
2872                         charString[0] = 0;
2873                         UTF32toUTF8Len(&value, 1, charString, 5);
2874                         if(value == '\0')
2875                            snprintf(string, sizeof(string), "\'\\0' (0)");
2876                         else if(value == '\t')
2877                            snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2878                         else if(value == '\n')
2879                            snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2880                         else if(value == '\r')
2881                            snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2882                         else if(wh.type.kind == charType && wh.type.isSigned)
2883                            snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2884                         else if(value > 256 || wh.type.kind != charType)
2885                         {
2886                            if(value > 0x10FFFF || !GetCharCategory(value))
2887                               snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2888                            else
2889                               snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2890                         }
2891                         else
2892                            snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2893                         string[sizeof(string)-1] = 0;
2894                         
2895                         wh.value = CopyString(string);
2896                         result = true;
2897                      }
2898                      else
2899                      {
2900                         wh.value = CopyString(exp.constant);
2901                         result = true;
2902                      }
2903                      break;
2904                   default:
2905                      if(exp.hasAddress)
2906                      {
2907                         wh.value = PrintHexUInt64(exp.address);
2908                         result = true;
2909                      }
2910                      else
2911                      {
2912                         char tempString[256];
2913                         if(exp.member.memberType == propertyMember)
2914                            snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2915                         else
2916                            snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression, 
2917                                  exp.type.OnGetString(tempString, null, null));
2918                      }
2919                      break;
2920                }
2921             }
2922             else
2923                snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2924             if(exp) FreeExpression(exp);
2925
2926             
2927             SetPrivateModule(backupPrivateModule);
2928             SetCurrentContext(backupContext);
2929             SetTopContext(backupContext);
2930             SetGlobalContext(backupContext);
2931             SetThisClass(backupThisClass);
2932          }
2933          //else 
2934          //   wh.value = CopyString("No source file found for selected frame");
2935          
2936          watchmsg[sizeof(watchmsg)-1] = 0;
2937          if(!wh.value)
2938             wh.value = CopyString(watchmsg);
2939       }
2940       ide.watchesView.UpdateWatch(wh);
2941       return result;
2942    }
2943
2944    void EvaluateWatches()
2945    {
2946       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateWatches()");
2947       for(wh : ide.workspace.watches)
2948          ResolveWatch(wh);
2949    }
2950
2951    char * ::GdbEvaluateExpression(char * expression)
2952    {
2953       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbEvaluateExpression(", expression, ")");
2954       eval.active = true;
2955       eval.error = none;
2956       GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2957       if(eval.active)
2958          ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2959       return eval.result;
2960    }
2961
2962    // to be removed... use GdbReadMemory that returns a byte array instead
2963    char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
2964    {
2965       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemoryString(", address, ")");
2966       eval.active = true;
2967       eval.error = none;
2968 #ifdef _DEBUG
2969       if(!size)
2970          _dpl(0, "GdbReadMemoryString called with size = 0!");
2971 #endif
2972       // GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
2973       if(GetRuntimePlatform() == win32)
2974          GdbCommand(false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
2975       else
2976          GdbCommand(false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
2977       if(eval.active)
2978          ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
2979       return eval.result;
2980    }
2981
2982    byte * ::GdbReadMemory(uint64 address, int bytes)
2983    {
2984       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemory(", address, ")");
2985       eval.active = true;
2986       eval.error = none;
2987       //GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
2988       if(GetRuntimePlatform() == win32)
2989          GdbCommand(false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
2990       else
2991          GdbCommand(false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
2992 #ifdef _DEBUG
2993       if(!bytes)
2994          _dpl(0, "GdbReadMemory called with bytes = 0!");
2995 #endif
2996       if(eval.active)
2997          ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
2998       else if(eval.result && strcmp(eval.result, "N/A"))
2999       {
3000          byte * result = new byte[bytes];
3001          byte * string = eval.result;
3002          int c = 0;
3003          while(true)
3004          {
3005             result[c++] = (byte)strtol(string, &string, 10);
3006             if(string)
3007             {
3008                if(*string == ',')
3009                   string++;
3010                 else
3011                   break;
3012             }
3013             else
3014                break;
3015          }
3016          return result;
3017       }
3018       return null;
3019    }
3020
3021    void EventHit(GdbDataStop stopItem, Breakpoint bpInternal, Breakpoint bpUser)
3022    {
3023       char * s1 = null; char * s2 = null;
3024       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EventHit(",
3025             "bpInternal(", bpInternal ? s1=bpInternal.CopyLocationString(false) : null, "), ",
3026             "bpUser(", bpUser ? s2=bpUser.CopyLocationString(false) : null, ")) -- ",
3027             "ignoreBreakpoints(", ignoreBreakpoints, "), ",
3028             "hitCursorBreakpoint(", bpUser && bpUser.type == runToCursor,  ")");
3029       delete s1; delete s2;
3030
3031       if(bpUser && stopItem.frame.line && bpUser.line != stopItem.frame.line)
3032       {
3033          // updating user breakpoint on hit location difference
3034          // todo, print something?
3035          bpUser.line = stopItem.frame.line;
3036          ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3037          ide.workspace.Save();
3038       }
3039       if(bpInternal)
3040       {
3041          bpInternal.hits++;
3042          if(bpInternal.type == internalModulesLoaded)
3043             modules = true;
3044          if(!bpUser && !userBreakOnInternalBreakpoint)
3045             GdbExecContinue(false);
3046       }
3047       if(bpUser)
3048       {
3049          bool conditionMet = true;
3050          if(bpUser.condition)
3051             conditionMet = ResolveWatch(bpUser.condition);
3052          bpUser.hits++;
3053          if(!ignoreBreakpoints && (bpUser.level == -1 || bpUser.level == frameCount-1) && conditionMet)
3054          {
3055             if(!bpUser.ignore)
3056                bpUser.breaks++;
3057             else
3058             {
3059                bpUser.ignore--;
3060                GdbExecContinue(false);
3061             }
3062          }
3063          else
3064             GdbExecContinue(false);
3065          ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3066          if(bpUser == bpRunToCursor)
3067          {
3068             UnsetBreakpoint(bpUser);
3069             delete bpRunToCursor;
3070          }
3071       }
3072
3073       if(!bpUser && !bpInternal)
3074          GdbExecContinue(false);
3075    }
3076
3077    void ValgrindTargetThreadExit()
3078    {
3079       ide.outputView.debugBox.Logf($"ValgrindTargetThreadExit\n");
3080       if(vgTargetHandle)
3081       {
3082          vgTargetHandle.Wait();
3083          delete vgTargetHandle;
3084       }
3085       HandleExit(null, null);
3086    }
3087
3088    void GdbThreadExit()
3089    {
3090       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbThreadExit()");
3091       if(state != terminated)
3092       {
3093          ChangeState(terminated);
3094          targetProcessId = 0;
3095          ClearBreakDisplay();
3096
3097          if(vgLogFile)
3098             delete vgLogFile;
3099          if(gdbHandle)
3100          {
3101             serialSemaphore.Release();
3102             gdbTimer.Stop();
3103             gdbHandle.Wait();
3104             delete gdbHandle;
3105             
3106             ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
3107             ide.outputView.debugBox.Logf($"Debugging stopped\n");
3108             ide.Update(null);
3109             HideDebuggerViews();
3110          }
3111          //ChangeState(terminated);
3112       }
3113    }
3114
3115    void GdbThreadMain(char * output)
3116    {
3117       int i;
3118       Array<char *> outTokens { minAllocSize = 50 };
3119       Array<char *> subTokens { minAllocSize = 50 };
3120       DebugListItem item { };
3121       DebugListItem item2 { };
3122       bool setWaitingForPID = false;
3123       
3124 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
3125 #ifdef GDB_DEBUG_CONSOLE
3126       _dpl2(_dpct, dplchan::gdbOutput, 0, output);
3127 #endif
3128 #ifdef GDB_DEBUG_OUTPUT
3129       {
3130          int len = strlen(output);
3131          if(len > 1024)
3132          {
3133             int c;
3134             char * start;
3135             char tmp[1025];
3136             tmp[1024] = '\0';
3137             start = output;
3138             for(c = 0; c < len / 1024; c++)
3139             {
3140                strncpy(tmp, start, 1024);
3141                ide.outputView.gdbBox.Logf("out: %s\n", tmp);
3142                start += 1024;
3143             }
3144             ide.outputView.gdbBox.Logf("out: %s\n", start);
3145          }
3146          else
3147          {
3148             ide.outputView.gdbBox.Logf("out: %s\n", output);
3149          }
3150       }
3151 #endif
3152 #ifdef GDB_DEBUG_CONSOLE
3153          strcpy(lastGdbOutput, output);
3154 #endif
3155 #ifdef GDB_DEBUG_GUI
3156          if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
3157 #endif
3158 #endif
3159       
3160       switch(output[0])
3161       {
3162          case '~':
3163             if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
3164             {
3165                symbols = false;
3166                ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
3167                ide.Update(null);
3168             }
3169             break;
3170          case '^':
3171             gdbReady = false;
3172             if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
3173             {
3174                //if(outTokens.count == 1)
3175                {
3176                   if(sentKill)
3177                   {
3178                      sentKill = false;
3179                      ChangeState(loaded);
3180                      targetProcessId = 0;
3181                      if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3182                      {
3183                         if(!strcmp(item.name, "reason"))
3184                         {
3185                            char * reason = item.value;
3186                            StripQuotes(reason, reason);
3187                            if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3188                            {
3189                               char * exitCode;
3190                               if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
3191                               {
3192                                  StripQuotes(item2.value, item2.value);
3193                                  if(!strcmp(item2.name, "exit-code"))
3194                                     exitCode = item2.value;
3195                                  else
3196                                     exitCode = null;
3197                               }
3198                               else
3199                                  exitCode = null;
3200                               HandleExit(reason, exitCode);
3201                            }
3202                         }
3203                         else
3204                            _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "kill reply (", item.name, "=", item.value, ") is unheard of");
3205                      }
3206                      else
3207                         HandleExit(null, null);
3208                   }
3209                }
3210                if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3211                {
3212                   if(!strcmp(item.name, "bkpt"))
3213                   {
3214                      sentBreakInsert = false;
3215 #ifdef _DEBUG
3216                      if(bpItem)
3217                         _dpl(0, "problem");
3218 #endif
3219                      bpItem = ParseBreakpoint(item.value, outTokens);
3220                      //breakType = bpValidation;
3221                   }
3222                   else if(!strcmp(item.name, "depth"))
3223                   {
3224                      StripQuotes(item.value, item.value);
3225                      frameCount = atoi(item.value);
3226                      activeFrame = null;
3227                      stackFrames.Free(Frame::Free);
3228                   }
3229                   else if(!strcmp(item.name, "stack"))
3230                   {
3231                      Frame frame;
3232                      if(stackFrames.count)
3233                         ide.callStackView.Logf("...\n");
3234                      else
3235                         activeFrame = null;
3236                      item.value = StripBrackets(item.value);
3237                      TokenizeList(item.value, ',', subTokens);
3238                      for(i = 0; i < subTokens.count; i++)
3239                      {
3240                         if(TokenizeListItem(subTokens[i], item))
3241                         {
3242                            if(!strcmp(item.name, "frame"))
3243                            {
3244                               frame = Frame { };
3245                               stackFrames.Add(frame);
3246                               item.value = StripCurlies(item.value);
3247                               ParseFrame(frame, item.value);
3248                               if(frame.file && frame.from)
3249                                  _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "unexpected frame file and from members present");
3250                               if(frame.file)
3251                               {
3252                                  char * s;
3253                                  if(activeFrameLevel == -1)
3254                                  {
3255                                     if(ide.projectView.IsModuleInProject(frame.file));
3256                                     {
3257                                        if(frame.level != 0)
3258                                        {
3259                                           //stopItem.frame = frame;
3260                                           breakType = selectFrame;
3261                                        }
3262                                        else
3263                                           activeFrame = frame;
3264                                        activeFrameLevel = frame.level;
3265                                     }
3266                                  }
3267                                  ide.callStackView.Logf("%3d ", frame.level);
3268                                  if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
3269                                     ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
3270                                  else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
3271                                     ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
3272                                  else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
3273                                     ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
3274                                  else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
3275                                     ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
3276                                  else
3277                                     ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
3278                                  delete s;
3279                               }
3280                               else
3281                               {
3282                                  ide.callStackView.Logf("%3d ", frame.level);
3283
3284                                  if(frame.from)
3285                                  {
3286                                     char * s;
3287                                     ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
3288                                     delete s;
3289                                  }
3290                                  else if(frame.func)
3291                                     ide.callStackView.Logf("%s\n", frame.func);
3292                                  else
3293                                     ide.callStackView.Logf($"unknown source\n");
3294                               }
3295                            }
3296                            else
3297                               _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "stack content (", item.name, "=", item.value, ") is unheard of");
3298                         }
3299                      }
3300                      if(activeFrameLevel == -1)
3301                      {
3302                         activeFrameLevel = 0;
3303                         activeFrame = stackFrames.first;
3304                      }
3305                      ide.callStackView.Home();
3306                      ide.Update(null);
3307                      subTokens.RemoveAll();
3308                   }
3309                   /*else if(!strcmp(item.name, "frame"))
3310                   {
3311                      Frame frame { };
3312                      item.value = StripCurlies(item.value);
3313                      ParseFrame(&frame, item.value);
3314                   }*/
3315                   else if(!strcmp(item.name, "thread-ids"))
3316                   {
3317                      ide.threadsView.Clear();
3318                      item.value = StripCurlies(item.value);
3319                      TokenizeList(item.value, ',', subTokens);
3320                      for(i = subTokens.count - 1; ; i--)
3321                      {
3322                         if(TokenizeListItem(subTokens[i], item))
3323                         {
3324                            if(!strcmp(item.name, "thread-id"))
3325                            {
3326                               int value;
3327                               StripQuotes(item.value, item.value);
3328                               value = atoi(item.value);
3329                               ide.threadsView.Logf("%3d \n", value);
3330                            }
3331                            else
3332                               _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "threads content (", item.name, "=", item.value, ") is unheard of");
3333                         }
3334                         if(!i)
3335                            break;
3336                      }
3337                      ide.threadsView.Home();
3338                      ide.Update(null);
3339                      subTokens.RemoveAll();
3340                      //if(!strcmp(outTokens[2], "number-of-threads"))
3341                   }
3342                   else if(!strcmp(item.name, "new-thread-id"))
3343                   {
3344                      StripQuotes(item.value, item.value);
3345                      activeThread = atoi(item.value);
3346                   }
3347                   else if(!strcmp(item.name, "value"))
3348                   {
3349                      StripQuotes(item.value, item.value);
3350                      eval.result = CopyString(item.value);
3351                      eval.active = false;
3352                   }
3353                   else if(!strcmp(item.name, "addr"))
3354                   {
3355                      for(i = 2; i < outTokens.count; i++)
3356                      {
3357                         if(TokenizeListItem(outTokens[i], item))
3358                         {
3359                            if(!strcmp(item.name, "total-bytes"))
3360                            {
3361                               StripQuotes(item.value, item.value);
3362                               eval.bytes = atoi(item.value);
3363                            }
3364                            else if(!strcmp(item.name, "next-row"))
3365                            {
3366                               StripQuotes(item.value, item.value);
3367                               eval.nextBlockAddress = _strtoui64(item.value, null, 0);
3368                            }
3369                            else if(!strcmp(item.name, "memory"))
3370                            {
3371                               int j;
3372                               //int value;
3373                               //StripQuotes(item.value, item.value);
3374                               item.value = StripBrackets(item.value);
3375                               // this should be treated as a list...
3376                               item.value = StripCurlies(item.value);
3377                               TokenizeList(item.value, ',', subTokens);
3378                               for(j = 0; j < subTokens.count; j++)
3379                               {
3380                                  if(TokenizeListItem(subTokens[j], item))
3381                                  {
3382                                     if(!strcmp(item.name, "data"))
3383                                     {
3384                                        item.value = StripBrackets(item.value);
3385                                        StripQuotes2(item.value, item.value);
3386                                        eval.result = CopyString(item.value);
3387                                        eval.active = false;
3388                                     }
3389                                  }
3390                               }
3391                               subTokens.RemoveAll();
3392                            }
3393                         }
3394                      }
3395                   }
3396                   else if(!strcmp(item.name, "source-path") || !strcmp(item.name, "BreakpointTable"))
3397                      _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "command reply (", item.name, "=", item.value, ") is ignored");
3398                   else
3399                      _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "command reply (", item.name, "=", item.value, ") is unheard of");
3400                }
3401             }
3402             else if(!strcmp(outTokens[0], "^running"))
3403             {
3404                waitingForPID = true;
3405                setWaitingForPID = true;
3406             }
3407             else if(!strcmp(outTokens[0], "^exit"))
3408             {
3409                ChangeState(terminated);
3410                // ide.outputView.debugBox.Logf("Exit\n");
3411                // ide.Update(null);
3412                gdbReady = true;
3413                serialSemaphore.Release();
3414             }
3415             else if(!strcmp(outTokens[0], "^error"))
3416             {
3417                if(sentBreakInsert)
3418                {
3419                   sentBreakInsert = false;
3420                   breakpointError = true;
3421                }
3422
3423                if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3424                {
3425                   if(!strcmp(item.name, "msg"))
3426                   {
3427                      StripQuotes(item.value, item.value);
3428                      if(eval.active)
3429                      {
3430                         eval.active = false;
3431                         eval.result = null;
3432                         if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3433                            eval.error = symbolNotFound;
3434                         else if(strstr(item.value, "Cannot access memory at address"))
3435                            eval.error = memoryCantBeRead;
3436                         else
3437                            eval.error = unknown;
3438                      }
3439                      else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3440                      {
3441                      }
3442                      else if(!strncmp(item.value, "Cannot access memory at address", 31))
3443                      {
3444                      }
3445                      else if(!strcmp(item.value, "Cannot find bounds of current function"))
3446                      {
3447                         ChangeState(stopped);
3448                         gdbHandle.Printf("-exec-continue\n");
3449                      }
3450                      else if(!strcmp(item.value, "ptrace: No such process."))
3451                      {
3452                         ChangeState(loaded);
3453                         targetProcessId = 0;
3454                      }
3455                      else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3456                      {
3457                      }
3458                      else if(!strcmp(item.value, "You can't do that without a process to debug."))
3459                      {
3460                         ChangeState(loaded);
3461                         targetProcessId = 0;
3462                      }
3463                      else if(strstr(item.value, "No such file or directory."))
3464                      {
3465                         ChangeState(loaded);
3466                         targetProcessId = 0;
3467                      }
3468                      else if(strstr(item.value, "During startup program exited with code "))
3469                      {
3470                         ChangeState(loaded);
3471                         targetProcessId = 0;
3472                      }
3473                      else
3474                      {
3475 #ifdef _DEBUG
3476                         if(strlen(item.value) < MAX_F_STRING)
3477                         {
3478                            char * s;
3479                            ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3480                            delete s;
3481                         }
3482                         else
3483                            ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3484 #endif
3485                      }
3486                   }
3487                }
3488                else
3489                   _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "error content (", item.name, "=", item.value, ") is unheard of");
3490             }
3491             else
3492                _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "result-record: ", outTokens[0]);
3493             outTokens.RemoveAll();
3494             break;
3495          case '+':
3496             _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "status-async-output: ", outTokens[0]);
3497             break;
3498          case '=':
3499             if(TokenizeList(output, ',', outTokens))
3500             {
3501                if(!strcmp(outTokens[0], "=library-loaded"))
3502                   FGODetectLoadedLibraryForAddedProjectIssues(outTokens);
3503                else if(!strcmp(outTokens[0], "=thread-group-created") || !strcmp(outTokens[0], "=thread-group-added") ||
3504                         !strcmp(outTokens[0], "=thread-group-started") || !strcmp(outTokens[0], "=thread-group-exited") ||
3505                         !strcmp(outTokens[0], "=thread-created") || !strcmp(outTokens[0], "=thread-exited") ||
3506                         !strcmp(outTokens[0], "=cmd-param-changed") || !strcmp(outTokens[0], "=library-unloaded") ||
3507                         !strcmp(outTokens[0], "=breakpoint-modified"))
3508                   _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, outTokens[0], outTokens.count>1 ? outTokens[1] : "",
3509                            outTokens.count>2 ? outTokens[2] : "", outTokens.count>3 ? outTokens[3] : "",
3510                            outTokens.count>4 ? outTokens[4] : "", outTokens.count>5 ? outTokens[5] : "",
3511                            outTokens.count>6 ? outTokens[6] : "", outTokens.count>7 ? outTokens[7] : "",
3512                            outTokens.count>8 ? outTokens[8] : "", outTokens.count>9 ? outTokens[9] : "");
3513                else
3514                   _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "notify-async-output: ", outTokens[0]);
3515             }
3516             outTokens.RemoveAll();
3517             break;
3518          case '*':
3519             gdbReady = false;
3520             if(TokenizeList(output, ',', outTokens))
3521             {
3522                if(!strcmp(outTokens[0],"*running"))
3523                {
3524                   waitingForPID = true;
3525                   setWaitingForPID = true;
3526                }
3527                else if(!strcmp(outTokens[0], "*stopped"))
3528                {
3529                   int tk;
3530                   ChangeState(stopped);
3531
3532                   for(tk = 1; tk < outTokens.count; tk++)
3533                   {
3534                      if(TokenizeListItem(outTokens[tk], item))
3535                      {
3536                         if(!strcmp(item.name, "reason"))
3537                         {
3538                            char * reason = item.value;
3539                            StripQuotes(reason, reason);
3540                            if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3541                            {
3542                               char * exitCode;
3543                               if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3544                               {
3545                                  tk++;
3546                                  StripQuotes(item2.value, item2.value);
3547                                  if(!strcmp(item2.name, "exit-code"))
3548                                     exitCode = item2.value;
3549                                  else
3550                                     exitCode = null;
3551                               }
3552                               else
3553                                  exitCode = null;
3554                               HandleExit(reason, exitCode);
3555                               needReset = true;
3556                            }
3557                            else if(!strcmp(reason, "breakpoint-hit"))
3558                            {
3559       #ifdef _DEBUG
3560                               if(stopItem)
3561                                  _dpl(0, "problem");
3562       #endif
3563                               stopItem = GdbDataStop { };
3564
3565                               for(i = tk+1; i < outTokens.count; i++)
3566                               {
3567                                  TokenizeListItem(outTokens[i], item);
3568                                  StripQuotes(item.value, item.value);
3569                                  if(!strcmp(item.name, "bkptno"))
3570                                     stopItem.bkptno = atoi(item.value);
3571                                  else if(!strcmp(item.name, "thread-id"))
3572                                     stopItem.threadid = atoi(item.value);
3573                                  else if(!strcmp(item.name, "frame"))
3574                                  {
3575                                     item.value = StripCurlies(item.value);
3576                                     ParseFrame(stopItem.frame, item.value);
3577                                  }
3578                                  else if(!strcmp(item.name, "disp") || !strcmp(item.name, "stopped-threads") || !strcmp(item.name, "core"))
3579                                     _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3580                                  else
3581                                     _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown breakpoint hit item name (", item.name, "=", item.value, ")");
3582                               }
3583
3584                               event = hit;
3585                            }
3586                            else if(!strcmp(reason, "end-stepping-range"))
3587                            {
3588       #ifdef _DEBUG
3589                               if(stopItem)
3590                                  _dpl(0, "problem");
3591       #endif
3592                               stopItem = GdbDataStop { };
3593
3594                               for(i = tk+1; i < outTokens.count; i++)
3595                               {
3596                                  TokenizeListItem(outTokens[i], item);
3597                                  StripQuotes(item.value, item.value);
3598                                  if(!strcmp(item.name, "thread-id"))
3599                                     stopItem.threadid = atoi(item.value);
3600                                  else if(!strcmp(item.name, "frame"))
3601                                  {
3602                                     item.value = StripCurlies(item.value);
3603                                     ParseFrame(stopItem.frame, item.value);
3604                                  }
3605                                  else if(!strcmp(item.name, "reason") || !strcmp(item.name, "bkptno"))
3606                                     _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3607                                  else
3608                                     _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown end of stepping range item name (", item.name, "=", item.value, ")");
3609                               }
3610
3611                               event = stepEnd;
3612                               ide.Update(null);
3613                            }
3614                            else if(!strcmp(reason, "function-finished"))
3615                            {
3616       #ifdef _DEBUG
3617                               if(stopItem)
3618                                  _dpl(0, "problem");
3619       #endif
3620                               stopItem = GdbDataStop { };
3621                               stopItem.reason = CopyString(reason);
3622
3623                               for(i = tk+1; i < outTokens.count; i++)
3624                               {
3625                                  TokenizeListItem(outTokens[i], item);
3626                                  StripQuotes(item.value, item.value);
3627                                  if(!strcmp(item.name, "thread-id"))
3628                                     stopItem.threadid = atoi(item.value);
3629                                  else if(!strcmp(item.name, "frame"))
3630                                  {
3631                                     item.value = StripCurlies(item.value);
3632                                     ParseFrame(stopItem.frame, item.value);
3633                                  }
3634                                  else if(!strcmp(item.name, "gdb-result-var"))
3635                                     stopItem.gdbResultVar = CopyString(item.value);
3636                                  else if(!strcmp(item.name, "return-value"))
3637                                     stopItem.returnValue = CopyString(item.value);
3638                                  else
3639                                     _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown function finished item name (", item.name, "=", item.value, ")");
3640                               }
3641
3642                               event = functionEnd;
3643                               ide.Update(null);
3644                            }
3645                            else if(!strcmp(reason, "signal-received"))
3646                            {
3647       #ifdef _DEBUG
3648                               if(stopItem)
3649                                  _dpl(0, "problem");
3650       #endif
3651                               stopItem = GdbDataStop { };
3652                               stopItem.reason = CopyString(reason);
3653
3654                               for(i = tk+1; i < outTokens.count; i++)
3655                               {
3656                                  TokenizeListItem(outTokens[i], item);
3657                                  StripQuotes(item.value, item.value);
3658                                  if(!strcmp(item.name, "signal-name"))
3659                                     stopItem.name = CopyString(item.value);
3660                                  else if(!strcmp(item.name, "signal-meaning"))
3661                                     stopItem.meaning = CopyString(item.value);
3662                                  else if(!strcmp(item.name, "thread-id"))
3663                                     stopItem.threadid = atoi(item.value);
3664                                  else if(!strcmp(item.name, "frame"))
3665                                  {
3666                                     item.value = StripCurlies(item.value);
3667                                     ParseFrame(stopItem.frame, item.value);
3668                                  }
3669                                  else
3670                                     _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown signal reveived item name (", item.name, "=", item.value, ")");
3671                               }
3672                               if(!strcmp(stopItem.name, "SIGTRAP"))
3673                               {
3674                                  switch(breakType)
3675                                  {
3676                                     case internal:
3677                                        breakType = none;
3678                                        break;
3679                                     case restart:
3680                                     case stop:
3681                                        break;
3682                                     default:
3683                                        event = breakEvent;
3684                                  }
3685                               }
3686                               else
3687                               {
3688                                  event = signal;
3689                               }
3690                            }
3691                            else if(!strcmp(reason, "watchpoint-trigger"))
3692                               _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint trigger not handled");
3693                            else if(!strcmp(reason, "read-watchpoint-trigger"))
3694                               _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason read watchpoint trigger not handled");
3695                            else if(!strcmp(reason, "access-watchpoint-trigger"))
3696                               _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason access watchpoint trigger not handled");
3697                            else if(!strcmp(reason, "watchpoint-scope"))
3698                               _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint scope not handled");
3699                            else if(!strcmp(reason, "location-reached"))
3700                               _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason location reached not handled");
3701                            else
3702                               _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown reason: ", reason);
3703                         }
3704                         else
3705                         {
3706                            PrintLn(output);
3707                         }
3708                      }
3709                   }
3710                   if(usingValgrind && event == none && !stopItem)
3711                      event = valgrindStartPause;
3712                   app.SignalEvent();
3713                }
3714             }
3715             else
3716                _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown exec-async-output: ", outTokens[0]);
3717             outTokens.RemoveAll();
3718             break;
3719          case '(':
3720             if(!strcmpi(output, "(gdb) "))
3721             {
3722                if(waitingForPID)
3723                {
3724                   char exeFile[MAX_LOCATION];
3725                   int oldProcessID = targetProcessId;
3726                   GetLastDirectory(targetFile, exeFile);
3727
3728                   while(!targetProcessId/*true*/)
3729                   {
3730                      targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3731                      if(targetProcessId || gdbHandle.Peek()) break;
3732                      Sleep(0.01);
3733                   }
3734
3735                   if(targetProcessId)
3736                      ChangeState(running);
3737                   else if(!oldProcessID)
3738                   {
3739                      ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3740                      // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3741                      gdbHandle.Printf("-gdb-exit\n");
3742                      gdbTimer.Stop();
3743                      ChangeState(terminated); //loaded;
3744                      prjConfig = null;
3745
3746                      if(ide.workspace)
3747                      {
3748                         for(bp : ide.workspace.breakpoints)
3749                            bp.inserted = false;
3750                      }
3751                      for(bp : sysBPs)
3752                         bp.inserted = false;
3753                      if(bpRunToCursor)
3754                         bpRunToCursor.inserted = false;
3755
3756                      ide.outputView.debugBox.Logf($"Debugging stopped\n");
3757                      ClearBreakDisplay();
3758
3759                #if defined(__unix__)
3760                      if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
3761                      {
3762                         progThread.terminate = true;
3763                         if(fifoFile)
3764                         {
3765                            fifoFile.CloseInput();
3766                            app.Unlock();
3767                            progThread.Wait();
3768                            app.Lock();
3769                            delete fifoFile;
3770                         }
3771
3772                         DeleteFile(progFifoPath);
3773                         progFifoPath[0] = '\0';
3774                         rmdir(progFifoDir);
3775                      }
3776                #endif
3777                   }
3778                }
3779                gdbReady = true;
3780                serialSemaphore.Release();
3781             }
3782             else
3783                _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown prompt", output);
3784
3785             break;
3786          case '&':
3787             if(!strncmp(output, "&\"warning:", 10))
3788             {
3789                char * content;
3790                content = strstr(output, "\"");
3791                StripQuotes(content, content);
3792                content = strstr(content, ":");
3793                if(content)
3794                   content++;
3795                if(content)
3796                {
3797                   char * s;
3798                   ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3799                   delete s;
3800                   ide.Update(null);
3801                }
3802             }
3803             break;
3804          default:
3805             _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown output: ", output);
3806       }
3807       if(!setWaitingForPID)
3808          waitingForPID = false;
3809       setWaitingForPID = false;
3810
3811       delete outTokens;
3812       delete subTokens;
3813       delete item;
3814       delete item2;
3815    }
3816
3817    // From GDB Output functions
3818    void FGODetectLoadedLibraryForAddedProjectIssues(Array<char *> outTokens)
3819    {
3820       char path[MAX_LOCATION] = "";
3821       char file[MAX_FILENAME] = "";
3822       bool symbolsLoaded;
3823       DebugListItem item { };
3824       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGODetectLoadedLibraryForAddedProjectIssues()");
3825       for(token : outTokens)
3826       {
3827          if(TokenizeListItem(token, item))
3828          {
3829             if(!strcmp(item.name, "target-name"))
3830             {
3831                StripQuotes(item.value, path);
3832                MakeSystemPath(path);
3833                GetLastDirectory(path, file);
3834             }
3835             else if(!strcmp(item.name, "symbols-loaded"))
3836             {
3837                symbolsLoaded = (atoi(item.value) == 1);
3838             }
3839          }
3840       }
3841       delete item;
3842       if(path[0] && file[0])
3843       {
3844          for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
3845          {
3846             bool match;
3847             char * dot;
3848             char prjTargetPath[MAX_LOCATION];
3849             char prjTargetFile[MAX_FILENAME];
3850             DirExpression targetDirExp = prj.GetTargetDir(currentCompiler, prj.config, bitDepth);
3851             strcpy(prjTargetPath, prj.topNode.path);
3852             PathCat(prjTargetPath, targetDirExp.dir);
3853             prjTargetFile[0] = '\0';
3854             prj.CatTargetFileName(prjTargetFile, currentCompiler, prj.config);
3855             PathCat(prjTargetPath, prjTargetFile);
3856             MakeSystemPath(prjTargetPath);
3857
3858             match = !fstrcmp(prjTargetFile, file);
3859             if(!match && (dot = strstr(prjTargetFile, ".so.")))
3860             {
3861                char * dot3 = strstr(dot+4, ".");
3862                if(dot3)
3863                {
3864                   dot3[0] = '\0';
3865                   match = !fstrcmp(prjTargetFile, file);
3866                }
3867                if(!match)
3868                {
3869                   dot[3] = '\0';
3870                   match = !fstrcmp(prjTargetFile, file);
3871                }
3872             }
3873             if(match)
3874             {
3875                // TODO: nice visual feedback to better warn user. use some ide notification system or other means.
3876                /* -- this is disabled because we can't trust gdb's symbols-loaded="0" field for =library-loaded (http://sourceware.org/bugzilla/show_bug.cgi?id=10693)
3877                if(!symbolsLoaded)
3878                   ide.outputView.debugBox.Logf($"Attention! No symbols for loaded library %s matched to the %s added project.\n", path, prj.topNode.name);
3879                */
3880                match = !fstrcmp(prjTargetPath, path);
3881                if(!match && (dot = strstr(prjTargetPath, ".so.")))
3882                {
3883                   char * dot3 = strstr(dot+4, ".");
3884                   if(dot3)
3885                   {
3886                      dot3[0] = '\0';
3887                      match = !fstrcmp(prjTargetPath, path);
3888                   }
3889                   if(!match)
3890                   {
3891                      dot[3] = '\0';
3892                      match = !fstrcmp(prjTargetPath, path);
3893                   }
3894                }
3895                if(!match)
3896                   ide.outputView.debugBox.Logf($"Loaded library %s doesn't match the %s target of the %s added project.\n", path, prjTargetPath, prj.topNode.name);
3897                break;
3898             }
3899          }
3900       }
3901    }
3902
3903    void FGOBreakpointModified(Array<char *> outTokens)
3904    {
3905       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGOBreakpointModified() -- TODO only if needed: support breakpoint modified");
3906 #if 0
3907       DebugListItem item { };
3908       if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3909       {
3910          if(!strcmp(item.name, "bkpt"))
3911          {
3912             GdbDataBreakpoint modBp = ParseBreakpoint(item.value, outTokens);
3913             delete modBp;
3914          }
3915       }
3916 #endif
3917    }
3918
3919
3920    ExpressionType ::DebugEvalExpTypeError(char * result)
3921    {
3922       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::DebugEvalExpTypeError()");
3923       if(result)
3924          return dummyExp;
3925       switch(eval.error)
3926       {
3927          case symbolNotFound:
3928             return symbolErrorExp;
3929          case memoryCantBeRead:
3930             return memoryErrorExp;
3931       }
3932       return unknownErrorExp;
3933    }
3934
3935    char * ::EvaluateExpression(char * expression, ExpressionType * error)
3936    {
3937       char * result;
3938       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateExpression(", expression, ")");
3939       if(ide.projectView && ide.debugger.state == stopped)
3940       {
3941          result = GdbEvaluateExpression(expression);
3942          *error = DebugEvalExpTypeError(result);
3943       }
3944       else
3945       {
3946          result = null;
3947          *error = noDebuggerErrorExp;
3948       }
3949       return result;
3950    }
3951
3952    char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
3953    {
3954       // check for state
3955       char * result = GdbReadMemoryString(address, size, format, 1, 1);
3956       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ReadMemory(", address, ")");
3957       if(!result || !strcmp(result, "N/A"))
3958          *error = memoryErrorExp;
3959       else
3960          *error = DebugEvalExpTypeError(result);
3961       return result;
3962    }
3963 }
3964
3965 class ValgrindLogThread : Thread
3966 {
3967    Debugger debugger;
3968
3969    unsigned int Main()
3970    {
3971       static char output[4096];
3972       Array<char> dynamicBuffer { minAllocSize = 4096 };
3973       File oldValgrindHandle = vgLogFile;
3974       incref oldValgrindHandle;
3975
3976       app.Lock();
3977       while(debugger.state != terminated && vgLogFile)
3978       {
3979          int result;
3980          app.Unlock();
3981          result = vgLogFile.Read(output, 1, sizeof(output));
3982          app.Lock();
3983          if(debugger.state == terminated || !vgLogFile/* || vgLogFile.Eof()*/)
3984             break;
3985          if(result)
3986          {
3987             int c;
3988             int start = 0;
3989
3990             for(c = 0; c<result; c++)
3991             {
3992                if(output[c] == '\n')
3993                {
3994                   int pos = dynamicBuffer.size;
3995                   dynamicBuffer.size += c - start;
3996                   memcpy(&dynamicBuffer[pos], output + start, c - start);
3997                   if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3998                   // COMMENTED OUT DUE TO ISSUE #135, FIXED
3999                   //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4000                      dynamicBuffer.size++;
4001                   dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4002 #ifdef _DEBUG
4003                   // printf("%s\n", dynamicBuffer.array);
4004 #endif
4005                   if(strstr(&dynamicBuffer[0], "vgdb me"))
4006                      debugger.serialSemaphore.Release();
4007                   ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4008                   dynamicBuffer.size = 0;
4009                   start = c + 1;
4010                }
4011             }
4012             if(c == result)
4013             {
4014                int pos = dynamicBuffer.size;
4015                dynamicBuffer.size += c - start;
4016                memcpy(&dynamicBuffer[pos], output + start, c - start);
4017             }
4018          }
4019          else if(debugger.state == stopped)
4020          {
4021 /*#ifdef _DEBUG
4022             printf("Got end of file from GDB!\n");
4023 #endif*/
4024             app.Unlock();
4025             Sleep(0.2);
4026             app.Lock();
4027          }
4028       }
4029       delete dynamicBuffer;
4030       ide.outputView.debugBox.Logf($"ValgrindLogThreadExit\n");
4031       //if(oldValgrindHandle == vgLogFile)
4032          debugger.GdbThreadExit/*ValgrindLogThreadExit*/();
4033       delete oldValgrindHandle;
4034       app.Unlock();
4035       return 0;
4036    }
4037 }
4038
4039 class ValgrindTargetThread : Thread
4040 {
4041    Debugger debugger;
4042
4043    unsigned int Main()
4044    {
4045       static char output[4096];
4046       Array<char> dynamicBuffer { minAllocSize = 4096 };
4047       DualPipe oldValgrindHandle = vgTargetHandle;
4048       incref oldValgrindHandle;
4049
4050       app.Lock();
4051       while(debugger.state != terminated && vgTargetHandle && !vgTargetHandle.Eof())
4052       {
4053          int result;
4054          app.Unlock();
4055          result = vgTargetHandle.Read(output, 1, sizeof(output));
4056          app.Lock();
4057          if(debugger.state == terminated || !vgTargetHandle || vgTargetHandle.Eof())
4058             break;
4059          if(result)
4060          {
4061             int c;
4062             int start = 0;
4063
4064             for(c = 0; c<result; c++)
4065             {
4066                if(output[c] == '\n')
4067                {
4068                   int pos = dynamicBuffer.size;
4069                   dynamicBuffer.size += c - start;
4070                   memcpy(&dynamicBuffer[pos], output + start, c - start);
4071                   if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4072                   // COMMENTED OUT DUE TO ISSUE #135, FIXED
4073                   //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4074                      dynamicBuffer.size++;
4075                   dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4076 #ifdef _DEBUG
4077                   // printf("%s\n", dynamicBuffer.array);
4078 #endif
4079                   ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4080
4081                   dynamicBuffer.size = 0;
4082                   start = c + 1;
4083                }
4084             }
4085             if(c == result)
4086             {
4087                int pos = dynamicBuffer.size;
4088                dynamicBuffer.size += c - start;
4089                memcpy(&dynamicBuffer[pos], output + start, c - start);
4090             }
4091          }
4092          else
4093          {
4094 #ifdef _DEBUG
4095             printf("Got end of file from GDB!\n");
4096 #endif
4097          }
4098       }
4099       delete dynamicBuffer;
4100       //if(oldValgrindHandle == vgTargetHandle)
4101          debugger.ValgrindTargetThreadExit();
4102       delete oldValgrindHandle;
4103       app.Unlock();
4104       return 0;
4105    }
4106 }
4107
4108 class GdbThread : Thread
4109 {
4110    Debugger debugger;
4111
4112    unsigned int Main()
4113    {
4114       static char output[4096];
4115       Array<char> dynamicBuffer { minAllocSize = 4096 };
4116       DualPipe oldGdbHandle = gdbHandle;
4117       incref oldGdbHandle;
4118
4119       app.Lock();
4120       while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
4121       {
4122          int result;
4123          app.Unlock();
4124          result = gdbHandle.Read(output, 1, sizeof(output));
4125          app.Lock();
4126          if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
4127             break;
4128          if(result)
4129          {
4130             int c;
4131             int start = 0;
4132
4133             for(c = 0; c<result; c++)
4134             {
4135                if(output[c] == '\n')
4136                {
4137                   int pos = dynamicBuffer.size;
4138                   dynamicBuffer.size += c - start;
4139                   memcpy(&dynamicBuffer[pos], output + start, c - start);
4140                   if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4141                   // COMMENTED OUT DUE TO ISSUE #135, FIXED
4142                   //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4143                      dynamicBuffer.size++;
4144                   dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4145 #ifdef _DEBUG
4146                   // _dpl(0, dynamicBuffer.array);
4147 #endif
4148                   debugger.GdbThreadMain(&dynamicBuffer[0]);
4149                   dynamicBuffer.size = 0;
4150                   start = c + 1;
4151                }
4152             }
4153             if(c == result)
4154             {
4155                int pos = dynamicBuffer.size;
4156                dynamicBuffer.size += c - start;
4157                memcpy(&dynamicBuffer[pos], output + start, c - start);
4158             }
4159          }
4160          else
4161          {
4162 #ifdef _DEBUG
4163             _dpl(0, "Got end of file from GDB!");
4164 #endif
4165          }
4166       }
4167       delete dynamicBuffer;
4168       //if(oldGdbHandle == gdbHandle)
4169          debugger.GdbThreadExit();
4170       delete oldGdbHandle;
4171       app.Unlock();
4172       return 0;
4173    }
4174 }
4175
4176 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
4177 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
4178
4179 #if defined(__unix__)
4180 #define uint _uint
4181 #include <errno.h>
4182 #include <stdio.h>
4183 #include <fcntl.h>
4184 #include <sys/types.h>
4185 #undef uint
4186
4187 File fifoFile;
4188
4189 class ProgramThread : Thread
4190 {
4191    bool terminate;
4192    unsigned int Main()
4193    {
4194       bool result = true;
4195       bool fileCreated = false;
4196       mode_t mask = 0600;
4197       static char output[1000];
4198       int fd;
4199
4200       /*if(!mkfifo(progFifoPath, mask))
4201       {
4202          fileCreated = true;
4203       }
4204       else
4205       {
4206          app.Lock();
4207          ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
4208          app.Unlock();
4209       }*/
4210
4211       if(FileExists(progFifoPath)) //fileCreated)
4212       {
4213          fifoFile = FileOpen(progFifoPath, read);
4214          if(!fifoFile)
4215          {
4216             app.Lock();
4217             ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
4218             app.Unlock();
4219          }
4220          else
4221          {
4222             fd = fileno((FILE *)fifoFile.input);
4223             //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
4224          }
4225       }
4226
4227       while(!terminate && fifoFile && !fifoFile.Eof())
4228       {
4229          fd_set rs, es;
4230          struct timeval time;
4231          int selectResult;
4232          time.tv_sec = 1;
4233          time.tv_usec = 0;
4234          FD_ZERO(&rs);
4235          FD_ZERO(&es);
4236          FD_SET(fd, &rs);
4237          FD_SET(fd, &es);
4238          selectResult = select(fd + 1, &rs, null, null, &time);
4239          if(FD_ISSET(fd, &rs))
4240          {
4241             int result = (int)read(fd, output, sizeof(output)-1);
4242             if(!result || (result < 0 && errno != EAGAIN))
4243                break;
4244             if(result > 0)
4245             {
4246                output[result] = '\0';
4247                if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
4248                {
4249                   app.Lock();
4250                   ide.outputView.debugBox.Log(output);
4251                   app.Unlock();
4252                }
4253             }
4254          }
4255       }
4256
4257       //if(fifoFile)
4258       {
4259          //fifoFile.CloseInput();
4260          //delete fifoFile;
4261          app.Lock();
4262          ide.outputView.debugBox.Log("\n");
4263          app.Unlock();
4264       }
4265       /*
4266       if(FileExists(progFifoPath)) //fileCreated)
4267       {
4268          DeleteFile(progFifoPath);
4269          progFifoPath[0] = '\0';
4270       }
4271       */
4272       return 0;
4273    }
4274 }
4275 #endif
4276
4277 class Argument : struct
4278 {
4279    Argument prev, next;
4280    char * name;
4281    property char * name { set { delete name; if(value) name = CopyString(value); } }
4282    char * val;
4283    property char * val { set { delete val; if(value) val = CopyString(value); } }
4284
4285    void Free()
4286    {
4287       delete name;
4288       delete val;
4289    }
4290
4291    ~Argument()
4292    {
4293       Free();
4294    }
4295 }
4296
4297 class Frame : struct
4298 {
4299    Frame prev, next;
4300    int level;
4301    char * addr;
4302    property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4303    char * func;
4304    property char * func { set { delete func; if(value) func = CopyString(value); } }
4305    int argsCount;
4306    OldList args;
4307    char * from;
4308    property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
4309    char * file;
4310    property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4311    char * absoluteFile;
4312    property char * absoluteFile { set { delete absoluteFile; if(value) absoluteFile = CopyUnescapedUnixPath(value); } }
4313    int line;
4314
4315    void Free()
4316    {
4317       delete addr;
4318       delete func;
4319       delete from;
4320       delete file;
4321       delete absoluteFile;
4322       args.Free(Argument::Free);
4323    }
4324
4325    ~Frame()
4326    {
4327       Free();
4328    }
4329 }
4330
4331 class GdbDataStop : struct
4332 {
4333    char * reason;
4334    int threadid;
4335    union
4336    {
4337       struct
4338       {
4339          int bkptno;
4340       };
4341       struct
4342       {
4343          char * name;
4344          char * meaning;
4345       };
4346       struct
4347       {
4348          char * gdbResultVar;
4349          char * returnValue;
4350       };
4351    };
4352    Frame frame { };
4353
4354    void Free()
4355    {
4356       if(reason)
4357       {
4358          if(!strcmp(reason, "signal-received"))
4359          {
4360             delete name;
4361             delete meaning;
4362          }
4363          else if(!strcmp(reason, "function-finished"))
4364          {
4365             delete gdbResultVar;
4366             delete returnValue;
4367          }
4368          delete reason;
4369       }
4370       if(frame) frame.Free();
4371    }
4372
4373    ~GdbDataStop()
4374    {
4375       Free();
4376    }
4377 }
4378
4379 class GdbDataBreakpoint : struct
4380 {
4381    int id;
4382    char * number;
4383    property char * number { set { delete number; if(value) number = CopyString(value); } }
4384    char * type;
4385    property char * type { set { delete type; if(value) type = CopyString(value); } }
4386    char * disp;
4387    property char * disp { set { delete disp; if(value) disp = CopyString(value); } }
4388    bool enabled;
4389    char * addr;
4390    property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4391    char * func;
4392    property char * func { set { delete func; if(value) func = CopyString(value); } }
4393    char * file;
4394    property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4395    char * fullname;
4396    property char * fullname { set { delete fullname; if(value) fullname = CopyUnescapedUnixPath(value); } }
4397    int line;
4398    char * at;
4399    property char * at { set { delete at; if(value) at = CopyString(value); } }
4400    int times;
4401
4402    Array<GdbDataBreakpoint> multipleBPs;
4403
4404    void Print()
4405    {
4406    _dpl(0, "");
4407       PrintLn("{", "#", number, " T", type, " D", disp, " E", enabled, " H", times, " (", func, ") (", file, ":", line, ") (", fullname, ") (", addr, ") (", at, ")", "}");
4408    }
4409
4410    void Free()
4411    {
4412       delete type;
4413       delete disp;
4414       delete addr;
4415       delete func;
4416       delete file;
4417       delete at;
4418       if(multipleBPs) multipleBPs.Free();
4419       delete multipleBPs;
4420    }
4421
4422    ~GdbDataBreakpoint()
4423    {
4424       Free();
4425    }
4426 }
4427
4428 class Breakpoint : struct
4429 {
4430    class_no_expansion;
4431
4432    char * function;
4433    property char * function { set { delete function; if(value) function = CopyString(value); } }
4434    char * relativeFilePath;
4435    property char * relativeFilePath { set { delete relativeFilePath; if(value) relativeFilePath = CopyString(value); } }
4436    char * absoluteFilePath;
4437    property char * absoluteFilePath { set { delete absoluteFilePath; if(value) absoluteFilePath = CopyString(value); } }
4438    int line;
4439    bool enabled;
4440    int hits;
4441    int breaks;
4442    int ignore;
4443    int level;
4444    Watch condition;
4445    bool inserted;
4446    BreakpointType type;
4447    DataRow row;
4448    GdbDataBreakpoint bp;
4449
4450    char * CopyLocationString(bool removePath)
4451    {
4452       char * location;
4453       char * file = relativeFilePath ? relativeFilePath : absoluteFilePath;
4454       bool removingPath = removePath && file;
4455       if(removingPath)
4456       {
4457          char * fileName = new char[MAX_FILENAME];
4458          GetLastDirectory(file, fileName);
4459          file = fileName;
4460       }
4461       if(function)
4462       {
4463          if(file)
4464             location = PrintString(file, ":", function);
4465          else
4466             location = CopyString(function);
4467       }
4468       else
4469          location = PrintString(file, ":", line);
4470       if(removingPath)
4471          delete file;
4472       return location;
4473    }
4474
4475    char * CopyUserLocationString()
4476    {
4477       char * location;
4478       char * loc = CopyLocationString(false);
4479       Project prj = null;
4480       for(p : ide.workspace.projects)
4481       {
4482          if(p.topNode.FindByFullPath(absoluteFilePath, false))
4483          {
4484             prj = p;
4485             break;
4486          }
4487       }
4488       if(prj)
4489       {
4490          location = PrintString("(", prj.name, ")", loc);
4491          delete loc;
4492       }
4493       else
4494          location = loc;
4495       return location;
4496    }
4497
4498    void Save(File f)
4499    {
4500       if(relativeFilePath && relativeFilePath[0])
4501       {
4502          f.Printf("    * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
4503          if(condition)
4504             f.Printf("       ~ %s\n", condition.expression);
4505       }
4506    }
4507
4508    void Free()
4509    {
4510       if(bp)
4511          bp.Free();
4512       delete bp;
4513       delete function;
4514       delete relativeFilePath;
4515       delete absoluteFilePath;
4516    }
4517
4518    ~Breakpoint()
4519    {
4520       Free();
4521    }
4522
4523 }
4524
4525 class Watch : struct
4526 {
4527    class_no_expansion;
4528    
4529    Type type;
4530    char * expression;
4531    char * value;
4532    DataRow row;
4533
4534    void Save(File f)
4535    {
4536       f.Printf("    ~ %s\n", expression);
4537    }
4538
4539    void Free()
4540    {
4541       delete expression;
4542       delete value;
4543       FreeType(type);
4544       type = null;
4545    }
4546
4547    void Reset()
4548    {
4549       delete value;
4550       FreeType(type);
4551       type = null;
4552    }
4553
4554    ~Watch()
4555    {
4556       Free();
4557    }
4558 }
4559
4560 class DebugListItem : struct
4561 {
4562    char * name;
4563    char * value;
4564 }
4565
4566 struct DebugEvaluationData
4567 {
4568    bool active;
4569    char * result;
4570    int bytes;
4571    uint64 nextBlockAddress;
4572
4573    DebuggerEvaluationError error;
4574 };
4575
4576 class CodeLocation : struct
4577 {
4578    char * file;
4579    char * absoluteFile;
4580    int line;
4581
4582    CodeLocation ::ParseCodeLocation(char * location)
4583    {
4584       if(location)
4585       {
4586          char * colon = null;
4587          char * temp;
4588          char loc[MAX_LOCATION];
4589          strcpy(loc, location);
4590          for(temp = loc; temp = strstr(temp, ":"); temp++)
4591             colon = temp;
4592          if(colon)
4593          {
4594             colon[0] = '\0';
4595             colon++;
4596             if(colon)
4597             {
4598                int line = atoi(colon);
4599                if(line)
4600                {
4601                   CodeLocation codloc { line = line };
4602                   codloc.file = CopyString(loc);
4603                   codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
4604                   return codloc;
4605                }
4606             }
4607          }
4608       }
4609       return null;
4610    }
4611
4612    void Free()
4613    {
4614       delete file;
4615       delete absoluteFile;
4616    }
4617
4618    ~CodeLocation()
4619    {
4620       Free();
4621    }
4622 }
4623
4624 void GDBFallBack(Expression exp, String expString)
4625 {
4626    char * result;
4627    ExpressionType evalError = dummyExp;
4628    result = Debugger::EvaluateExpression(expString, &evalError);
4629    if(result)
4630    {
4631       exp.constant = result;
4632       exp.type = constantExp;
4633    }
4634 }