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