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