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