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