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