a37b55cc18a3b0512ee60918d7f61ccdfc856b35
[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          gdbTimer.Stop();
2560          GdbCommand(false, "-gdb-exit");
2561
2562          if(gdbThread)
2563          {
2564             app.Unlock();
2565             gdbThread.Wait();
2566             app.Lock();
2567          }
2568          if(gdbHandle)
2569          {
2570             gdbHandle.Wait();
2571             delete gdbHandle;
2572          }
2573       }
2574       gdbTimer.Stop();
2575       _ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
2576       prjConfig = null;
2577       needReset = false;
2578
2579       if(ide.workspace)
2580       {
2581          for(bp : ide.workspace.breakpoints)
2582          {
2583             bp.inserted = false;
2584             delete bp.bp;
2585          }
2586       }
2587       for(bp : sysBPs)
2588       {
2589          bp.inserted = false;
2590          delete bp.bp;
2591       }
2592       if(bpRunToCursor)
2593       {
2594          bpRunToCursor.inserted = false;
2595          delete bpRunToCursor.bp;
2596       }
2597       
2598       ide.outputView.debugBox.Logf($"Debugging stopped\n");
2599       ClearBreakDisplay();
2600       ide.Update(null);
2601
2602 #if defined(__unix__)
2603       if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
2604       {
2605          progThread.terminate = true;
2606          if(fifoFile)
2607          {
2608             fifoFile.CloseInput();
2609             app.Unlock();
2610             progThread.Wait();
2611             app.Lock();
2612             delete fifoFile;
2613          }         
2614          DeleteFile(progFifoPath);
2615          progFifoPath[0] = '\0';
2616          rmdir(progFifoDir);
2617       }
2618 #endif
2619    }
2620
2621    void WatchesCodeEditorLinkInit()
2622    {
2623       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkInit()");
2624       /*
2625       char tempPath[MAX_LOCATION];
2626       char path[MAX_LOCATION];
2627       
2628       //void MakeFilePathProjectRelative(char * path, char * relativePath)
2629       if(!ide.projectView.project.GetRelativePath(activeFrame.file, tempPath))
2630          strcpy(tempPath, activeFrame.file);
2631       
2632       strcpy(path, ide.workspace.projectDir);
2633       PathCat(path, tempPath);
2634       codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2635       if(!codeEditor)
2636       {
2637          for(srcDir : ide.workspace.sourceDirs)
2638          {
2639             strcpy(path, srcDir);
2640             PathCat(path, tempPath);
2641             codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2642             if(codeEditor) break;
2643          }
2644       }
2645       */
2646
2647       /*if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2648          activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);*/
2649       if(!activeFrame || !activeFrame.absoluteFile)
2650          codeEditor = null;
2651       else
2652          codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal, false);
2653       if(codeEditor)
2654       {
2655          codeEditor.inUseDebug = true;
2656          incref codeEditor;
2657       }
2658       //watchesInit = true;
2659    }
2660
2661    void WatchesCodeEditorLinkRelease()
2662    {
2663       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkRelease()");
2664       //if(watchesInit)
2665       {
2666          if(codeEditor)
2667          {
2668             codeEditor.inUseDebug = false;
2669             if(!codeEditor.visible)
2670                codeEditor.Destroy(0);
2671             delete codeEditor;
2672          }
2673       }
2674    }
2675
2676    bool ResolveWatch(Watch wh)
2677    {
2678       bool result = false;
2679       
2680       _dpl2(_dpct, dplchan::debuggerWatches, 0, "Debugger::ResolveWatch()");
2681       wh.Reset();
2682
2683       /*delete wh.value;
2684       if(wh.type) 
2685       {
2686          FreeType(wh.type);
2687          wh.type = null;
2688       }*/
2689
2690       if(wh.expression)
2691       {
2692          char watchmsg[MAX_F_STRING];
2693          if(state == stopped && !codeEditor)
2694             wh.value = CopyString($"No source file found for selected frame");
2695          //if(codeEditor && state == stopped || state != stopped)
2696          else
2697          {
2698             Module backupPrivateModule;
2699             Context backupContext;
2700             Class backupThisClass;
2701             Expression exp;
2702             parseError = false;
2703
2704             backupPrivateModule = GetPrivateModule();
2705             backupContext = GetCurrentContext();
2706             backupThisClass = GetThisClass();
2707             if(codeEditor)
2708             {
2709                SetPrivateModule(codeEditor.privateModule);
2710                SetCurrentContext(codeEditor.globalContext);
2711                SetTopContext(codeEditor.globalContext);
2712                SetGlobalContext(codeEditor.globalContext);
2713                SetGlobalData(&codeEditor.globalData);
2714             }
2715          
2716             exp = ParseExpressionString(wh.expression);
2717             
2718             if(exp && !parseError)
2719             {
2720                char expString[4096];
2721                expString[0] = 0;
2722                PrintExpression(exp, expString);
2723
2724                if(GetPrivateModule())
2725                {
2726                   if(codeEditor)
2727                      DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2728                   ProcessExpressionType(exp);
2729                }
2730                wh.type = exp.expType;
2731                if(wh.type)
2732                   wh.type.refCount++;
2733                DebugComputeExpression(exp);
2734                if(ExpressionIsError(exp))
2735                {
2736                   GDBFallBack(exp, expString);
2737                }
2738
2739                /*if(exp.hasAddress)
2740                {
2741                   char temp[MAX_F_STRING];
2742                   sprintf(temp, "0x%x", exp.address);
2743                   wh.address = CopyString(temp);
2744                   // wh.address = CopyStringf("0x%x", exp.address);
2745                }*/
2746 /*
2747 //#ifdef _DEBUG
2748                {
2749                   Type dataType = exp.expType;
2750                   if(dataType)
2751                   {
2752                      char temp[MAX_F_STRING];
2753                      switch(dataType.kind)
2754                      {
2755                         case charType:
2756                            sprintf(temp, "%i", exp.val.c);
2757                            break;
2758                         case shortType:
2759                            sprintf(temp, "%i", exp.val.s);
2760                            break;
2761                         case intType:
2762                         case longType:
2763                         case enumType:
2764                            sprintf(temp, "%i", exp.val.i);
2765                            break;
2766                         case int64Type:
2767                            sprintf(temp, "%i", exp.val.i64);
2768                            break;
2769                         case pointerType:
2770                            sprintf(temp, "%i", exp.val.p);
2771                            break;
2772
2773                         case floatType:
2774                         {
2775                            long v = (long)exp.val.f;
2776                            sprintf(temp, "%i", v);
2777                            break;
2778                         } 
2779                         case doubleType:
2780                         {
2781                            long v = (long)exp.val.d;
2782                            sprintf(temp, "%i", v);
2783                            break;
2784                         }
2785                      }
2786                      if(temp)
2787                         wh.intVal = CopyString(temp);
2788                      switch(dataType.kind)
2789                      {
2790                         case charType:
2791                            sprintf(temp, "0x%x", exp.val.c);
2792                            break;
2793                         case shortType:
2794                            sprintf(temp, "0x%x", exp.val.s);
2795                            break;
2796                         case enumType:
2797                         case intType:
2798                            sprintf(temp, "0x%x", exp.val.i);
2799                            break;
2800                         case int64Type:
2801                            sprintf(temp, "0x%x", exp.val.i64);
2802                            break;
2803                         case longType:
2804                            sprintf(temp, "0x%x", exp.val.i64);
2805                            break;
2806                         case pointerType:
2807                            sprintf(temp, "0x%x", exp.val.p);
2808                            break;
2809
2810                         case floatType:
2811                         {
2812                            long v = (long)exp.val.f;
2813                            sprintf(temp, "0x%x", v);
2814                            break;
2815                         } 
2816                         case doubleType:
2817                         {
2818                            long v = (long)exp.val.d;
2819                            sprintf(temp, "0x%x", v);
2820                            break;
2821                         }
2822                      }
2823                      if(temp)
2824                         wh.hexVal = CopyString(temp);
2825                      switch(dataType.kind)
2826                      {
2827                         case charType:
2828                            sprintf(temp, "0o%o", exp.val.c);
2829                            break;
2830                         case shortType:
2831                            sprintf(temp, "0o%o", exp.val.s);
2832                            break;
2833                         case enumType:
2834                         case intType:
2835                            sprintf(temp, "0o%o", exp.val.i);
2836                            break;
2837                         case int64Type:
2838                            sprintf(temp, "0o%o", exp.val.i64);
2839                            break;
2840                         case longType:
2841                            sprintf(temp, "0o%o", exp.val.i64);
2842                            break;
2843                         case pointerType:
2844                            sprintf(temp, "0o%o", exp.val.p);
2845                            break;
2846
2847                         case floatType:
2848                         {
2849                            long v = (long)exp.val.f;
2850                            sprintf(temp, "0o%o", v);
2851                            break;
2852                         } 
2853                         case doubleType:
2854                         {
2855                            long v = (long)exp.val.d;
2856                            sprintf(temp, "0o%o", v);
2857                            break;
2858                         }
2859                      }
2860                      if(temp)
2861                         wh.octVal = CopyString(temp);
2862                   }
2863                }
2864                // WHATS THIS HERE ?
2865                if(exp.type == constantExp && exp.constant)
2866                   wh.constant = CopyString(exp.constant);
2867 //#endif
2868 */
2869
2870                switch(exp.type)
2871                {
2872                   case symbolErrorExp:
2873                      snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2874                      break;
2875                   case structMemberSymbolErrorExp:
2876                      // todo get info as in next case (ExpClassMemberSymbolError)
2877                      snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2878                      break;
2879                   case classMemberSymbolErrorExp:
2880                      {
2881                         Class _class;
2882                         Expression memberExp = exp.member.exp;
2883                         Identifier memberID = exp.member.member;
2884                         Type type = memberExp.expType;
2885                         if(type)
2886                         {
2887                            _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2888                            if(!_class)
2889                            {
2890                               char string[256] = "";
2891                               Symbol classSym;
2892                               PrintTypeNoConst(type, string, false, true);
2893                               classSym = FindClass(string);
2894                               _class = classSym ? classSym.registered : null;
2895                            }
2896                            if(_class)
2897                               snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2898                            else
2899                               snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2900                         }
2901                         else
2902                            snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2903                      }
2904                      break;
2905                   case memoryErrorExp:
2906                      // Need to ensure when set to memoryErrorExp, constant is set
2907                      snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2908                      break;
2909                   case dereferenceErrorExp:
2910                      snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2911                      break;
2912                   case unknownErrorExp:
2913                      snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2914                      break;
2915                   case noDebuggerErrorExp:
2916                      snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2917                      break;
2918                   case debugStateErrorExp:
2919                      snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2920                      break;
2921                   case 0:
2922                      snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2923                      break;
2924                   case constantExp:
2925                   case stringExp:
2926                      // Temporary Code for displaying Strings
2927                      if((exp.expType && ((exp.expType.kind == pointerType || 
2928                               exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) || 
2929                            (wh.type && wh.type.kind == classType && wh.type._class && 
2930                               wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2931                               !strcmp(wh.type._class.registered.name, "String")))
2932                      {
2933
2934                         if(exp.expType.kind != arrayType || exp.hasAddress)
2935                         {
2936                            uint64 address;
2937                            char * string;
2938                            char value[4196];
2939                            int len;
2940                            //char temp[MAX_F_STRING * 32];
2941
2942                            ExpressionType evalError = dummyExp;
2943                            /*if(exp.expType.kind == arrayType)
2944                               sprintf(temp, "(char*)0x%x", exp.address);
2945                            else
2946                               sprintf(temp, "(char*)%s", exp.constant);*/
2947
2948                            //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2949                            // address = strtoul(exp.constant, null, 0);
2950                            address = _strtoui64(exp.constant, null, 0);
2951                            //_dpl(0, "0x", address);
2952                            // snprintf(value, sizeof(value), "0x%08x ", address);
2953
2954                            if(address > 0xFFFFFFFFLL)
2955                               snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2956                            else
2957                               snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2958                            value[sizeof(value)-1] = 0;
2959                            
2960                            if(!address)
2961                               strcat(value, $"Null string");
2962                            else
2963                            {
2964                               int size = 4096;
2965                               len = strlen(value);
2966                               string = null;
2967                               while(!string && size > 2)
2968                               {
2969                                  string = GdbReadMemory(address, size);
2970                                  size /= 2;
2971                               }
2972                               if(string && string[0])
2973                               {
2974                                  value[len++] = '(';
2975                                  if(UTF8Validate(string))
2976                                  {
2977                                     int c;
2978                                     char ch;
2979                                     
2980                                     for(c = 0; (ch = string[c]) && c<4096; c++)
2981                                        value[len++] = ch;                                 
2982                                     value[len++] = ')';
2983                                     value[len++] = '\0';
2984                                     
2985                                  }
2986                                  else
2987                                  {
2988                                     ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2989                                     strcat(value, ") (ISO8859-1)");
2990                                  }
2991
2992                                  delete string;
2993                               }
2994                               else if(string)
2995                               {
2996                                  strcat(value, $"Empty string");
2997                                  delete string;
2998                               }
2999                               else
3000                                  strcat(value, $"Couldn't read memory");
3001                            }
3002                            wh.value = CopyString(value);
3003                         }
3004                      }
3005                      else if(wh.type && wh.type.kind == classType && wh.type._class && 
3006                               wh.type._class.registered && wh.type._class.registered.type == enumClass)
3007                      {
3008                         uint64 value = strtoul(exp.constant, null, 0);
3009                         Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
3010                         EnumClassData enumeration = (EnumClassData)enumClass.data;
3011                         NamedLink item;
3012                         for(item = enumeration.values.first; item; item = item.next)
3013                            if((int)item.data == value)
3014                               break;
3015                         if(item)
3016                            wh.value = CopyString(item.name);
3017                         else
3018                            wh.value = CopyString($"Invalid Enum Value");
3019                         result = true;
3020                      }
3021                      else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class && 
3022                               wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
3023                      {
3024                         unichar value;
3025                         int signedValue;
3026                         char charString[5];
3027                         char string[256];
3028
3029                         if(exp.constant[0] == '\'')
3030                         {
3031                            if((int)((byte *)exp.constant)[1] > 127)
3032                            {
3033                               int nb;
3034                               value = UTF8GetChar(exp.constant + 1, &nb);
3035                               if(nb < 2) value = exp.constant[1];
3036                               signedValue = value;
3037                            }
3038                            else
3039                            {
3040                               signedValue = exp.constant[1];
3041                               {
3042                                  // Precomp Syntax error with boot strap here:
3043                                  byte b = (byte)(char)signedValue;
3044                                  value = (unichar) b;
3045                               }
3046                            }
3047                         }
3048                         else
3049                         {
3050                            if(wh.type.kind == charType && wh.type.isSigned)
3051                            {
3052                               signedValue = (int)(char)strtol(exp.constant, null, 0);
3053                               {
3054                                  // Precomp Syntax error with boot strap here:
3055                                  byte b = (byte)(char)signedValue;
3056                                  value = (unichar) b;
3057                               }
3058                            }
3059                            else
3060                            {
3061                               value = (uint)strtoul(exp.constant, null, 0);
3062                               signedValue = (int)value;
3063                            }
3064                         }
3065                         charString[0] = 0;
3066                         UTF32toUTF8Len(&value, 1, charString, 5);
3067                         if(value == '\0')
3068                            snprintf(string, sizeof(string), "\'\\0' (0)");
3069                         else if(value == '\t')
3070                            snprintf(string, sizeof(string), "\'\\t' (%d)", value);
3071                         else if(value == '\n')
3072                            snprintf(string, sizeof(string), "\'\\n' (%d)", value);
3073                         else if(value == '\r')
3074                            snprintf(string, sizeof(string), "\'\\r' (%d)", value);
3075                         else if(wh.type.kind == charType && wh.type.isSigned)
3076                            snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
3077                         else if(value > 256 || wh.type.kind != charType)
3078                         {
3079                            if(value > 0x10FFFF || !GetCharCategory(value))
3080                               snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
3081                            else
3082                               snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
3083                         }
3084                         else
3085                            snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
3086                         string[sizeof(string)-1] = 0;
3087                         
3088                         wh.value = CopyString(string);
3089                         result = true;
3090                      }
3091                      else
3092                      {
3093                         wh.value = CopyString(exp.constant);
3094                         result = true;
3095                      }
3096                      break;
3097                   default:
3098                      if(exp.hasAddress)
3099                      {
3100                         wh.value = PrintHexUInt64(exp.address);
3101                         result = true;
3102                      }
3103                      else
3104                      {
3105                         char tempString[256];
3106                         if(exp.member.memberType == propertyMember)
3107                            snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
3108                         else
3109                            snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression, 
3110                                  exp.type.OnGetString(tempString, null, null));
3111                      }
3112                      break;
3113                }
3114             }
3115             else
3116                snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
3117             if(exp) FreeExpression(exp);
3118
3119             
3120             SetPrivateModule(backupPrivateModule);
3121             SetCurrentContext(backupContext);
3122             SetTopContext(backupContext);
3123             SetGlobalContext(backupContext);
3124             SetThisClass(backupThisClass);
3125          }
3126          //else 
3127          //   wh.value = CopyString("No source file found for selected frame");
3128          
3129          watchmsg[sizeof(watchmsg)-1] = 0;
3130          if(!wh.value)
3131             wh.value = CopyString(watchmsg);
3132       }
3133       ide.watchesView.UpdateWatch(wh);
3134       return result;
3135    }
3136
3137    void EvaluateWatches()
3138    {
3139       _dpl2(_dpct, dplchan::debuggerWatches, 0, "Debugger::EvaluateWatches()");
3140       for(wh : ide.workspace.watches)
3141          ResolveWatch(wh);
3142    }
3143
3144    char * ::GdbEvaluateExpression(char * expression)
3145    {
3146       _dpl2(_dpct, dplchan::debuggerWatches, 0, "Debugger::GdbEvaluateExpression(", expression, ")");
3147       eval.active = true;
3148       eval.error = none;
3149       GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
3150       if(eval.active)
3151          ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
3152       return eval.result;
3153    }
3154
3155    // to be removed... use GdbReadMemory that returns a byte array instead
3156    char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
3157    {
3158       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemoryString(", address, ")");
3159       eval.active = true;
3160       eval.error = none;
3161 #ifdef _DEBUG
3162       if(!size)
3163          _dpl(0, "GdbReadMemoryString called with size = 0!");
3164 #endif
3165       // GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
3166       if(GetRuntimePlatform() == win32)
3167          GdbCommand(false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
3168       else
3169          GdbCommand(false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
3170       if(eval.active)
3171          ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
3172       return eval.result;
3173    }
3174
3175    byte * ::GdbReadMemory(uint64 address, int bytes)
3176    {
3177       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemory(", address, ")");
3178       eval.active = true;
3179       eval.error = none;
3180       //GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
3181       if(GetRuntimePlatform() == win32)
3182          GdbCommand(false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
3183       else
3184          GdbCommand(false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
3185 #ifdef _DEBUG
3186       if(!bytes)
3187          _dpl(0, "GdbReadMemory called with bytes = 0!");
3188 #endif
3189       if(eval.active)
3190          ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
3191       else if(eval.result && strcmp(eval.result, "N/A"))
3192       {
3193          byte * result = new byte[bytes];
3194          byte * string = eval.result;
3195          int c = 0;
3196          while(true)
3197          {
3198             result[c++] = (byte)strtol(string, &string, 10);
3199             if(string)
3200             {
3201                if(*string == ',')
3202                   string++;
3203                 else
3204                   break;
3205             }
3206             else
3207                break;
3208          }
3209          return result;
3210       }
3211       return null;
3212    }
3213
3214    bool BreakpointHit(GdbDataStop stopItem, Breakpoint bpInternal, Breakpoint bpUser)
3215    {
3216       bool result = true;
3217       char * s1 = null; char * s2 = null;
3218       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::BreakpointHit(",
3219             "bpInternal(", bpInternal ? s1=bpInternal.CopyLocationString(false) : null, "), ",
3220             "bpUser(", bpUser ? s2=bpUser.CopyLocationString(false) : null, ")) -- ",
3221             "ignoreBreakpoints(", ignoreBreakpoints, "), ",
3222             "hitCursorBreakpoint(", bpUser && bpUser.type == runToCursor,  ")");
3223       delete s1; delete s2;
3224
3225       if(bpUser && stopItem.frame.line && bpUser.line != stopItem.frame.line)
3226       {
3227          // updating user breakpoint on hit location difference
3228          // todo, print something?
3229          bpUser.line = stopItem.frame.line;
3230          ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3231          ide.workspace.Save();
3232       }
3233       if(bpInternal)
3234       {
3235          bpInternal.hits++;
3236          if(bpInternal.type == internalModulesLoaded)
3237             modules = true;
3238          if(!bpUser && !userBreakOnInternalBreakpoint)
3239          {
3240             if(userAction == stepOut)//if(prevStopItem.reason == functionFinished)
3241                StepOut(ignoreBreakpoints);
3242             else
3243                result = false;
3244          }
3245       }
3246       if(bpUser)
3247       {
3248          bool conditionMet = true;
3249          bool levelMatch = (bpUser.level == -1 || bpUser.level == frameCount-1);
3250          if(bpUser.condition)
3251             conditionMet = ResolveWatch(bpUser.condition);
3252          bpUser.hits++;
3253          if(levelMatch && conditionMet)
3254          {
3255             if(!bpUser.ignore)
3256                bpUser.breaks++;
3257             else
3258             {
3259                bpUser.ignore--;
3260                result = false;
3261             }
3262          }
3263          else
3264             result = false;
3265          ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3266       }
3267
3268       if(!bpUser && !bpInternal)
3269          result = false;
3270
3271       return result;
3272    }
3273
3274    void ValgrindTargetThreadExit()
3275    {
3276       ide.outputView.debugBox.Logf($"ValgrindTargetThreadExit\n");
3277       if(vgTargetHandle)
3278       {
3279          vgTargetHandle.Wait();
3280          delete vgTargetHandle;
3281       }
3282       HandleExit(null, null);
3283    }
3284
3285    void GdbThreadExit()
3286    {
3287       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbThreadExit()");
3288       if(state != terminated)
3289       {
3290          _ChangeState(terminated);
3291          targetProcessId = 0;
3292          ClearBreakDisplay();
3293
3294          if(vgLogFile)
3295             delete vgLogFile;
3296          if(gdbHandle)
3297          {
3298             serialSemaphore.Release();
3299             gdbTimer.Stop();
3300             gdbHandle.Wait();
3301             delete gdbHandle;
3302             
3303             ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
3304             ide.outputView.debugBox.Logf($"Debugging stopped\n");
3305             ide.Update(null);
3306             HideDebuggerViews();
3307          }
3308          //_ChangeState(terminated);
3309       }
3310    }
3311
3312    void GdbThreadMain(char * output)
3313    {
3314       int i;
3315       char * t;
3316       Array<char *> outTokens { minAllocSize = 50 };
3317       Array<char *> subTokens { minAllocSize = 50 };
3318       DebugListItem item { };
3319       DebugListItem item2 { };
3320       bool setWaitingForPID = false;
3321       
3322 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
3323 #ifdef GDB_DEBUG_CONSOLE
3324       _dpl2(_dpct, dplchan::gdbOutput, 0, output);
3325 #endif
3326 #ifdef GDB_DEBUG_OUTPUT
3327       {
3328          int len = strlen(output);
3329          if(len > 1024)
3330          {
3331             int c;
3332             char * start;
3333             char tmp[1025];
3334             tmp[1024] = '\0';
3335             start = output;
3336             for(c = 0; c < len / 1024; c++)
3337             {
3338                strncpy(tmp, start, 1024);
3339                ide.outputView.gdbBox.Logf("out: %s\n", tmp);
3340                start += 1024;
3341             }
3342             ide.outputView.gdbBox.Logf("out: %s\n", start);
3343          }
3344          else
3345          {
3346             ide.outputView.gdbBox.Logf("out: %s\n", output);
3347          }
3348       }
3349 #endif
3350 #ifdef GDB_DEBUG_CONSOLE
3351          strcpy(lastGdbOutput, output);
3352 #endif
3353 #ifdef GDB_DEBUG_GUI
3354          if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
3355 #endif
3356 #endif
3357       
3358       switch(output[0])
3359       {
3360          case '~':
3361             if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
3362             {
3363                symbols = false;
3364                ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
3365                ide.Update(null);
3366             }
3367             if(!entryPoint && (t = strstr(output, "Entry point:")))
3368             {
3369                char * addr = t + strlen("Entry point:");
3370                t = addr;
3371                if(*t++ == ' ' && *t++ == '0' && *t == 'x')
3372                {
3373                   *addr = '*';
3374                   while(isxdigit(*++t));
3375                   *t = '\0';
3376                   for(bp : sysBPs; bp.type == internalEntry)
3377                   {
3378                      bp.function = addr;
3379                      bp.enabled = entryPoint = true;
3380                      break;
3381                   }
3382                }
3383             }
3384             break;
3385          case '^':
3386             gdbReady = false;
3387             if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
3388             {
3389                //if(outTokens.count == 1)
3390                {
3391                   if(sentKill)
3392                   {
3393                      sentKill = false;
3394                      _ChangeState(loaded);
3395                      targetProcessId = 0;
3396                      if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3397                      {
3398                         if(!strcmp(item.name, "reason"))
3399                         {
3400                            char * reason = item.value;
3401                            StripQuotes(reason, reason);
3402                            if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3403                            {
3404                               char * exitCode;
3405                               if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
3406                               {
3407                                  StripQuotes(item2.value, item2.value);
3408                                  if(!strcmp(item2.name, "exit-code"))
3409                                     exitCode = item2.value;
3410                                  else
3411                                     exitCode = null;
3412                               }
3413                               else
3414                                  exitCode = null;
3415                               HandleExit(reason, exitCode);
3416                            }
3417                         }
3418                         else
3419                            _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "kill reply (", item.name, "=", item.value, ") is unheard of");
3420                      }
3421                      else
3422                         HandleExit(null, null);
3423                   }
3424                }
3425                if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3426                {
3427                   if(!strcmp(item.name, "bkpt"))
3428                   {
3429                      sentBreakInsert = false;
3430 #ifdef _DEBUG
3431                      if(bpItem)
3432                         _dpl(0, "problem");
3433 #endif
3434                      bpItem = ParseBreakpoint(item.value, outTokens);
3435                      //breakType = bpValidation;
3436                   }
3437                   else if(!strcmp(item.name, "depth"))
3438                   {
3439                      StripQuotes(item.value, item.value);
3440                      frameCount = atoi(item.value);
3441                      activeFrame = null;
3442                      stackFrames.Free(Frame::Free);
3443                   }
3444                   else if(!strcmp(item.name, "stack"))
3445                   {
3446                      Frame frame;
3447                      if(stackFrames.count)
3448                         ide.callStackView.Logf("...\n");
3449                      else
3450                         activeFrame = null;
3451                      item.value = StripBrackets(item.value);
3452                      TokenizeList(item.value, ',', subTokens);
3453                      for(i = 0; i < subTokens.count; i++)
3454                      {
3455                         if(TokenizeListItem(subTokens[i], item))
3456                         {
3457                            if(!strcmp(item.name, "frame"))
3458                            {
3459                               frame = Frame { };
3460                               stackFrames.Add(frame);
3461                               item.value = StripCurlies(item.value);
3462                               ParseFrame(frame, item.value);
3463                               if(frame.file && frame.from)
3464                                  _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "unexpected frame file and from members present");
3465                               if(frame.file)
3466                               {
3467                                  char * s;
3468                                  if(activeFrameLevel == -1)
3469                                  {
3470                                     if(ide.projectView.IsModuleInProject(frame.file));
3471                                     {
3472                                        if(frame.level != 0)
3473                                        {
3474                                           //stopItem.frame = frame;
3475                                           breakType = selectFrame;
3476                                        }
3477                                        else
3478                                           activeFrame = frame;
3479                                        activeFrameLevel = frame.level;
3480                                     }
3481                                  }
3482                                  ide.callStackView.Logf("%3d ", frame.level);
3483                                  if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
3484                                     ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
3485                                  else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
3486                                     ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
3487                                  else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
3488                                     ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
3489                                  else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
3490                                     ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
3491                                  else
3492                                     ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
3493                                  delete s;
3494                               }
3495                               else
3496                               {
3497                                  ide.callStackView.Logf("%3d ", frame.level);
3498
3499                                  if(frame.from)
3500                                  {
3501                                     char * s;
3502                                     ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
3503                                     delete s;
3504                                  }
3505                                  else if(frame.func)
3506                                     ide.callStackView.Logf("%s\n", frame.func);
3507                                  else
3508                                     ide.callStackView.Logf($"unknown source\n");
3509                               }
3510                            }
3511                            else
3512                               _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "stack content (", item.name, "=", item.value, ") is unheard of");
3513                         }
3514                      }
3515                      if(activeFrameLevel == -1)
3516                      {
3517                         activeFrameLevel = 0;
3518                         activeFrame = stackFrames.first;
3519                      }
3520                      ide.callStackView.Home();
3521                      ide.Update(null);
3522                      subTokens.RemoveAll();
3523                   }
3524                   /*else if(!strcmp(item.name, "frame"))
3525                   {
3526                      Frame frame { };
3527                      item.value = StripCurlies(item.value);
3528                      ParseFrame(&frame, item.value);
3529                   }*/
3530                   else if(!strcmp(item.name, "thread-ids"))
3531                   {
3532                      ide.threadsView.Clear();
3533                      item.value = StripCurlies(item.value);
3534                      TokenizeList(item.value, ',', subTokens);
3535                      for(i = subTokens.count - 1; ; i--)
3536                      {
3537                         if(TokenizeListItem(subTokens[i], item))
3538                         {
3539                            if(!strcmp(item.name, "thread-id"))
3540                            {
3541                               int value;
3542                               StripQuotes(item.value, item.value);
3543                               value = atoi(item.value);
3544                               ide.threadsView.Logf("%3d \n", value);
3545                            }
3546                            else
3547                               _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "threads content (", item.name, "=", item.value, ") is unheard of");
3548                         }
3549                         if(!i)
3550                            break;
3551                      }
3552                      ide.threadsView.Home();
3553                      ide.Update(null);
3554                      subTokens.RemoveAll();
3555                      //if(!strcmp(outTokens[2], "number-of-threads"))
3556                   }
3557                   else if(!strcmp(item.name, "new-thread-id"))
3558                   {
3559                      StripQuotes(item.value, item.value);
3560                      activeThread = atoi(item.value);
3561                   }
3562                   else if(!strcmp(item.name, "value"))
3563                   {
3564                      StripQuotes(item.value, item.value);
3565                      eval.result = CopyString(item.value);
3566                      eval.active = false;
3567                   }
3568                   else if(!strcmp(item.name, "addr"))
3569                   {
3570                      for(i = 2; i < outTokens.count; i++)
3571                      {
3572                         if(TokenizeListItem(outTokens[i], item))
3573                         {
3574                            if(!strcmp(item.name, "total-bytes"))
3575                            {
3576                               StripQuotes(item.value, item.value);
3577                               eval.bytes = atoi(item.value);
3578                            }
3579                            else if(!strcmp(item.name, "next-row"))
3580                            {
3581                               StripQuotes(item.value, item.value);
3582                               eval.nextBlockAddress = _strtoui64(item.value, null, 0);
3583                            }
3584                            else if(!strcmp(item.name, "memory"))
3585                            {
3586                               int j;
3587                               //int value;
3588                               //StripQuotes(item.value, item.value);
3589                               item.value = StripBrackets(item.value);
3590                               // this should be treated as a list...
3591                               item.value = StripCurlies(item.value);
3592                               TokenizeList(item.value, ',', subTokens);
3593                               for(j = 0; j < subTokens.count; j++)
3594                               {
3595                                  if(TokenizeListItem(subTokens[j], item))
3596                                  {
3597                                     if(!strcmp(item.name, "data"))
3598                                     {
3599                                        item.value = StripBrackets(item.value);
3600                                        StripQuotes2(item.value, item.value);
3601                                        eval.result = CopyString(item.value);
3602                                        eval.active = false;
3603                                     }
3604                                  }
3605                               }
3606                               subTokens.RemoveAll();
3607                            }
3608                         }
3609                      }
3610                   }
3611                   else if(!strcmp(item.name, "source-path") || !strcmp(item.name, "BreakpointTable"))
3612                      _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "command reply (", item.name, "=", item.value, ") is ignored");
3613                   else
3614                      _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "command reply (", item.name, "=", item.value, ") is unheard of");
3615                }
3616             }
3617             else if(!strcmp(outTokens[0], "^running"))
3618             {
3619                waitingForPID = true;
3620                setWaitingForPID = true;
3621                ClearBreakDisplay();
3622             }
3623             else if(!strcmp(outTokens[0], "^exit"))
3624             {
3625                _ChangeState(terminated);
3626                // ide.outputView.debugBox.Logf("Exit\n");
3627                // ide.Update(null);
3628                gdbReady = true;
3629                serialSemaphore.Release();
3630             }
3631             else if(!strcmp(outTokens[0], "^error"))
3632             {
3633                if(sentBreakInsert)
3634                {
3635                   sentBreakInsert = false;
3636                   breakpointError = true;
3637                }
3638
3639                if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3640                {
3641                   if(!strcmp(item.name, "msg"))
3642                   {
3643                      StripQuotes(item.value, item.value);
3644                      if(eval.active)
3645                      {
3646                         eval.active = false;
3647                         eval.result = null;
3648                         if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3649                            eval.error = symbolNotFound;
3650                         else if(strstr(item.value, "Cannot access memory at address"))
3651                            eval.error = memoryCantBeRead;
3652                         else
3653                            eval.error = unknown;
3654                      }
3655                      else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3656                      {
3657                      }
3658                      else if(!strncmp(item.value, "Cannot access memory at address", 31))
3659                      {
3660                      }
3661                      else if(!strcmp(item.value, "Cannot find bounds of current function"))
3662                      {
3663                         _ChangeState(stopped);
3664                         gdbHandle.Printf("-exec-continue\n");
3665                      }
3666                      else if(!strcmp(item.value, "ptrace: No such process."))
3667                      {
3668                         _ChangeState(loaded);
3669                         targetProcessId = 0;
3670                      }
3671                      else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3672                      {
3673                      }
3674                      else if(!strcmp(item.value, "You can't do that without a process to debug."))
3675                      {
3676                         _ChangeState(loaded);
3677                         targetProcessId = 0;
3678                      }
3679                      else if(strstr(item.value, "No such file or directory."))
3680                      {
3681                         _ChangeState(loaded);
3682                         targetProcessId = 0;
3683                      }
3684                      else if(strstr(item.value, "During startup program exited with code "))
3685                      {
3686                         _ChangeState(loaded);
3687                         targetProcessId = 0;
3688                      }
3689                      else
3690                      {
3691 #ifdef _DEBUG
3692                         if(strlen(item.value) < MAX_F_STRING)
3693                         {
3694                            char * s;
3695                            ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3696                            delete s;
3697                         }
3698                         else
3699                            ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3700 #endif
3701                      }
3702                   }
3703                }
3704                else
3705                   _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "error content (", item.name, "=", item.value, ") is unheard of");
3706             }
3707             else
3708                _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "result-record: ", outTokens[0]);
3709             outTokens.RemoveAll();
3710             break;
3711          case '+':
3712             _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "status-async-output: ", outTokens[0]);
3713             break;
3714          case '=':
3715             if(TokenizeList(output, ',', outTokens))
3716             {
3717                if(!strcmp(outTokens[0], "=library-loaded"))
3718                   FGODetectLoadedLibraryForAddedProjectIssues(outTokens);
3719                else if(!strcmp(outTokens[0], "=thread-group-created") || !strcmp(outTokens[0], "=thread-group-added") ||
3720                         !strcmp(outTokens[0], "=thread-group-started") || !strcmp(outTokens[0], "=thread-group-exited") ||
3721                         !strcmp(outTokens[0], "=thread-created") || !strcmp(outTokens[0], "=thread-exited") ||
3722                         !strcmp(outTokens[0], "=cmd-param-changed") || !strcmp(outTokens[0], "=library-unloaded") ||
3723                         !strcmp(outTokens[0], "=breakpoint-modified"))
3724                   _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, outTokens[0], outTokens.count>1 ? outTokens[1] : "",
3725                            outTokens.count>2 ? outTokens[2] : "", outTokens.count>3 ? outTokens[3] : "",
3726                            outTokens.count>4 ? outTokens[4] : "", outTokens.count>5 ? outTokens[5] : "",
3727                            outTokens.count>6 ? outTokens[6] : "", outTokens.count>7 ? outTokens[7] : "",
3728                            outTokens.count>8 ? outTokens[8] : "", outTokens.count>9 ? outTokens[9] : "");
3729                else
3730                   _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "notify-async-output: ", outTokens[0]);
3731             }
3732             outTokens.RemoveAll();
3733             break;
3734          case '*':
3735             gdbReady = false;
3736             if(TokenizeList(output, ',', outTokens))
3737             {
3738                if(!strcmp(outTokens[0],"*running"))
3739                {
3740                   waitingForPID = true;
3741                   setWaitingForPID = true;
3742                }
3743                else if(!strcmp(outTokens[0], "*stopped"))
3744                {
3745                   int tk;
3746                   _ChangeState(stopped);
3747
3748                   for(tk = 1; tk < outTokens.count; tk++)
3749                   {
3750                      if(TokenizeListItem(outTokens[tk], item))
3751                      {
3752                         if(!strcmp(item.name, "reason"))
3753                         {
3754                            char * reason = item.value;
3755                            StripQuotes(reason, reason);
3756                            if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3757                            {
3758                               char * exitCode;
3759                               if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3760                               {
3761                                  tk++;
3762                                  StripQuotes(item2.value, item2.value);
3763                                  if(!strcmp(item2.name, "exit-code"))
3764                                     exitCode = item2.value;
3765                                  else
3766                                     exitCode = null;
3767                               }
3768                               else
3769                                  exitCode = null;
3770                               HandleExit(reason, exitCode);
3771                               needReset = true;
3772                            }
3773                            else if(!strcmp(reason, "breakpoint-hit") ||
3774                                    !strcmp(reason, "function-finished") ||
3775                                    !strcmp(reason, "end-stepping-range") ||
3776                                    !strcmp(reason, "location-reached") ||
3777                                    !strcmp(reason, "signal-received"))
3778                            {
3779                               char r = reason[0];
3780 #ifdef _DEBUG
3781                               if(stopItem) _dpl(0, "problem");
3782 #endif
3783                               stopItem = GdbDataStop { };
3784                               stopItem.reason = r == 'b' ? breakpointHit : r == 'f' ? functionFinished : r == 'e' ? endSteppingRange : r == 'l' ? locationReached : signalReceived;
3785
3786                               for(i = tk+1; i < outTokens.count; i++)
3787                               {
3788                                  TokenizeListItem(outTokens[i], item);
3789                                  StripQuotes(item.value, item.value);
3790                                  if(!strcmp(item.name, "thread-id"))
3791                                     stopItem.threadid = atoi(item.value);
3792                                  else if(!strcmp(item.name, "frame"))
3793                                  {
3794                                     item.value = StripCurlies(item.value);
3795                                     ParseFrame(stopItem.frame, item.value);
3796                                  }
3797                                  else if(stopItem.reason == breakpointHit && !strcmp(item.name, "bkptno"))
3798                                     stopItem.bkptno = atoi(item.value);
3799                                  else if(stopItem.reason == functionFinished && !strcmp(item.name, "gdb-result-var"))
3800                                     stopItem.gdbResultVar = CopyString(item.value);
3801                                  else if(stopItem.reason == functionFinished && !strcmp(item.name, "return-value"))
3802                                     stopItem.returnValue = CopyString(item.value);
3803                                  else if(stopItem.reason == signalReceived && !strcmp(item.name, "signal-name"))
3804                                     stopItem.name = CopyString(item.value);
3805                                  else if(stopItem.reason == signalReceived && !strcmp(item.name, "signal-meaning"))
3806                                     stopItem.meaning = CopyString(item.value);
3807                                  else if(!strcmp(item.name, "stopped-threads"))
3808                                     _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, reason, ": Advanced thread debugging not handled");
3809                                  else if(!strcmp(item.name, "core"))
3810                                     _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, reason, ": Information (core) not used");
3811                                  else if(!strcmp(item.name, "disp"))
3812                                     _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, reason, ": (", item.name, "=", item.value, ")");
3813                                  else
3814                                     _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown ", reason, " item name (", item.name, "=", item.value, ")");
3815                               }
3816
3817                               if(stopItem.reason == signalReceived && !strcmp(stopItem.name, "SIGTRAP"))
3818                               {
3819                                  switch(breakType)
3820                                  {
3821                                     case internal:
3822                                        breakType = none;
3823                                        break;
3824                                     case restart:
3825                                     case stop:
3826                                        break;
3827                                     default:
3828                                        event = breakEvent;
3829                                  }
3830                               }
3831                               else
3832                               {
3833                                  event = r == 'b' ? hit : r == 'f' ? functionEnd : r == 'e' ? stepEnd : r == 'l' ? locationReached : signal;
3834                                  ide.Update(null);
3835                               }
3836                            }
3837                            else if(!strcmp(reason, "watchpoint-trigger"))
3838                               _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint trigger not handled");
3839                            else if(!strcmp(reason, "read-watchpoint-trigger"))
3840                               _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason read watchpoint trigger not handled");
3841                            else if(!strcmp(reason, "access-watchpoint-trigger"))
3842                               _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason access watchpoint trigger not handled");
3843                            else if(!strcmp(reason, "watchpoint-scope"))
3844                               _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint scope not handled");
3845                            else
3846                               _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown reason: ", reason);
3847                         }
3848                         else
3849                         {
3850                            PrintLn(output);
3851                         }
3852                      }
3853                   }
3854                   if(usingValgrind && event == none && !stopItem)
3855                      event = valgrindStartPause;
3856                   app.SignalEvent();
3857                }
3858             }
3859             else
3860                _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown exec-async-output: ", outTokens[0]);
3861             outTokens.RemoveAll();
3862             break;
3863          case '(':
3864             if(!strcmpi(output, "(gdb) "))
3865             {
3866                if(waitingForPID)
3867                {
3868                   char exeFile[MAX_LOCATION];
3869                   int oldProcessID = targetProcessId;
3870                   GetLastDirectory(targetFile, exeFile);
3871
3872                   while(!targetProcessId/*true*/)
3873                   {
3874                      targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3875                      if(targetProcessId || gdbHandle.Peek()) break;
3876                      Sleep(0.01);
3877                   }
3878
3879                   if(targetProcessId)
3880                      _ChangeState(running);
3881                   else if(!oldProcessID)
3882                   {
3883                      ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3884                      // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3885                      gdbHandle.Printf("-gdb-exit\n");
3886                      gdbTimer.Stop();
3887                      _ChangeState(terminated); //loaded;
3888                      prjConfig = null;
3889
3890                      if(ide.workspace)
3891                      {
3892                         for(bp : ide.workspace.breakpoints)
3893                            bp.inserted = false;
3894                      }
3895                      for(bp : sysBPs)
3896                         bp.inserted = false;
3897                      if(bpRunToCursor)
3898                         bpRunToCursor.inserted = false;
3899
3900                      ide.outputView.debugBox.Logf($"Debugging stopped\n");
3901                      ClearBreakDisplay();
3902
3903                #if defined(__unix__)
3904                      if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
3905                      {
3906                         progThread.terminate = true;
3907                         if(fifoFile)
3908                         {
3909                            fifoFile.CloseInput();
3910                            app.Unlock();
3911                            progThread.Wait();
3912                            app.Lock();
3913                            delete fifoFile;
3914                         }
3915
3916                         DeleteFile(progFifoPath);
3917                         progFifoPath[0] = '\0';
3918                         rmdir(progFifoDir);
3919                      }
3920                #endif
3921                   }
3922                }
3923                gdbReady = true;
3924                serialSemaphore.Release();
3925             }
3926             else
3927                _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown prompt", output);
3928
3929             break;
3930          case '&':
3931             if(!strncmp(output, "&\"warning:", 10))
3932             {
3933                char * content;
3934                content = strstr(output, "\"");
3935                StripQuotes(content, content);
3936                content = strstr(content, ":");
3937                if(content)
3938                   content++;
3939                if(content)
3940                {
3941                   char * s;
3942                   ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3943                   delete s;
3944                   ide.Update(null);
3945                }
3946             }
3947             break;
3948          default:
3949             _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown output: ", output);
3950       }
3951       if(!setWaitingForPID)
3952          waitingForPID = false;
3953       setWaitingForPID = false;
3954
3955       delete outTokens;
3956       delete subTokens;
3957       delete item;
3958       delete item2;
3959    }
3960
3961    // From GDB Output functions
3962    void FGODetectLoadedLibraryForAddedProjectIssues(Array<char *> outTokens)
3963    {
3964       char path[MAX_LOCATION] = "";
3965       char file[MAX_FILENAME] = "";
3966       bool symbolsLoaded;
3967       DebugListItem item { };
3968       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGODetectLoadedLibraryForAddedProjectIssues()");
3969       for(token : outTokens)
3970       {
3971          if(TokenizeListItem(token, item))
3972          {
3973             if(!strcmp(item.name, "target-name"))
3974             {
3975                StripQuotes(item.value, path);
3976                MakeSystemPath(path);
3977                GetLastDirectory(path, file);
3978             }
3979             else if(!strcmp(item.name, "symbols-loaded"))
3980             {
3981                symbolsLoaded = (atoi(item.value) == 1);
3982             }
3983          }
3984       }
3985       delete item;
3986       if(path[0] && file[0])
3987       {
3988          for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
3989          {
3990             bool match;
3991             char * dot;
3992             char prjTargetPath[MAX_LOCATION];
3993             char prjTargetFile[MAX_FILENAME];
3994             DirExpression targetDirExp = prj.GetTargetDir(currentCompiler, prj.config, bitDepth);
3995             strcpy(prjTargetPath, prj.topNode.path);
3996             PathCat(prjTargetPath, targetDirExp.dir);
3997             prjTargetFile[0] = '\0';
3998             prj.CatTargetFileName(prjTargetFile, currentCompiler, prj.config);
3999             PathCat(prjTargetPath, prjTargetFile);
4000             MakeSystemPath(prjTargetPath);
4001
4002             match = !fstrcmp(prjTargetFile, file);
4003             if(!match && (dot = strstr(prjTargetFile, ".so.")))
4004             {
4005                char * dot3 = strstr(dot+4, ".");
4006                if(dot3)
4007                {
4008                   dot3[0] = '\0';
4009                   match = !fstrcmp(prjTargetFile, file);
4010                }
4011                if(!match)
4012                {
4013                   dot[3] = '\0';
4014                   match = !fstrcmp(prjTargetFile, file);
4015                }
4016             }
4017             if(match)
4018             {
4019                // TODO: nice visual feedback to better warn user. use some ide notification system or other means.
4020                /* -- 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)
4021                if(!symbolsLoaded)
4022                   ide.outputView.debugBox.Logf($"Attention! No symbols for loaded library %s matched to the %s added project.\n", path, prj.topNode.name);
4023                */
4024                match = !fstrcmp(prjTargetPath, path);
4025                if(!match && (dot = strstr(prjTargetPath, ".so.")))
4026                {
4027                   char * dot3 = strstr(dot+4, ".");
4028                   if(dot3)
4029                   {
4030                      dot3[0] = '\0';
4031                      match = !fstrcmp(prjTargetPath, path);
4032                   }
4033                   if(!match)
4034                   {
4035                      dot[3] = '\0';
4036                      match = !fstrcmp(prjTargetPath, path);
4037                   }
4038                }
4039                if(match)
4040                   projectsLibraryLoaded[prj.name] = true;
4041                else
4042                   ide.outputView.debugBox.Logf($"Loaded library %s doesn't match the %s target of the %s added project.\n", path, prjTargetPath, prj.topNode.name);
4043                break;
4044             }
4045          }
4046       }
4047    }
4048
4049    void FGOBreakpointModified(Array<char *> outTokens)
4050    {
4051       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGOBreakpointModified() -- TODO only if needed: support breakpoint modified");
4052 #if 0
4053       DebugListItem item { };
4054       if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
4055       {
4056          if(!strcmp(item.name, "bkpt"))
4057          {
4058             GdbDataBreakpoint modBp = ParseBreakpoint(item.value, outTokens);
4059             delete modBp;
4060          }
4061       }
4062 #endif
4063    }
4064
4065
4066    ExpressionType ::DebugEvalExpTypeError(char * result)
4067    {
4068       _dpl2(_dpct, dplchan::debuggerWatches, 0, "Debugger::DebugEvalExpTypeError()");
4069       if(result)
4070          return dummyExp;
4071       switch(eval.error)
4072       {
4073          case symbolNotFound:
4074             return symbolErrorExp;
4075          case memoryCantBeRead:
4076             return memoryErrorExp;
4077       }
4078       return unknownErrorExp;
4079    }
4080
4081    char * ::EvaluateExpression(char * expression, ExpressionType * error)
4082    {
4083       char * result;
4084       _dpl2(_dpct, dplchan::debuggerWatches, 0, "Debugger::EvaluateExpression(", expression, ")");
4085       if(ide.projectView && ide.debugger.state == stopped)
4086       {
4087          result = GdbEvaluateExpression(expression);
4088          *error = DebugEvalExpTypeError(result);
4089       }
4090       else
4091       {
4092          result = null;
4093          *error = noDebuggerErrorExp;
4094       }
4095       return result;
4096    }
4097
4098    char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
4099    {
4100       // check for state
4101       char * result = GdbReadMemoryString(address, size, format, 1, 1);
4102       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ReadMemory(", address, ")");
4103       if(!result || !strcmp(result, "N/A"))
4104          *error = memoryErrorExp;
4105       else
4106          *error = DebugEvalExpTypeError(result);
4107       return result;
4108    }
4109 }
4110
4111 class ValgrindLogThread : Thread
4112 {
4113    Debugger debugger;
4114
4115    unsigned int Main()
4116    {
4117       static char output[4096];
4118       Array<char> dynamicBuffer { minAllocSize = 4096 };
4119       File oldValgrindHandle = vgLogFile;
4120       incref oldValgrindHandle;
4121
4122       app.Lock();
4123       while(debugger.state != terminated && vgLogFile)
4124       {
4125          int result;
4126          app.Unlock();
4127          result = vgLogFile.Read(output, 1, sizeof(output));
4128          app.Lock();
4129          if(debugger.state == terminated || !vgLogFile/* || vgLogFile.Eof()*/)
4130             break;
4131          if(result)
4132          {
4133             int c;
4134             int start = 0;
4135
4136             for(c = 0; c<result; c++)
4137             {
4138                if(output[c] == '\n')
4139                {
4140                   int pos = dynamicBuffer.size;
4141                   dynamicBuffer.size += c - start;
4142                   memcpy(&dynamicBuffer[pos], output + start, c - start);
4143                   if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4144                   // COMMENTED OUT DUE TO ISSUE #135, FIXED
4145                   //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4146                      dynamicBuffer.size++;
4147                   dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4148 #ifdef _DEBUG
4149                   // printf("%s\n", dynamicBuffer.array);
4150 #endif
4151                   if(strstr(&dynamicBuffer[0], "vgdb me"))
4152                      debugger.serialSemaphore.Release();
4153                   ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4154                   dynamicBuffer.size = 0;
4155                   start = c + 1;
4156                }
4157             }
4158             if(c == result)
4159             {
4160                int pos = dynamicBuffer.size;
4161                dynamicBuffer.size += c - start;
4162                memcpy(&dynamicBuffer[pos], output + start, c - start);
4163             }
4164          }
4165          else if(debugger.state == stopped)
4166          {
4167 /*#ifdef _DEBUG
4168             printf("Got end of file from GDB!\n");
4169 #endif*/
4170             app.Unlock();
4171             Sleep(0.2);
4172             app.Lock();
4173          }
4174       }
4175       delete dynamicBuffer;
4176       ide.outputView.debugBox.Logf($"ValgrindLogThreadExit\n");
4177       //if(oldValgrindHandle == vgLogFile)
4178          debugger.GdbThreadExit/*ValgrindLogThreadExit*/();
4179       delete oldValgrindHandle;
4180       app.Unlock();
4181       return 0;
4182    }
4183 }
4184
4185 class ValgrindTargetThread : Thread
4186 {
4187    Debugger debugger;
4188
4189    unsigned int Main()
4190    {
4191       static char output[4096];
4192       Array<char> dynamicBuffer { minAllocSize = 4096 };
4193       DualPipe oldValgrindHandle = vgTargetHandle;
4194       incref oldValgrindHandle;
4195
4196       app.Lock();
4197       while(debugger.state != terminated && vgTargetHandle && !vgTargetHandle.Eof())
4198       {
4199          int result;
4200          app.Unlock();
4201          result = vgTargetHandle.Read(output, 1, sizeof(output));
4202          app.Lock();
4203          if(debugger.state == terminated || !vgTargetHandle || vgTargetHandle.Eof())
4204             break;
4205          if(result)
4206          {
4207             int c;
4208             int start = 0;
4209
4210             for(c = 0; c<result; c++)
4211             {
4212                if(output[c] == '\n')
4213                {
4214                   int pos = dynamicBuffer.size;
4215                   dynamicBuffer.size += c - start;
4216                   memcpy(&dynamicBuffer[pos], output + start, c - start);
4217                   if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4218                   // COMMENTED OUT DUE TO ISSUE #135, FIXED
4219                   //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4220                      dynamicBuffer.size++;
4221                   dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4222 #ifdef _DEBUG
4223                   // printf("%s\n", dynamicBuffer.array);
4224 #endif
4225                   ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4226
4227                   dynamicBuffer.size = 0;
4228                   start = c + 1;
4229                }
4230             }
4231             if(c == result)
4232             {
4233                int pos = dynamicBuffer.size;
4234                dynamicBuffer.size += c - start;
4235                memcpy(&dynamicBuffer[pos], output + start, c - start);
4236             }
4237          }
4238          else
4239          {
4240 #ifdef _DEBUG
4241             printf("Got end of file from GDB!\n");
4242 #endif
4243          }
4244       }
4245       delete dynamicBuffer;
4246       //if(oldValgrindHandle == vgTargetHandle)
4247          debugger.ValgrindTargetThreadExit();
4248       delete oldValgrindHandle;
4249       app.Unlock();
4250       return 0;
4251    }
4252 }
4253
4254 class GdbThread : Thread
4255 {
4256    Debugger debugger;
4257
4258    unsigned int Main()
4259    {
4260       static char output[4096];
4261       Array<char> dynamicBuffer { minAllocSize = 4096 };
4262       DualPipe oldGdbHandle = gdbHandle;
4263       incref oldGdbHandle;
4264
4265       app.Lock();
4266       while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
4267       {
4268          int result;
4269          app.Unlock();
4270          result = gdbHandle.Read(output, 1, sizeof(output));
4271          app.Lock();
4272          if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
4273             break;
4274          if(result)
4275          {
4276             int c;
4277             int start = 0;
4278
4279             for(c = 0; c<result; c++)
4280             {
4281                if(output[c] == '\n')
4282                {
4283                   int pos = dynamicBuffer.size;
4284                   dynamicBuffer.size += c - start;
4285                   memcpy(&dynamicBuffer[pos], output + start, c - start);
4286                   if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4287                   // COMMENTED OUT DUE TO ISSUE #135, FIXED
4288                   //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4289                      dynamicBuffer.size++;
4290                   dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4291 #ifdef _DEBUG
4292                   // _dpl(0, dynamicBuffer.array);
4293 #endif
4294                   debugger.GdbThreadMain(&dynamicBuffer[0]);
4295                   dynamicBuffer.size = 0;
4296                   start = c + 1;
4297                }
4298             }
4299             if(c == result)
4300             {
4301                int pos = dynamicBuffer.size;
4302                dynamicBuffer.size += c - start;
4303                memcpy(&dynamicBuffer[pos], output + start, c - start);
4304             }
4305          }
4306          else
4307          {
4308 #ifdef _DEBUG
4309             _dpl(0, "Got end of file from GDB!");
4310 #endif
4311          }
4312       }
4313       delete dynamicBuffer;
4314       //if(oldGdbHandle == gdbHandle)
4315          debugger.GdbThreadExit();
4316       delete oldGdbHandle;
4317       app.Unlock();
4318       return 0;
4319    }
4320 }
4321
4322 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
4323 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
4324
4325 #if defined(__unix__)
4326 #define uint _uint
4327 #include <errno.h>
4328 #include <stdio.h>
4329 #include <fcntl.h>
4330 #include <sys/types.h>
4331 #undef uint
4332
4333 File fifoFile;
4334
4335 class ProgramThread : Thread
4336 {
4337    bool terminate;
4338    unsigned int Main()
4339    {
4340       bool result = true;
4341       bool fileCreated = false;
4342       mode_t mask = 0600;
4343       static char output[1000];
4344       int fd;
4345
4346       /*if(!mkfifo(progFifoPath, mask))
4347       {
4348          fileCreated = true;
4349       }
4350       else
4351       {
4352          app.Lock();
4353          ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
4354          app.Unlock();
4355       }*/
4356
4357       if(FileExists(progFifoPath)) //fileCreated)
4358       {
4359          fifoFile = FileOpen(progFifoPath, read);
4360          if(!fifoFile)
4361          {
4362             app.Lock();
4363             ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
4364             app.Unlock();
4365          }
4366          else
4367          {
4368             fd = fileno((FILE *)fifoFile.input);
4369             //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
4370          }
4371       }
4372
4373       while(!terminate && fifoFile && !fifoFile.Eof())
4374       {
4375          fd_set rs, es;
4376          struct timeval time;
4377          int selectResult;
4378          time.tv_sec = 1;
4379          time.tv_usec = 0;
4380          FD_ZERO(&rs);
4381          FD_ZERO(&es);
4382          FD_SET(fd, &rs);
4383          FD_SET(fd, &es);
4384          selectResult = select(fd + 1, &rs, null, null, &time);
4385          if(FD_ISSET(fd, &rs))
4386          {
4387             int result = (int)read(fd, output, sizeof(output)-1);
4388             if(!result || (result < 0 && errno != EAGAIN))
4389                break;
4390             if(result > 0)
4391             {
4392                output[result] = '\0';
4393                if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
4394                {
4395                   app.Lock();
4396                   ide.outputView.debugBox.Log(output);
4397                   app.Unlock();
4398                }
4399             }
4400          }
4401       }
4402
4403       //if(fifoFile)
4404       {
4405          //fifoFile.CloseInput();
4406          //delete fifoFile;
4407          app.Lock();
4408          ide.outputView.debugBox.Log("\n");
4409          app.Unlock();
4410       }
4411       /*
4412       if(FileExists(progFifoPath)) //fileCreated)
4413       {
4414          DeleteFile(progFifoPath);
4415          progFifoPath[0] = '\0';
4416       }
4417       */
4418       return 0;
4419    }
4420 }
4421 #endif
4422
4423 class Argument : struct
4424 {
4425    Argument prev, next;
4426    char * name;
4427    property char * name { set { delete name; if(value) name = CopyString(value); } }
4428    char * val;
4429    property char * val { set { delete val; if(value) val = CopyString(value); } }
4430
4431    void Free()
4432    {
4433       delete name;
4434       delete val;
4435    }
4436
4437    ~Argument()
4438    {
4439       Free();
4440    }
4441 }
4442
4443 class Frame : struct
4444 {
4445    Frame prev, next;
4446    int level;
4447    char * addr;
4448    property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4449    char * func;
4450    property char * func { set { delete func; if(value) func = CopyString(value); } }
4451    int argsCount;
4452    OldList args;
4453    char * from;
4454    property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
4455    char * file;
4456    property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4457    char * absoluteFile;
4458    property char * absoluteFile { set { delete absoluteFile; if(value) absoluteFile = CopyUnescapedUnixPath(value); } }
4459    int line;
4460
4461    void Free()
4462    {
4463       delete addr;
4464       delete func;
4465       delete from;
4466       delete file;
4467       delete absoluteFile;
4468       args.Free(Argument::Free);
4469    }
4470
4471    ~Frame()
4472    {
4473       Free();
4474    }
4475 }
4476
4477 class GdbDataStop : struct
4478 {
4479    DebuggerReason reason;
4480    int threadid;
4481    union
4482    {
4483       struct
4484       {
4485          int bkptno;
4486       };
4487       struct
4488       {
4489          char * name;
4490          char * meaning;
4491       };
4492       struct
4493       {
4494          char * gdbResultVar;
4495          char * returnValue;
4496       };
4497    };
4498    Frame frame { };
4499
4500    void Free()
4501    {
4502       if(reason)
4503       {
4504          if(reason == signalReceived)
4505          {
4506             delete name;
4507             delete meaning;
4508          }
4509          else if(reason == functionFinished)
4510          {
4511             delete gdbResultVar;
4512             delete returnValue;
4513          }
4514       }
4515       if(frame) frame.Free();
4516    }
4517
4518    ~GdbDataStop()
4519    {
4520       Free();
4521    }
4522 }
4523
4524 class GdbDataBreakpoint : struct
4525 {
4526    int id;
4527    char * number;
4528    property char * number { set { delete number; if(value) number = CopyString(value); } }
4529    char * type;
4530    property char * type { set { delete type; if(value) type = CopyString(value); } }
4531    char * disp;
4532    property char * disp { set { delete disp; if(value) disp = CopyString(value); } }
4533    bool enabled;
4534    char * addr;
4535    property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4536    char * func;
4537    property char * func { set { delete func; if(value) func = CopyString(value); } }
4538    char * file;
4539    property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4540    char * fullname;
4541    property char * fullname { set { delete fullname; if(value) fullname = CopyUnescapedUnixPath(value); } }
4542    int line;
4543    char * at;
4544    property char * at { set { delete at; if(value) at = CopyString(value); } }
4545    int times;
4546
4547    Array<GdbDataBreakpoint> multipleBPs;
4548
4549    void Print()
4550    {
4551    _dpl(0, "");
4552       PrintLn("{", "#", number, " T", type, " D", disp, " E", enabled, " H", times, " (", func, ") (", file, ":", line, ") (", fullname, ") (", addr, ") (", at, ")", "}");
4553    }
4554
4555    void Free()
4556    {
4557       delete type;
4558       delete disp;
4559       delete addr;
4560       delete func;
4561       delete file;
4562       delete at;
4563       if(multipleBPs) multipleBPs.Free();
4564       delete multipleBPs;
4565    }
4566
4567    ~GdbDataBreakpoint()
4568    {
4569       Free();
4570    }
4571 }
4572
4573 class Breakpoint : struct
4574 {
4575    class_no_expansion;
4576
4577    char * function;
4578    property char * function { set { delete function; if(value) function = CopyString(value); } }
4579    char * relativeFilePath;
4580    property char * relativeFilePath { set { delete relativeFilePath; if(value) relativeFilePath = CopyString(value); } }
4581    char * absoluteFilePath;
4582    property char * absoluteFilePath { set { delete absoluteFilePath; if(value) absoluteFilePath = CopyString(value); } }
4583    char * location;
4584    property char * location { set { delete location; if(value) location = CopyString(value); } }
4585    int line;
4586    bool enabled;
4587    int hits;
4588    int breaks;
4589    int ignore;
4590    int level;
4591    Watch condition;
4592    bool inserted;
4593    BreakpointType type;
4594    DataRow row;
4595    GdbDataBreakpoint bp;
4596    Project project;
4597
4598    void ParseLocation()
4599    {
4600       char * prjName = null;
4601       char * filePath = null;
4602       char * file;
4603       char * line;
4604       char fullPath[MAX_LOCATION];
4605       ProjectNode node;
4606       if(location[0] == '\(' && location[1] && (file = strchr(location+2, '\)')) && file[1])
4607       {
4608          prjName = new char[file-location];
4609          strncpy(prjName, location+1, file-location-1);
4610          prjName[file-location-1] = '\0';
4611          file++;
4612       }
4613       else
4614          file = location;
4615       if((line = strchr(file+1, ':')))
4616       {
4617          filePath = new char[strlen(file)+1];
4618          strncpy(filePath, file, line-file);
4619          filePath[line-file] = '\0';
4620          line++;
4621       }
4622       else
4623          filePath = CopyString(file);
4624       property::relativeFilePath = filePath;
4625       if(prjName)
4626       {
4627          for(prj : ide.workspace.projects)
4628          {
4629             if(!strcmp(prjName, prj.name))
4630             {
4631                node = prj.topNode.FindWithPath(filePath, false);
4632                if(node)
4633                {
4634                   node.GetFullFilePath(fullPath);
4635                   property::absoluteFilePath = fullPath;
4636                   project = prj;
4637                   break;
4638                }
4639             }
4640          }
4641          if(line[0])
4642             this.line = atoi(line);
4643       }
4644       else
4645       {
4646          node = ide.projectView.project.topNode.Find(filePath, false);
4647          if(node)
4648          {
4649             node.GetFullFilePath(fullPath);
4650             property::absoluteFilePath = fullPath;
4651          }
4652          project = ide.project;
4653       }
4654       if(!absoluteFilePath)
4655          property::absoluteFilePath = "";
4656       delete prjName;
4657       delete filePath;
4658    }
4659
4660    char * CopyLocationString(bool removePath)
4661    {
4662       char * location;
4663       char * file = relativeFilePath ? relativeFilePath : absoluteFilePath;
4664       bool removingPath = removePath && file;
4665       if(removingPath)
4666       {
4667          char * fileName = new char[MAX_FILENAME];
4668          GetLastDirectory(file, fileName);
4669          file = fileName;
4670       }
4671       if(function)
4672       {
4673          if(file)
4674             location = PrintString(file, ":", function);
4675          else
4676             location = CopyString(function);
4677       }
4678       else
4679          location = PrintString(file, ":", line);
4680       if(removingPath)
4681          delete file;
4682       return location;
4683    }
4684
4685    char * CopyUserLocationString()
4686    {
4687       char * location;
4688       char * loc = CopyLocationString(false);
4689       Project prj = null;
4690       for(p : ide.workspace.projects; p != ide.workspace.projects.firstIterator.data)
4691       {
4692          if(p.topNode.FindByFullPath(absoluteFilePath, false))
4693          {
4694             prj = p;
4695             break;
4696          }
4697       }
4698       if(prj)
4699       {
4700          location = PrintString("(", prj.name, ")", loc);
4701          delete loc;
4702       }
4703       else
4704          location = loc;
4705       return location;
4706    }
4707
4708    void Save(File f)
4709    {
4710       if(relativeFilePath && relativeFilePath[0])
4711       {
4712          char * location = CopyUserLocationString();
4713          f.Printf("    * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, location);
4714          delete location;
4715          if(condition)
4716             f.Printf("       ~ %s\n", condition.expression);
4717       }
4718    }
4719
4720    void Free()
4721    {
4722       if(bp)
4723          bp.Free();
4724       delete bp;
4725       delete function;
4726       delete relativeFilePath;
4727       delete absoluteFilePath;
4728       delete location;
4729    }
4730
4731    ~Breakpoint()
4732    {
4733       Free();
4734    }
4735
4736 }
4737
4738 class Watch : struct
4739 {
4740    class_no_expansion;
4741    
4742    Type type;
4743    char * expression;
4744    char * value;
4745    DataRow row;
4746
4747    void Save(File f)
4748    {
4749       f.Printf("    ~ %s\n", expression);
4750    }
4751
4752    void Free()
4753    {
4754       delete expression;
4755       delete value;
4756       FreeType(type);
4757       type = null;
4758    }
4759
4760    void Reset()
4761    {
4762       delete value;
4763       FreeType(type);
4764       type = null;
4765    }
4766
4767    ~Watch()
4768    {
4769       Free();
4770    }
4771 }
4772
4773 class DebugListItem : struct
4774 {
4775    char * name;
4776    char * value;
4777 }
4778
4779 struct DebugEvaluationData
4780 {
4781    bool active;
4782    char * result;
4783    int bytes;
4784    uint64 nextBlockAddress;
4785
4786    DebuggerEvaluationError error;
4787 };
4788
4789 class CodeLocation : struct
4790 {
4791    char * file;
4792    char * absoluteFile;
4793    int line;
4794
4795    CodeLocation ::ParseCodeLocation(char * location)
4796    {
4797       if(location)
4798       {
4799          char * colon = null;
4800          char * temp;
4801          char loc[MAX_LOCATION];
4802          strcpy(loc, location);
4803          for(temp = loc; temp = strstr(temp, ":"); temp++)
4804             colon = temp;
4805          if(colon)
4806          {
4807             colon[0] = '\0';
4808             colon++;
4809             if(colon)
4810             {
4811                int line = atoi(colon);
4812                if(line)
4813                {
4814                   CodeLocation codloc { line = line };
4815                   codloc.file = CopyString(loc);
4816                   codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
4817                   return codloc;
4818                }
4819             }
4820          }
4821       }
4822       return null;
4823    }
4824
4825    void Free()
4826    {
4827       delete file;
4828       delete absoluteFile;
4829    }
4830
4831    ~CodeLocation()
4832    {
4833       Free();
4834    }
4835 }
4836
4837 void GDBFallBack(Expression exp, String expString)
4838 {
4839    char * result;
4840    ExpressionType evalError = dummyExp;
4841    result = Debugger::EvaluateExpression(expString, &evalError);
4842    if(result)
4843    {
4844       exp.constant = result;
4845       exp.type = constantExp;
4846    }
4847 }