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