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