ide; (#968) fixed toggling a breakpoint in wrong file with same file name in differen...
[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.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       bool vgFullLeakCheck = ide.workspace.vgFullLeakCheck;
2136       Project project = ide.project;
2137       DirExpression targetDirExp = project.GetTargetDir(compiler, config, bitDepth);
2138       PathBackup pathBackup { };
2139
2140       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbInit()");
2141       if(currentCompiler != compiler)
2142       {
2143          delete currentCompiler;
2144          currentCompiler = compiler;
2145          incref currentCompiler;
2146       }
2147       prjConfig = config;
2148       this.bitDepth = bitDepth;
2149       usingValgrind = useValgrind;
2150
2151       ChangeState(loaded);
2152       sentKill = false;
2153       sentBreakInsert = false;
2154       breakpointError = false;
2155       ignoreBreakpoints = false;
2156       symbols = true;
2157       targeted = false;
2158       modules = false;
2159       needReset = false;
2160       
2161       ide.outputView.ShowClearSelectTab(debug);
2162       ide.outputView.debugBox.Logf($"Starting debug mode\n");
2163
2164 #ifdef GDB_DEBUG_OUTPUT
2165       ide.outputView.gdbBox.Logf("run: Starting GDB\n");
2166 #endif
2167
2168       strcpy(tempPath, ide.workspace.projectDir);
2169       PathCatSlash(tempPath, targetDirExp.dir);
2170       delete targetDir;
2171       targetDir = CopyString(tempPath);
2172       project.CatTargetFileName(tempPath, compiler, config);
2173       delete targetFile;
2174       targetFile = CopyString(tempPath);
2175
2176       GetWorkingDir(oldDirectory, MAX_LOCATION);
2177       if(ide.workspace.debugDir && ide.workspace.debugDir[0])
2178       {
2179          char temp[MAX_LOCATION];
2180          strcpy(temp, ide.workspace.projectDir);
2181          PathCatSlash(temp, ide.workspace.debugDir);
2182          ChangeWorkingDir(temp);
2183       }
2184       else
2185          ChangeWorkingDir(ide.workspace.projectDir);
2186       
2187       ide.SetPath(true, compiler, config, bitDepth);
2188
2189       // TODO: This pollutes the environment, but at least it works
2190       // It shouldn't really affect the IDE as the PATH gets restored and other variables set for testing will unlikely cause problems
2191       // What is the proper solution for this? DualPipeOpenEnv?
2192       // gdb set environment commands don't seem to take effect
2193       for(e : ide.workspace.environmentVars)
2194       {
2195          SetEnvironment(e.name, e.string);
2196       }
2197
2198       if(usingValgrind)
2199       {
2200          char * clArgs = ide.workspace.commandLineArgs;
2201          const char *valgrindCommand = "valgrind"; // TODO: valgrind command config option //TODO: valgrind options
2202          vgLogFile = CreateTemporaryFile(vgLogPath, "ecereidevglog");
2203          if(vgLogFile)
2204          {
2205             incref vgLogFile;
2206             vgLogThread.Create();
2207          }
2208          else
2209          {
2210             ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't open temporary log file for Valgrind output\n");
2211             result = false;
2212          }
2213          if(result && !CheckCommandAvailable(valgrindCommand))
2214          {
2215             ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for Valgrind is not available.\n", valgrindCommand);
2216             result = false;
2217          }
2218          if(result)
2219          {
2220             sprintf(command, "%s --vgdb=yes --vgdb-error=0 --log-file=%s%s %s%s%s",
2221                   valgrindCommand, vgLogPath, vgFullLeakCheck ? " --leak-check=full" : "", targetFile, clArgs ? " " : "", clArgs ? clArgs : "");
2222             vgTargetHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
2223             if(!vgTargetHandle)
2224             {
2225                ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start Valgrind\n");
2226                result = false;
2227             }
2228          }
2229          if(result)
2230          {
2231             incref vgTargetHandle;
2232             vgTargetThread.Create();
2233
2234             targetProcessId = vgTargetHandle.GetProcessID();
2235             waitingForPID = false;
2236             if(!targetProcessId)
2237             {
2238                ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get Valgrind process ID\n");
2239                result = false;
2240             }
2241          }
2242          if(result)
2243          {
2244             app.Unlock();
2245             serialSemaphore.Wait();
2246             app.Lock();
2247          }
2248       }
2249
2250       if(result)
2251       {
2252          strcpy(command,
2253             (compiler.targetPlatform == win32 && bitDepth == 64) ? "x86_64-w64-mingw32-gdb" :
2254             (compiler.targetPlatform == win32 && bitDepth == 32) ? "i686-w64-mingw32-gdb" :
2255             "gdb");
2256          if(!CheckCommandAvailable(command))
2257          {
2258             ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for GDB is not available.\n", command);
2259             result = false;
2260          }
2261          else
2262          {
2263             strcat(command, " -n -silent --interpreter=mi2"); //-async //\"%s\"
2264             gdbTimer.Start();
2265             gdbHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
2266             if(!gdbHandle)
2267             {
2268                ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start GDB\n");
2269                result = false;
2270             }
2271          }
2272       }
2273       if(result)
2274       {
2275          incref gdbHandle;
2276          gdbThread.Create();
2277
2278          gdbProcessId = gdbHandle.GetProcessID();
2279          if(!gdbProcessId)
2280          {
2281             ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get GDB process ID\n");
2282             result = false;
2283          }
2284       }
2285       if(result)
2286       {
2287          app.Unlock();
2288          serialSemaphore.Wait();
2289          app.Lock();
2290
2291          GdbCommand(false, "-gdb-set verbose off");
2292          //GdbCommand(false, "-gdb-set exec-done-display on");
2293          GdbCommand(false, "-gdb-set step-mode off");
2294          GdbCommand(false, "-gdb-set unwindonsignal on");
2295          //GdbCommand(false, "-gdb-set shell on");
2296          GdbCommand(false, "set print elements 992");
2297          GdbCommand(false, "-gdb-set backtrace limit 100000");
2298
2299          if(!GdbTargetSet())
2300          {
2301             //ChangeState(terminated);
2302             result = false;
2303          }
2304       }
2305       if(result)
2306       {
2307 #if defined(__unix__)
2308          {
2309             CreateTemporaryDir(progFifoDir, "ecereide");
2310             strcpy(progFifoPath, progFifoDir);
2311             PathCat(progFifoPath, "ideprogfifo");
2312             if(!mkfifo(progFifoPath, 0600))
2313             {
2314                //fileCreated = true;
2315             }
2316             else
2317             {
2318                //app.Lock();
2319                ide.outputView.debugBox.Logf(createFIFOMsg, progFifoPath);
2320                //app.Unlock();
2321             }
2322          }
2323
2324          if(!usingValgrind)
2325          {
2326             progThread.terminate = false;
2327             progThread.Create();
2328          }
2329 #endif
2330
2331 #if defined(__WIN32__)
2332          GdbCommand(false, "-gdb-set new-console on");
2333 #endif
2334
2335 #if defined(__unix__)
2336          if(!usingValgrind)
2337             GdbCommand(false, "-inferior-tty-set %s", progFifoPath);
2338 #endif
2339
2340          if(!usingValgrind)
2341             GdbCommand(false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
2342          /*
2343          for(e : ide.workspace.environmentVars)
2344          {
2345             GdbCommand(false, "set environment %s=%s", e.name, e.string);
2346          }
2347          */
2348       }
2349
2350       ChangeWorkingDir(oldDirectory);
2351
2352       delete pathBackup;
2353
2354       if(!result)
2355          GdbExit();
2356       delete targetDirExp;
2357       return result;
2358    }
2359
2360    void GdbExit()
2361    {
2362       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExit()");
2363       if(gdbHandle && gdbProcessId)
2364       {
2365          GdbCommand(false, "-gdb-exit");
2366
2367          if(gdbThread)
2368          {
2369             app.Unlock();
2370             gdbThread.Wait();
2371             app.Lock();
2372          }
2373          if(gdbHandle)
2374          {
2375             gdbHandle.Wait();
2376             delete gdbHandle;
2377          }
2378       }
2379       gdbTimer.Stop();
2380       ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
2381       prjConfig = null;
2382       needReset = false;
2383
2384       if(ide.workspace)
2385       {
2386          for(bp : ide.workspace.breakpoints)
2387          {
2388             bp.inserted = false;
2389             delete bp.bp;
2390          }
2391       }
2392       for(bp : sysBPs)
2393       {
2394          bp.inserted = false;
2395          delete bp.bp;
2396       }
2397       if(bpRunToCursor)
2398       {
2399          bpRunToCursor.inserted = false;
2400          delete bpRunToCursor.bp;
2401       }
2402       
2403       ide.outputView.debugBox.Logf($"Debugging stopped\n");
2404       ClearBreakDisplay();
2405       ide.Update(null);
2406
2407 #if defined(__unix__)
2408       if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
2409       {
2410          progThread.terminate = true;
2411          if(fifoFile)
2412          {
2413             fifoFile.CloseInput();
2414             app.Unlock();
2415             progThread.Wait();
2416             app.Lock();
2417             delete fifoFile;
2418          }         
2419          DeleteFile(progFifoPath);
2420          progFifoPath[0] = '\0';
2421          rmdir(progFifoDir);
2422       }
2423 #endif
2424    }
2425
2426    void WatchesCodeEditorLinkInit()
2427    {
2428       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkInit()");
2429       /*
2430       char tempPath[MAX_LOCATION];
2431       char path[MAX_LOCATION];
2432       
2433       //void MakeFilePathProjectRelative(char * path, char * relativePath)
2434       if(!ide.projectView.project.GetRelativePath(activeFrame.file, tempPath))
2435          strcpy(tempPath, activeFrame.file);
2436       
2437       strcpy(path, ide.workspace.projectDir);
2438       PathCat(path, tempPath);
2439       codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2440       if(!codeEditor)
2441       {
2442          for(srcDir : ide.workspace.sourceDirs)
2443          {
2444             strcpy(path, srcDir);
2445             PathCat(path, tempPath);
2446             codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2447             if(codeEditor) break;
2448          }
2449       }
2450       */
2451
2452       /*if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2453          activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);*/
2454       if(!activeFrame || !activeFrame.absoluteFile)
2455          codeEditor = null;
2456       else
2457          codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal, false);
2458       if(codeEditor)
2459       {
2460          codeEditor.inUseDebug = true;
2461          incref codeEditor;
2462       }
2463       //watchesInit = true;
2464    }
2465
2466    void WatchesCodeEditorLinkRelease()
2467    {
2468       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkRelease()");
2469       //if(watchesInit)
2470       {
2471          if(codeEditor)
2472          {
2473             codeEditor.inUseDebug = false;
2474             if(!codeEditor.visible)
2475                codeEditor.Destroy(0);
2476             delete codeEditor;
2477          }
2478       }
2479    }
2480
2481    bool ResolveWatch(Watch wh)
2482    {
2483       bool result = false;
2484       
2485       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ResolveWatch()");
2486       wh.Reset();
2487
2488       /*delete wh.value;
2489       if(wh.type) 
2490       {
2491          FreeType(wh.type);
2492          wh.type = null;
2493       }*/
2494
2495       if(wh.expression)
2496       {
2497          char watchmsg[MAX_F_STRING];
2498          if(state == stopped && !codeEditor)
2499             wh.value = CopyString($"No source file found for selected frame");
2500          //if(codeEditor && state == stopped || state != stopped)
2501          else
2502          {
2503             Module backupPrivateModule;
2504             Context backupContext;
2505             Class backupThisClass;
2506             Expression exp;
2507             parseError = false;
2508
2509             backupPrivateModule = GetPrivateModule();
2510             backupContext = GetCurrentContext();
2511             backupThisClass = GetThisClass();
2512             if(codeEditor)
2513             {
2514                SetPrivateModule(codeEditor.privateModule);
2515                SetCurrentContext(codeEditor.globalContext);
2516                SetTopContext(codeEditor.globalContext);
2517                SetGlobalContext(codeEditor.globalContext);
2518                SetGlobalData(&codeEditor.globalData);
2519             }
2520          
2521             exp = ParseExpressionString(wh.expression);
2522             
2523             if(exp && !parseError)
2524             {
2525                char expString[4096];
2526                expString[0] = 0;
2527                PrintExpression(exp, expString);
2528
2529                if(GetPrivateModule())
2530                {
2531                   if(codeEditor)
2532                      DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2533                   ProcessExpressionType(exp);
2534                }
2535                wh.type = exp.expType;
2536                if(wh.type)
2537                   wh.type.refCount++;
2538                DebugComputeExpression(exp);
2539                if(ExpressionIsError(exp))
2540                {
2541                   GDBFallBack(exp, expString);
2542                }
2543
2544                /*if(exp.hasAddress)
2545                {
2546                   char temp[MAX_F_STRING];
2547                   sprintf(temp, "0x%x", exp.address);
2548                   wh.address = CopyString(temp);
2549                   // wh.address = CopyStringf("0x%x", exp.address);
2550                }*/
2551 /*
2552 //#ifdef _DEBUG
2553                {
2554                   Type dataType = exp.expType;
2555                   if(dataType)
2556                   {
2557                      char temp[MAX_F_STRING];
2558                      switch(dataType.kind)
2559                      {
2560                         case charType:
2561                            sprintf(temp, "%i", exp.val.c);
2562                            break;
2563                         case shortType:
2564                            sprintf(temp, "%i", exp.val.s);
2565                            break;
2566                         case intType:
2567                         case longType:
2568                         case enumType:
2569                            sprintf(temp, "%i", exp.val.i);
2570                            break;
2571                         case int64Type:
2572                            sprintf(temp, "%i", exp.val.i64);
2573                            break;
2574                         case pointerType:
2575                            sprintf(temp, "%i", exp.val.p);
2576                            break;
2577
2578                         case floatType:
2579                         {
2580                            long v = (long)exp.val.f;
2581                            sprintf(temp, "%i", v);
2582                            break;
2583                         } 
2584                         case doubleType:
2585                         {
2586                            long v = (long)exp.val.d;
2587                            sprintf(temp, "%i", v);
2588                            break;
2589                         }
2590                      }
2591                      if(temp)
2592                         wh.intVal = CopyString(temp);
2593                      switch(dataType.kind)
2594                      {
2595                         case charType:
2596                            sprintf(temp, "0x%x", exp.val.c);
2597                            break;
2598                         case shortType:
2599                            sprintf(temp, "0x%x", exp.val.s);
2600                            break;
2601                         case enumType:
2602                         case intType:
2603                            sprintf(temp, "0x%x", exp.val.i);
2604                            break;
2605                         case int64Type:
2606                            sprintf(temp, "0x%x", exp.val.i64);
2607                            break;
2608                         case longType:
2609                            sprintf(temp, "0x%x", exp.val.i64);
2610                            break;
2611                         case pointerType:
2612                            sprintf(temp, "0x%x", exp.val.p);
2613                            break;
2614
2615                         case floatType:
2616                         {
2617                            long v = (long)exp.val.f;
2618                            sprintf(temp, "0x%x", v);
2619                            break;
2620                         } 
2621                         case doubleType:
2622                         {
2623                            long v = (long)exp.val.d;
2624                            sprintf(temp, "0x%x", v);
2625                            break;
2626                         }
2627                      }
2628                      if(temp)
2629                         wh.hexVal = CopyString(temp);
2630                      switch(dataType.kind)
2631                      {
2632                         case charType:
2633                            sprintf(temp, "0o%o", exp.val.c);
2634                            break;
2635                         case shortType:
2636                            sprintf(temp, "0o%o", exp.val.s);
2637                            break;
2638                         case enumType:
2639                         case intType:
2640                            sprintf(temp, "0o%o", exp.val.i);
2641                            break;
2642                         case int64Type:
2643                            sprintf(temp, "0o%o", exp.val.i64);
2644                            break;
2645                         case longType:
2646                            sprintf(temp, "0o%o", exp.val.i64);
2647                            break;
2648                         case pointerType:
2649                            sprintf(temp, "0o%o", exp.val.p);
2650                            break;
2651
2652                         case floatType:
2653                         {
2654                            long v = (long)exp.val.f;
2655                            sprintf(temp, "0o%o", v);
2656                            break;
2657                         } 
2658                         case doubleType:
2659                         {
2660                            long v = (long)exp.val.d;
2661                            sprintf(temp, "0o%o", v);
2662                            break;
2663                         }
2664                      }
2665                      if(temp)
2666                         wh.octVal = CopyString(temp);
2667                   }
2668                }
2669                // WHATS THIS HERE ?
2670                if(exp.type == constantExp && exp.constant)
2671                   wh.constant = CopyString(exp.constant);
2672 //#endif
2673 */
2674
2675                switch(exp.type)
2676                {
2677                   case symbolErrorExp:
2678                      snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2679                      break;
2680                   case structMemberSymbolErrorExp:
2681                      // todo get info as in next case (ExpClassMemberSymbolError)
2682                      snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2683                      break;
2684                   case classMemberSymbolErrorExp:
2685                      {
2686                         Class _class;
2687                         Expression memberExp = exp.member.exp;
2688                         Identifier memberID = exp.member.member;
2689                         Type type = memberExp.expType;
2690                         if(type)
2691                         {
2692                            _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2693                            if(!_class)
2694                            {
2695                               char string[256] = "";
2696                               Symbol classSym;
2697                               PrintTypeNoConst(type, string, false, true);
2698                               classSym = FindClass(string);
2699                               _class = classSym ? classSym.registered : null;
2700                            }
2701                            if(_class)
2702                               snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2703                            else
2704                               snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2705                         }
2706                         else
2707                            snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2708                      }
2709                      break;
2710                   case memoryErrorExp:
2711                      // Need to ensure when set to memoryErrorExp, constant is set
2712                      snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2713                      break;
2714                   case dereferenceErrorExp:
2715                      snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2716                      break;
2717                   case unknownErrorExp:
2718                      snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2719                      break;
2720                   case noDebuggerErrorExp:
2721                      snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2722                      break;
2723                   case debugStateErrorExp:
2724                      snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2725                      break;
2726                   case 0:
2727                      snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2728                      break;
2729                   case constantExp:
2730                   case stringExp:
2731                      // Temporary Code for displaying Strings
2732                      if((exp.expType && ((exp.expType.kind == pointerType || 
2733                               exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) || 
2734                            (wh.type && wh.type.kind == classType && wh.type._class && 
2735                               wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2736                               !strcmp(wh.type._class.registered.name, "String")))
2737                      {
2738
2739                         if(exp.expType.kind != arrayType || exp.hasAddress)
2740                         {
2741                            uint64 address;
2742                            char * string;
2743                            char value[4196];
2744                            int len;
2745                            //char temp[MAX_F_STRING * 32];
2746
2747                            ExpressionType evalError = dummyExp;
2748                            /*if(exp.expType.kind == arrayType)
2749                               sprintf(temp, "(char*)0x%x", exp.address);
2750                            else
2751                               sprintf(temp, "(char*)%s", exp.constant);*/
2752
2753                            //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2754                            // address = strtoul(exp.constant, null, 0);
2755                            address = _strtoui64(exp.constant, null, 0);
2756                            //_dpl(0, "0x", address);
2757                            // snprintf(value, sizeof(value), "0x%08x ", address);
2758
2759                            if(address > 0xFFFFFFFFLL)
2760                               snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2761                            else
2762                               snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2763                            value[sizeof(value)-1] = 0;
2764                            
2765                            if(!address)
2766                               strcat(value, $"Null string");
2767                            else
2768                            {
2769                               int size = 4096;
2770                               len = strlen(value);
2771                               string = null;
2772                               while(!string && size > 2)
2773                               {
2774                                  string = GdbReadMemory(address, size);
2775                                  size /= 2;
2776                               }
2777                               if(string && string[0])
2778                               {
2779                                  value[len++] = '(';
2780                                  if(UTF8Validate(string))
2781                                  {
2782                                     int c;
2783                                     char ch;
2784                                     
2785                                     for(c = 0; (ch = string[c]) && c<4096; c++)
2786                                        value[len++] = ch;                                 
2787                                     value[len++] = ')';
2788                                     value[len++] = '\0';
2789                                     
2790                                  }
2791                                  else
2792                                  {
2793                                     ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2794                                     strcat(value, ") (ISO8859-1)");
2795                                  }
2796
2797                                  delete string;
2798                               }
2799                               else if(string)
2800                               {
2801                                  strcat(value, $"Empty string");
2802                                  delete string;
2803                               }
2804                               else
2805                                  strcat(value, $"Couldn't read memory");
2806                            }
2807                            wh.value = CopyString(value);
2808                         }
2809                      }
2810                      else if(wh.type && wh.type.kind == classType && wh.type._class && 
2811                               wh.type._class.registered && wh.type._class.registered.type == enumClass)
2812                      {
2813                         uint64 value = strtoul(exp.constant, null, 0);
2814                         Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2815                         EnumClassData enumeration = (EnumClassData)enumClass.data;
2816                         NamedLink item;
2817                         for(item = enumeration.values.first; item; item = item.next)
2818                            if((int)item.data == value)
2819                               break;
2820                         if(item)
2821                            wh.value = CopyString(item.name);
2822                         else
2823                            wh.value = CopyString($"Invalid Enum Value");
2824                         result = true;
2825                      }
2826                      else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class && 
2827                               wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2828                      {
2829                         unichar value;
2830                         int signedValue;
2831                         char charString[5];
2832                         char string[256];
2833
2834                         if(exp.constant[0] == '\'')
2835                         {
2836                            if((int)((byte *)exp.constant)[1] > 127)
2837                            {
2838                               int nb;
2839                               value = UTF8GetChar(exp.constant + 1, &nb);
2840                               if(nb < 2) value = exp.constant[1];
2841                               signedValue = value;
2842                            }
2843                            else
2844                            {
2845                               signedValue = exp.constant[1];
2846                               {
2847                                  // Precomp Syntax error with boot strap here:
2848                                  byte b = (byte)(char)signedValue;
2849                                  value = (unichar) b;
2850                               }
2851                            }
2852                         }
2853                         else
2854                         {
2855                            if(wh.type.kind == charType && wh.type.isSigned)
2856                            {
2857                               signedValue = (int)(char)strtol(exp.constant, null, 0);
2858                               {
2859                                  // Precomp Syntax error with boot strap here:
2860                                  byte b = (byte)(char)signedValue;
2861                                  value = (unichar) b;
2862                               }
2863                            }
2864                            else
2865                            {
2866                               value = (uint)strtoul(exp.constant, null, 0);
2867                               signedValue = (int)value;
2868                            }
2869                         }
2870                         charString[0] = 0;
2871                         UTF32toUTF8Len(&value, 1, charString, 5);
2872                         if(value == '\0')
2873                            snprintf(string, sizeof(string), "\'\\0' (0)");
2874                         else if(value == '\t')
2875                            snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2876                         else if(value == '\n')
2877                            snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2878                         else if(value == '\r')
2879                            snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2880                         else if(wh.type.kind == charType && wh.type.isSigned)
2881                            snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2882                         else if(value > 256 || wh.type.kind != charType)
2883                         {
2884                            if(value > 0x10FFFF || !GetCharCategory(value))
2885                               snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2886                            else
2887                               snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2888                         }
2889                         else
2890                            snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2891                         string[sizeof(string)-1] = 0;
2892                         
2893                         wh.value = CopyString(string);
2894                         result = true;
2895                      }
2896                      else
2897                      {
2898                         wh.value = CopyString(exp.constant);
2899                         result = true;
2900                      }
2901                      break;
2902                   default:
2903                      if(exp.hasAddress)
2904                      {
2905                         wh.value = PrintHexUInt64(exp.address);
2906                         result = true;
2907                      }
2908                      else
2909                      {
2910                         char tempString[256];
2911                         if(exp.member.memberType == propertyMember)
2912                            snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2913                         else
2914                            snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression, 
2915                                  exp.type.OnGetString(tempString, null, null));
2916                      }
2917                      break;
2918                }
2919             }
2920             else
2921                snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2922             if(exp) FreeExpression(exp);
2923
2924             
2925             SetPrivateModule(backupPrivateModule);
2926             SetCurrentContext(backupContext);
2927             SetTopContext(backupContext);
2928             SetGlobalContext(backupContext);
2929             SetThisClass(backupThisClass);
2930          }
2931          //else 
2932          //   wh.value = CopyString("No source file found for selected frame");
2933          
2934          watchmsg[sizeof(watchmsg)-1] = 0;
2935          if(!wh.value)
2936             wh.value = CopyString(watchmsg);
2937       }
2938       ide.watchesView.UpdateWatch(wh);
2939       return result;
2940    }
2941
2942    void EvaluateWatches()
2943    {
2944       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateWatches()");
2945       for(wh : ide.workspace.watches)
2946          ResolveWatch(wh);
2947    }
2948
2949    char * ::GdbEvaluateExpression(char * expression)
2950    {
2951       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbEvaluateExpression(", expression, ")");
2952       eval.active = true;
2953       eval.error = none;
2954       GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2955       if(eval.active)
2956          ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2957       return eval.result;
2958    }
2959
2960    // to be removed... use GdbReadMemory that returns a byte array instead
2961    char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
2962    {
2963       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemoryString(", address, ")");
2964       eval.active = true;
2965       eval.error = none;
2966 #ifdef _DEBUG
2967       if(!size)
2968          _dpl(0, "GdbReadMemoryString called with size = 0!");
2969 #endif
2970       // GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
2971       if(GetRuntimePlatform() == win32)
2972          GdbCommand(false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
2973       else
2974          GdbCommand(false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
2975       if(eval.active)
2976          ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
2977       return eval.result;
2978    }
2979
2980    byte * ::GdbReadMemory(uint64 address, int bytes)
2981    {
2982       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemory(", address, ")");
2983       eval.active = true;
2984       eval.error = none;
2985       //GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
2986       if(GetRuntimePlatform() == win32)
2987          GdbCommand(false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
2988       else
2989          GdbCommand(false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
2990 #ifdef _DEBUG
2991       if(!bytes)
2992          _dpl(0, "GdbReadMemory called with bytes = 0!");
2993 #endif
2994       if(eval.active)
2995          ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
2996       else if(eval.result && strcmp(eval.result, "N/A"))
2997       {
2998          byte * result = new byte[bytes];
2999          byte * string = eval.result;
3000          int c = 0;
3001          while(true)
3002          {
3003             result[c++] = (byte)strtol(string, &string, 10);
3004             if(string)
3005             {
3006                if(*string == ',')
3007                   string++;
3008                 else
3009                   break;
3010             }
3011             else
3012                break;
3013          }
3014          return result;
3015       }
3016       return null;
3017    }
3018
3019    void EventHit(GdbDataStop stopItem, Breakpoint bpInternal, Breakpoint bpUser)
3020    {
3021       char * s1 = null; char * s2 = null;
3022       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EventHit(",
3023             "bpInternal(", bpInternal ? s1=bpInternal.CopyLocationString(false) : null, "), ",
3024             "bpUser(", bpUser ? s2=bpUser.CopyLocationString(false) : null, ")) -- ",
3025             "ignoreBreakpoints(", ignoreBreakpoints, "), ",
3026             "hitCursorBreakpoint(", bpUser && bpUser.type == runToCursor,  ")");
3027       delete s1; delete s2;
3028
3029       if(bpUser && stopItem.frame.line && bpUser.line != stopItem.frame.line)
3030       {
3031          // updating user breakpoint on hit location difference
3032          // todo, print something?
3033          bpUser.line = stopItem.frame.line;
3034          ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3035          ide.workspace.Save();
3036       }
3037       if(bpInternal)
3038       {
3039          bpInternal.hits++;
3040          if(bpInternal.type == internalModulesLoaded)
3041             modules = true;
3042          if(!bpUser && !userBreakOnInternalBreakpoint)
3043             GdbExecContinue(false);
3044       }
3045       if(bpUser)
3046       {
3047          bool conditionMet = true;
3048          if(bpUser.condition)
3049             conditionMet = ResolveWatch(bpUser.condition);
3050          bpUser.hits++;
3051          if(!ignoreBreakpoints && (bpUser.level == -1 || bpUser.level == frameCount-1) && conditionMet)
3052          {
3053             if(!bpUser.ignore)
3054                bpUser.breaks++;
3055             else
3056             {
3057                bpUser.ignore--;
3058                GdbExecContinue(false);
3059             }
3060          }
3061          else
3062             GdbExecContinue(false);
3063          ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3064          if(bpUser == bpRunToCursor)
3065          {
3066             UnsetBreakpoint(bpUser);
3067             delete bpRunToCursor;
3068          }
3069       }
3070
3071       if(!bpUser && !bpInternal)
3072          GdbExecContinue(false);
3073    }
3074
3075    void ValgrindTargetThreadExit()
3076    {
3077       ide.outputView.debugBox.Logf($"ValgrindTargetThreadExit\n");
3078       if(vgTargetHandle)
3079       {
3080          vgTargetHandle.Wait();
3081          delete vgTargetHandle;
3082       }
3083       HandleExit(null, null);
3084    }
3085
3086    void GdbThreadExit()
3087    {
3088       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbThreadExit()");
3089       if(state != terminated)
3090       {
3091          ChangeState(terminated);
3092          targetProcessId = 0;
3093          ClearBreakDisplay();
3094
3095          if(vgLogFile)
3096             delete vgLogFile;
3097          if(gdbHandle)
3098          {
3099             serialSemaphore.Release();
3100             gdbTimer.Stop();
3101             gdbHandle.Wait();
3102             delete gdbHandle;
3103             
3104             ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
3105             ide.outputView.debugBox.Logf($"Debugging stopped\n");
3106             ide.Update(null);
3107             HideDebuggerViews();
3108          }
3109          //ChangeState(terminated);
3110       }
3111    }
3112
3113    void GdbThreadMain(char * output)
3114    {
3115       int i;
3116       Array<char *> outTokens { minAllocSize = 50 };
3117       Array<char *> subTokens { minAllocSize = 50 };
3118       DebugListItem item { };
3119       DebugListItem item2 { };
3120       bool setWaitingForPID = false;
3121       
3122 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
3123 #ifdef GDB_DEBUG_CONSOLE
3124       _dpl2(_dpct, dplchan::gdbOutput, 0, output);
3125 #endif
3126 #ifdef GDB_DEBUG_OUTPUT
3127       {
3128          int len = strlen(output);
3129          if(len > 1024)
3130          {
3131             int c;
3132             char * start;
3133             char tmp[1025];
3134             tmp[1024] = '\0';
3135             start = output;
3136             for(c = 0; c < len / 1024; c++)
3137             {
3138                strncpy(tmp, start, 1024);
3139                ide.outputView.gdbBox.Logf("out: %s\n", tmp);
3140                start += 1024;
3141             }
3142             ide.outputView.gdbBox.Logf("out: %s\n", start);
3143          }
3144          else
3145          {
3146             ide.outputView.gdbBox.Logf("out: %s\n", output);
3147          }
3148       }
3149 #endif
3150 #ifdef GDB_DEBUG_CONSOLE
3151          strcpy(lastGdbOutput, output);
3152 #endif
3153 #ifdef GDB_DEBUG_GUI
3154          if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
3155 #endif
3156 #endif
3157       
3158       switch(output[0])
3159       {
3160          case '~':
3161             if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
3162             {
3163                symbols = false;
3164                ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
3165                ide.Update(null);
3166             }
3167             break;
3168          case '^':
3169             gdbReady = false;
3170             if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
3171             {
3172                //if(outTokens.count == 1)
3173                {
3174                   if(sentKill)
3175                   {
3176                      sentKill = false;
3177                      ChangeState(loaded);
3178                      targetProcessId = 0;
3179                      if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3180                      {
3181                         if(!strcmp(item.name, "reason"))
3182                         {
3183                            char * reason = item.value;
3184                            StripQuotes(reason, reason);
3185                            if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3186                            {
3187                               char * exitCode;
3188                               if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
3189                               {
3190                                  StripQuotes(item2.value, item2.value);
3191                                  if(!strcmp(item2.name, "exit-code"))
3192                                     exitCode = item2.value;
3193                                  else
3194                                     exitCode = null;
3195                               }
3196                               else
3197                                  exitCode = null;
3198                               HandleExit(reason, exitCode);
3199                            }
3200                         }
3201                         else
3202                            _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "kill reply (", item.name, "=", item.value, ") is unheard of");
3203                      }
3204                      else
3205                         HandleExit(null, null);
3206                   }
3207                }
3208                if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3209                {
3210                   if(!strcmp(item.name, "bkpt"))
3211                   {
3212                      sentBreakInsert = false;
3213 #ifdef _DEBUG
3214                      if(bpItem)
3215                         _dpl(0, "problem");
3216 #endif
3217                      bpItem = ParseBreakpoint(item.value, outTokens);
3218                      //breakType = bpValidation;
3219                   }
3220                   else if(!strcmp(item.name, "depth"))
3221                   {
3222                      StripQuotes(item.value, item.value);
3223                      frameCount = atoi(item.value);
3224                      activeFrame = null;
3225                      stackFrames.Free(Frame::Free);
3226                   }
3227                   else if(!strcmp(item.name, "stack"))
3228                   {
3229                      Frame frame;
3230                      if(stackFrames.count)
3231                         ide.callStackView.Logf("...\n");
3232                      else
3233                         activeFrame = null;
3234                      item.value = StripBrackets(item.value);
3235                      TokenizeList(item.value, ',', subTokens);
3236                      for(i = 0; i < subTokens.count; i++)
3237                      {
3238                         if(TokenizeListItem(subTokens[i], item))
3239                         {
3240                            if(!strcmp(item.name, "frame"))
3241                            {
3242                               frame = Frame { };
3243                               stackFrames.Add(frame);
3244                               item.value = StripCurlies(item.value);
3245                               ParseFrame(frame, item.value);
3246                               if(frame.file && frame.from)
3247                                  _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "unexpected frame file and from members present");
3248                               if(frame.file)
3249                               {
3250                                  char * s;
3251                                  if(activeFrameLevel == -1)
3252                                  {
3253                                     if(ide.projectView.IsModuleInProject(frame.file));
3254                                     {
3255                                        if(frame.level != 0)
3256                                        {
3257                                           //stopItem.frame = frame;
3258                                           breakType = selectFrame;
3259                                        }
3260                                        else
3261                                           activeFrame = frame;
3262                                        activeFrameLevel = frame.level;
3263                                     }
3264                                  }
3265                                  ide.callStackView.Logf("%3d ", frame.level);
3266                                  if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
3267                                     ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
3268                                  else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
3269                                     ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
3270                                  else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
3271                                     ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
3272                                  else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
3273                                     ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
3274                                  else
3275                                     ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
3276                                  delete s;
3277                               }
3278                               else
3279                               {
3280                                  ide.callStackView.Logf("%3d ", frame.level);
3281
3282                                  if(frame.from)
3283                                  {
3284                                     char * s;
3285                                     ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
3286                                     delete s;
3287                                  }
3288                                  else if(frame.func)
3289                                     ide.callStackView.Logf("%s\n", frame.func);
3290                                  else
3291                                     ide.callStackView.Logf($"unknown source\n");
3292                               }
3293                            }
3294                            else
3295                               _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "stack content (", item.name, "=", item.value, ") is unheard of");
3296                         }
3297                      }
3298                      if(activeFrameLevel == -1)
3299                      {
3300                         activeFrameLevel = 0;
3301                         activeFrame = stackFrames.first;
3302                      }
3303                      ide.callStackView.Home();
3304                      ide.Update(null);
3305                      subTokens.RemoveAll();
3306                   }
3307                   /*else if(!strcmp(item.name, "frame"))
3308                   {
3309                      Frame frame { };
3310                      item.value = StripCurlies(item.value);
3311                      ParseFrame(&frame, item.value);
3312                   }*/
3313                   else if(!strcmp(item.name, "thread-ids"))
3314                   {
3315                      ide.threadsView.Clear();
3316                      item.value = StripCurlies(item.value);
3317                      TokenizeList(item.value, ',', subTokens);
3318                      for(i = subTokens.count - 1; ; i--)
3319                      {
3320                         if(TokenizeListItem(subTokens[i], item))
3321                         {
3322                            if(!strcmp(item.name, "thread-id"))
3323                            {
3324                               int value;
3325                               StripQuotes(item.value, item.value);
3326                               value = atoi(item.value);
3327                               ide.threadsView.Logf("%3d \n", value);
3328                            }
3329                            else
3330                               _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "threads content (", item.name, "=", item.value, ") is unheard of");
3331                         }
3332                         if(!i)
3333                            break;
3334                      }
3335                      ide.threadsView.Home();
3336                      ide.Update(null);
3337                      subTokens.RemoveAll();
3338                      //if(!strcmp(outTokens[2], "number-of-threads"))
3339                   }
3340                   else if(!strcmp(item.name, "new-thread-id"))
3341                   {
3342                      StripQuotes(item.value, item.value);
3343                      activeThread = atoi(item.value);
3344                   }
3345                   else if(!strcmp(item.name, "value"))
3346                   {
3347                      StripQuotes(item.value, item.value);
3348                      eval.result = CopyString(item.value);
3349                      eval.active = false;
3350                   }
3351                   else if(!strcmp(item.name, "addr"))
3352                   {
3353                      for(i = 2; i < outTokens.count; i++)
3354                      {
3355                         if(TokenizeListItem(outTokens[i], item))
3356                         {
3357                            if(!strcmp(item.name, "total-bytes"))
3358                            {
3359                               StripQuotes(item.value, item.value);
3360                               eval.bytes = atoi(item.value);
3361                            }
3362                            else if(!strcmp(item.name, "next-row"))
3363                            {
3364                               StripQuotes(item.value, item.value);
3365                               eval.nextBlockAddress = _strtoui64(item.value, null, 0);
3366                            }
3367                            else if(!strcmp(item.name, "memory"))
3368                            {
3369                               int j;
3370                               //int value;
3371                               //StripQuotes(item.value, item.value);
3372                               item.value = StripBrackets(item.value);
3373                               // this should be treated as a list...
3374                               item.value = StripCurlies(item.value);
3375                               TokenizeList(item.value, ',', subTokens);
3376                               for(j = 0; j < subTokens.count; j++)
3377                               {
3378                                  if(TokenizeListItem(subTokens[j], item))
3379                                  {
3380                                     if(!strcmp(item.name, "data"))
3381                                     {
3382                                        item.value = StripBrackets(item.value);
3383                                        StripQuotes2(item.value, item.value);
3384                                        eval.result = CopyString(item.value);
3385                                        eval.active = false;
3386                                     }
3387                                  }
3388                               }
3389                               subTokens.RemoveAll();
3390                            }
3391                         }
3392                      }
3393                   }
3394                   else if(!strcmp(item.name, "source-path") || !strcmp(item.name, "BreakpointTable"))
3395                      _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "command reply (", item.name, "=", item.value, ") is ignored");
3396                   else
3397                      _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "command reply (", item.name, "=", item.value, ") is unheard of");
3398                }
3399             }
3400             else if(!strcmp(outTokens[0], "^running"))
3401             {
3402                waitingForPID = true;
3403                setWaitingForPID = true;
3404             }
3405             else if(!strcmp(outTokens[0], "^exit"))
3406             {
3407                ChangeState(terminated);
3408                // ide.outputView.debugBox.Logf("Exit\n");
3409                // ide.Update(null);
3410                gdbReady = true;
3411                serialSemaphore.Release();
3412             }
3413             else if(!strcmp(outTokens[0], "^error"))
3414             {
3415                if(sentBreakInsert)
3416                {
3417                   sentBreakInsert = false;
3418                   breakpointError = true;
3419                }
3420
3421                if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3422                {
3423                   if(!strcmp(item.name, "msg"))
3424                   {
3425                      StripQuotes(item.value, item.value);
3426                      if(eval.active)
3427                      {
3428                         eval.active = false;
3429                         eval.result = null;
3430                         if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3431                            eval.error = symbolNotFound;
3432                         else if(strstr(item.value, "Cannot access memory at address"))
3433                            eval.error = memoryCantBeRead;
3434                         else
3435                            eval.error = unknown;
3436                      }
3437                      else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3438                      {
3439                      }
3440                      else if(!strncmp(item.value, "Cannot access memory at address", 31))
3441                      {
3442                      }
3443                      else if(!strcmp(item.value, "Cannot find bounds of current function"))
3444                      {
3445                         ChangeState(stopped);
3446                         gdbHandle.Printf("-exec-continue\n");
3447                      }
3448                      else if(!strcmp(item.value, "ptrace: No such process."))
3449                      {
3450                         ChangeState(loaded);
3451                         targetProcessId = 0;
3452                      }
3453                      else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3454                      {
3455                      }
3456                      else if(!strcmp(item.value, "You can't do that without a process to debug."))
3457                      {
3458                         ChangeState(loaded);
3459                         targetProcessId = 0;
3460                      }
3461                      else if(strstr(item.value, "No such file or directory."))
3462                      {
3463                         ChangeState(loaded);
3464                         targetProcessId = 0;
3465                      }
3466                      else if(strstr(item.value, "During startup program exited with code "))
3467                      {
3468                         ChangeState(loaded);
3469                         targetProcessId = 0;
3470                      }
3471                      else
3472                      {
3473 #ifdef _DEBUG
3474                         if(strlen(item.value) < MAX_F_STRING)
3475                         {
3476                            char * s;
3477                            ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3478                            delete s;
3479                         }
3480                         else
3481                            ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3482 #endif
3483                      }
3484                   }
3485                }
3486                else
3487                   _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "error content (", item.name, "=", item.value, ") is unheard of");
3488             }
3489             else
3490                _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "result-record: ", outTokens[0]);
3491             outTokens.RemoveAll();
3492             break;
3493          case '+':
3494             _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "status-async-output: ", outTokens[0]);
3495             break;
3496          case '=':
3497             if(TokenizeList(output, ',', outTokens))
3498             {
3499                if(!strcmp(outTokens[0], "=library-loaded"))
3500                   FGODetectLoadedLibraryForAddedProjectIssues(outTokens);
3501                else if(!strcmp(outTokens[0], "=thread-group-created") || !strcmp(outTokens[0], "=thread-group-added") ||
3502                         !strcmp(outTokens[0], "=thread-group-started") || !strcmp(outTokens[0], "=thread-group-exited") ||
3503                         !strcmp(outTokens[0], "=thread-created") || !strcmp(outTokens[0], "=thread-exited") ||
3504                         !strcmp(outTokens[0], "=cmd-param-changed") || !strcmp(outTokens[0], "=library-unloaded") ||
3505                         !strcmp(outTokens[0], "=breakpoint-modified"))
3506                   _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, outTokens[0], outTokens.count>1 ? outTokens[1] : "",
3507                            outTokens.count>2 ? outTokens[2] : "", outTokens.count>3 ? outTokens[3] : "",
3508                            outTokens.count>4 ? outTokens[4] : "", outTokens.count>5 ? outTokens[5] : "",
3509                            outTokens.count>6 ? outTokens[6] : "", outTokens.count>7 ? outTokens[7] : "",
3510                            outTokens.count>8 ? outTokens[8] : "", outTokens.count>9 ? outTokens[9] : "");
3511                else
3512                   _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "notify-async-output: ", outTokens[0]);
3513             }
3514             outTokens.RemoveAll();
3515             break;
3516          case '*':
3517             gdbReady = false;
3518             if(TokenizeList(output, ',', outTokens))
3519             {
3520                if(!strcmp(outTokens[0],"*running"))
3521                {
3522                   waitingForPID = true;
3523                   setWaitingForPID = true;
3524                }
3525                else if(!strcmp(outTokens[0], "*stopped"))
3526                {
3527                   int tk;
3528                   ChangeState(stopped);
3529
3530                   for(tk = 1; tk < outTokens.count; tk++)
3531                   {
3532                      if(TokenizeListItem(outTokens[tk], item))
3533                      {
3534                         if(!strcmp(item.name, "reason"))
3535                         {
3536                            char * reason = item.value;
3537                            StripQuotes(reason, reason);
3538                            if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3539                            {
3540                               char * exitCode;
3541                               if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3542                               {
3543                                  tk++;
3544                                  StripQuotes(item2.value, item2.value);
3545                                  if(!strcmp(item2.name, "exit-code"))
3546                                     exitCode = item2.value;
3547                                  else
3548                                     exitCode = null;
3549                               }
3550                               else
3551                                  exitCode = null;
3552                               HandleExit(reason, exitCode);
3553                               needReset = true;
3554                            }
3555                            else if(!strcmp(reason, "breakpoint-hit"))
3556                            {
3557       #ifdef _DEBUG
3558                               if(stopItem)
3559                                  _dpl(0, "problem");
3560       #endif
3561                               stopItem = GdbDataStop { };
3562
3563                               for(i = tk+1; i < outTokens.count; i++)
3564                               {
3565                                  TokenizeListItem(outTokens[i], item);
3566                                  StripQuotes(item.value, item.value);
3567                                  if(!strcmp(item.name, "bkptno"))
3568                                     stopItem.bkptno = atoi(item.value);
3569                                  else if(!strcmp(item.name, "thread-id"))
3570                                     stopItem.threadid = atoi(item.value);
3571                                  else if(!strcmp(item.name, "frame"))
3572                                  {
3573                                     item.value = StripCurlies(item.value);
3574                                     ParseFrame(stopItem.frame, item.value);
3575                                  }
3576                                  else if(!strcmp(item.name, "disp") || !strcmp(item.name, "stopped-threads") || !strcmp(item.name, "core"))
3577                                     _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3578                                  else
3579                                     _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown breakpoint hit item name (", item.name, "=", item.value, ")");
3580                               }
3581
3582                               event = hit;
3583                            }
3584                            else if(!strcmp(reason, "end-stepping-range"))
3585                            {
3586       #ifdef _DEBUG
3587                               if(stopItem)
3588                                  _dpl(0, "problem");
3589       #endif
3590                               stopItem = GdbDataStop { };
3591
3592                               for(i = tk+1; i < outTokens.count; i++)
3593                               {
3594                                  TokenizeListItem(outTokens[i], item);
3595                                  StripQuotes(item.value, item.value);
3596                                  if(!strcmp(item.name, "thread-id"))
3597                                     stopItem.threadid = atoi(item.value);
3598                                  else if(!strcmp(item.name, "frame"))
3599                                  {
3600                                     item.value = StripCurlies(item.value);
3601                                     ParseFrame(stopItem.frame, item.value);
3602                                  }
3603                                  else if(!strcmp(item.name, "reason") || !strcmp(item.name, "bkptno"))
3604                                     _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3605                                  else
3606                                     _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown end of stepping range item name (", item.name, "=", item.value, ")");
3607                               }
3608
3609                               event = stepEnd;
3610                               ide.Update(null);
3611                            }
3612                            else if(!strcmp(reason, "function-finished"))
3613                            {
3614       #ifdef _DEBUG
3615                               if(stopItem)
3616                                  _dpl(0, "problem");
3617       #endif
3618                               stopItem = GdbDataStop { };
3619                               stopItem.reason = CopyString(reason);
3620
3621                               for(i = tk+1; i < outTokens.count; i++)
3622                               {
3623                                  TokenizeListItem(outTokens[i], item);
3624                                  StripQuotes(item.value, item.value);
3625                                  if(!strcmp(item.name, "thread-id"))
3626                                     stopItem.threadid = atoi(item.value);
3627                                  else if(!strcmp(item.name, "frame"))
3628                                  {
3629                                     item.value = StripCurlies(item.value);
3630                                     ParseFrame(stopItem.frame, item.value);
3631                                  }
3632                                  else if(!strcmp(item.name, "gdb-result-var"))
3633                                     stopItem.gdbResultVar = CopyString(item.value);
3634                                  else if(!strcmp(item.name, "return-value"))
3635                                     stopItem.returnValue = CopyString(item.value);
3636                                  else
3637                                     _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown function finished item name (", item.name, "=", item.value, ")");
3638                               }
3639
3640                               event = functionEnd;
3641                               ide.Update(null);
3642                            }
3643                            else if(!strcmp(reason, "signal-received"))
3644                            {
3645       #ifdef _DEBUG
3646                               if(stopItem)
3647                                  _dpl(0, "problem");
3648       #endif
3649                               stopItem = GdbDataStop { };
3650                               stopItem.reason = CopyString(reason);
3651
3652                               for(i = tk+1; i < outTokens.count; i++)
3653                               {
3654                                  TokenizeListItem(outTokens[i], item);
3655                                  StripQuotes(item.value, item.value);
3656                                  if(!strcmp(item.name, "signal-name"))
3657                                     stopItem.name = CopyString(item.value);
3658                                  else if(!strcmp(item.name, "signal-meaning"))
3659                                     stopItem.meaning = CopyString(item.value);
3660                                  else if(!strcmp(item.name, "thread-id"))
3661                                     stopItem.threadid = atoi(item.value);
3662                                  else if(!strcmp(item.name, "frame"))
3663                                  {
3664                                     item.value = StripCurlies(item.value);
3665                                     ParseFrame(stopItem.frame, item.value);
3666                                  }
3667                                  else
3668                                     _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown signal reveived item name (", item.name, "=", item.value, ")");
3669                               }
3670                               if(!strcmp(stopItem.name, "SIGTRAP"))
3671                               {
3672                                  switch(breakType)
3673                                  {
3674                                     case internal:
3675                                        breakType = none;
3676                                        break;
3677                                     case restart:
3678                                     case stop:
3679                                        break;
3680                                     default:
3681                                        event = breakEvent;
3682                                  }
3683                               }
3684                               else
3685                               {
3686                                  event = signal;
3687                               }
3688                            }
3689                            else if(!strcmp(reason, "watchpoint-trigger"))
3690                               _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint trigger not handled");
3691                            else if(!strcmp(reason, "read-watchpoint-trigger"))
3692                               _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason read watchpoint trigger not handled");
3693                            else if(!strcmp(reason, "access-watchpoint-trigger"))
3694                               _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason access watchpoint trigger not handled");
3695                            else if(!strcmp(reason, "watchpoint-scope"))
3696                               _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint scope not handled");
3697                            else if(!strcmp(reason, "location-reached"))
3698                               _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason location reached not handled");
3699                            else
3700                               _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown reason: ", reason);
3701                         }
3702                         else
3703                         {
3704                            PrintLn(output);
3705                         }
3706                      }
3707                   }
3708                   if(usingValgrind && event == none && !stopItem)
3709                      event = valgrindStartPause;
3710                   app.SignalEvent();
3711                }
3712             }
3713             else
3714                _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown exec-async-output: ", outTokens[0]);
3715             outTokens.RemoveAll();
3716             break;
3717          case '(':
3718             if(!strcmpi(output, "(gdb) "))
3719             {
3720                if(waitingForPID)
3721                {
3722                   char exeFile[MAX_LOCATION];
3723                   int oldProcessID = targetProcessId;
3724                   GetLastDirectory(targetFile, exeFile);
3725
3726                   while(!targetProcessId/*true*/)
3727                   {
3728                      targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3729                      if(targetProcessId || gdbHandle.Peek()) break;
3730                      Sleep(0.01);
3731                   }
3732
3733                   if(targetProcessId)
3734                      ChangeState(running);
3735                   else if(!oldProcessID)
3736                   {
3737                      ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3738                      // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3739                      gdbHandle.Printf("-gdb-exit\n");
3740                      gdbTimer.Stop();
3741                      ChangeState(terminated); //loaded;
3742                      prjConfig = null;
3743
3744                      if(ide.workspace)
3745                      {
3746                         for(bp : ide.workspace.breakpoints)
3747                            bp.inserted = false;
3748                      }
3749                      for(bp : sysBPs)
3750                         bp.inserted = false;
3751                      if(bpRunToCursor)
3752                         bpRunToCursor.inserted = false;
3753
3754                      ide.outputView.debugBox.Logf($"Debugging stopped\n");
3755                      ClearBreakDisplay();
3756
3757                #if defined(__unix__)
3758                      if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
3759                      {
3760                         progThread.terminate = true;
3761                         if(fifoFile)
3762                         {
3763                            fifoFile.CloseInput();
3764                            app.Unlock();
3765                            progThread.Wait();
3766                            app.Lock();
3767                            delete fifoFile;
3768                         }
3769
3770                         DeleteFile(progFifoPath);
3771                         progFifoPath[0] = '\0';
3772                         rmdir(progFifoDir);
3773                      }
3774                #endif
3775                   }
3776                }
3777                gdbReady = true;
3778                serialSemaphore.Release();
3779             }
3780             else
3781                _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown prompt", output);
3782
3783             break;
3784          case '&':
3785             if(!strncmp(output, "&\"warning:", 10))
3786             {
3787                char * content;
3788                content = strstr(output, "\"");
3789                StripQuotes(content, content);
3790                content = strstr(content, ":");
3791                if(content)
3792                   content++;
3793                if(content)
3794                {
3795                   char * s;
3796                   ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3797                   delete s;
3798                   ide.Update(null);
3799                }
3800             }
3801             break;
3802          default:
3803             _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown output: ", output);
3804       }
3805       if(!setWaitingForPID)
3806          waitingForPID = false;
3807       setWaitingForPID = false;
3808
3809       delete outTokens;
3810       delete subTokens;
3811       delete item;
3812       delete item2;
3813    }
3814
3815    // From GDB Output functions
3816    void FGODetectLoadedLibraryForAddedProjectIssues(Array<char *> outTokens)
3817    {
3818       char path[MAX_LOCATION] = "";
3819       char file[MAX_FILENAME] = "";
3820       bool symbolsLoaded;
3821       DebugListItem item { };
3822       //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGODetectLoadedLibraryForAddedProjectIssues()");
3823       for(token : outTokens)
3824       {
3825          if(TokenizeListItem(token, item))
3826          {
3827             if(!strcmp(item.name, "target-name"))
3828             {
3829                StripQuotes(item.value, path);
3830                MakeSystemPath(path);
3831                GetLastDirectory(path, file);
3832             }
3833             else if(!strcmp(item.name, "symbols-loaded"))
3834             {
3835                symbolsLoaded = (atoi(item.value) == 1);
3836             }
3837          }
3838       }
3839       delete item;
3840       if(path[0] && file[0])
3841       {
3842          for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
3843          {
3844             bool match;
3845             char * dot;
3846             char prjTargetPath[MAX_LOCATION];
3847             char prjTargetFile[MAX_FILENAME];
3848             DirExpression targetDirExp = prj.GetTargetDir(currentCompiler, prj.config, bitDepth);
3849             strcpy(prjTargetPath, prj.topNode.path);
3850             PathCat(prjTargetPath, targetDirExp.dir);
3851             prjTargetFile[0] = '\0';
3852             prj.CatTargetFileName(prjTargetFile, currentCompiler, prj.config);
3853             PathCat(prjTargetPath, prjTargetFile);
3854             MakeSystemPath(prjTargetPath);
3855
3856             match = !fstrcmp(prjTargetFile, file);
3857             if(!match && (dot = strstr(prjTargetFile, ".so.")))
3858             {
3859                char * dot3 = strstr(dot+4, ".");
3860                if(dot3)
3861                {
3862                   dot3[0] = '\0';
3863                   match = !fstrcmp(prjTargetFile, file);
3864                }
3865                if(!match)
3866                {
3867                   dot[3] = '\0';
3868                   match = !fstrcmp(prjTargetFile, file);
3869                }
3870             }
3871             if(match)
3872             {
3873                // TODO: nice visual feedback to better warn user. use some ide notification system or other means.
3874                /* -- 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)
3875                if(!symbolsLoaded)
3876                   ide.outputView.debugBox.Logf($"Attention! No symbols for loaded library %s matched to the %s added project.\n", path, prj.topNode.name);
3877                */
3878                match = !fstrcmp(prjTargetPath, path);
3879                if(!match && (dot = strstr(prjTargetPath, ".so.")))
3880                {
3881                   char * dot3 = strstr(dot+4, ".");
3882                   if(dot3)
3883                   {
3884                      dot3[0] = '\0';
3885                      match = !fstrcmp(prjTargetPath, path);
3886                   }
3887                   if(!match)
3888                   {
3889                      dot[3] = '\0';
3890                      match = !fstrcmp(prjTargetPath, path);
3891                   }
3892                }
3893                if(!match)
3894                   ide.outputView.debugBox.Logf($"Loaded library %s doesn't match the %s target of the %s added project.\n", path, prjTargetPath, prj.topNode.name);
3895                break;
3896             }
3897          }
3898       }
3899    }
3900
3901    void FGOBreakpointModified(Array<char *> outTokens)
3902    {
3903       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGOBreakpointModified() -- TODO only if needed: support breakpoint modified");
3904 #if 0
3905       DebugListItem item { };
3906       if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3907       {
3908          if(!strcmp(item.name, "bkpt"))
3909          {
3910             GdbDataBreakpoint modBp = ParseBreakpoint(item.value, outTokens);
3911             delete modBp;
3912          }
3913       }
3914 #endif
3915    }
3916
3917
3918    ExpressionType ::DebugEvalExpTypeError(char * result)
3919    {
3920       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::DebugEvalExpTypeError()");
3921       if(result)
3922          return dummyExp;
3923       switch(eval.error)
3924       {
3925          case symbolNotFound:
3926             return symbolErrorExp;
3927          case memoryCantBeRead:
3928             return memoryErrorExp;
3929       }
3930       return unknownErrorExp;
3931    }
3932
3933    char * ::EvaluateExpression(char * expression, ExpressionType * error)
3934    {
3935       char * result;
3936       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateExpression(", expression, ")");
3937       if(ide.projectView && ide.debugger.state == stopped)
3938       {
3939          result = GdbEvaluateExpression(expression);
3940          *error = DebugEvalExpTypeError(result);
3941       }
3942       else
3943       {
3944          result = null;
3945          *error = noDebuggerErrorExp;
3946       }
3947       return result;
3948    }
3949
3950    char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
3951    {
3952       // check for state
3953       char * result = GdbReadMemoryString(address, size, format, 1, 1);
3954       _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ReadMemory(", address, ")");
3955       if(!result || !strcmp(result, "N/A"))
3956          *error = memoryErrorExp;
3957       else
3958          *error = DebugEvalExpTypeError(result);
3959       return result;
3960    }
3961 }
3962
3963 class ValgrindLogThread : Thread
3964 {
3965    Debugger debugger;
3966
3967    unsigned int Main()
3968    {
3969       static char output[4096];
3970       Array<char> dynamicBuffer { minAllocSize = 4096 };
3971       File oldValgrindHandle = vgLogFile;
3972       incref oldValgrindHandle;
3973
3974       app.Lock();
3975       while(debugger.state != terminated && vgLogFile)
3976       {
3977          int result;
3978          app.Unlock();
3979          result = vgLogFile.Read(output, 1, sizeof(output));
3980          app.Lock();
3981          if(debugger.state == terminated || !vgLogFile/* || vgLogFile.Eof()*/)
3982             break;
3983          if(result)
3984          {
3985             int c;
3986             int start = 0;
3987
3988             for(c = 0; c<result; c++)
3989             {
3990                if(output[c] == '\n')
3991                {
3992                   int pos = dynamicBuffer.size;
3993                   dynamicBuffer.size += c - start;
3994                   memcpy(&dynamicBuffer[pos], output + start, c - start);
3995                   if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3996                   // COMMENTED OUT DUE TO ISSUE #135, FIXED
3997                   //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
3998                      dynamicBuffer.size++;
3999                   dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4000 #ifdef _DEBUG
4001                   // printf("%s\n", dynamicBuffer.array);
4002 #endif
4003                   if(strstr(&dynamicBuffer[0], "vgdb me"))
4004                      debugger.serialSemaphore.Release();
4005                   ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4006                   dynamicBuffer.size = 0;
4007                   start = c + 1;
4008                }
4009             }
4010             if(c == result)
4011             {
4012                int pos = dynamicBuffer.size;
4013                dynamicBuffer.size += c - start;
4014                memcpy(&dynamicBuffer[pos], output + start, c - start);
4015             }
4016          }
4017          else if(debugger.state == stopped)
4018          {
4019 /*#ifdef _DEBUG
4020             printf("Got end of file from GDB!\n");
4021 #endif*/
4022             app.Unlock();
4023             Sleep(0.2);
4024             app.Lock();
4025          }
4026       }
4027       delete dynamicBuffer;
4028       ide.outputView.debugBox.Logf($"ValgrindLogThreadExit\n");
4029       //if(oldValgrindHandle == vgLogFile)
4030          debugger.GdbThreadExit/*ValgrindLogThreadExit*/();
4031       delete oldValgrindHandle;
4032       app.Unlock();
4033       return 0;
4034    }
4035 }
4036
4037 class ValgrindTargetThread : Thread
4038 {
4039    Debugger debugger;
4040
4041    unsigned int Main()
4042    {
4043       static char output[4096];
4044       Array<char> dynamicBuffer { minAllocSize = 4096 };
4045       DualPipe oldValgrindHandle = vgTargetHandle;
4046       incref oldValgrindHandle;
4047
4048       app.Lock();
4049       while(debugger.state != terminated && vgTargetHandle && !vgTargetHandle.Eof())
4050       {
4051          int result;
4052          app.Unlock();
4053          result = vgTargetHandle.Read(output, 1, sizeof(output));
4054          app.Lock();
4055          if(debugger.state == terminated || !vgTargetHandle || vgTargetHandle.Eof())
4056             break;
4057          if(result)
4058          {
4059             int c;
4060             int start = 0;
4061
4062             for(c = 0; c<result; c++)
4063             {
4064                if(output[c] == '\n')
4065                {
4066                   int pos = dynamicBuffer.size;
4067                   dynamicBuffer.size += c - start;
4068                   memcpy(&dynamicBuffer[pos], output + start, c - start);
4069                   if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4070                   // COMMENTED OUT DUE TO ISSUE #135, FIXED
4071                   //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4072                      dynamicBuffer.size++;
4073                   dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4074 #ifdef _DEBUG
4075                   // printf("%s\n", dynamicBuffer.array);
4076 #endif
4077                   ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4078
4079                   dynamicBuffer.size = 0;
4080                   start = c + 1;
4081                }
4082             }
4083             if(c == result)
4084             {
4085                int pos = dynamicBuffer.size;
4086                dynamicBuffer.size += c - start;
4087                memcpy(&dynamicBuffer[pos], output + start, c - start);
4088             }
4089          }
4090          else
4091          {
4092 #ifdef _DEBUG
4093             printf("Got end of file from GDB!\n");
4094 #endif
4095          }
4096       }
4097       delete dynamicBuffer;
4098       //if(oldValgrindHandle == vgTargetHandle)
4099          debugger.ValgrindTargetThreadExit();
4100       delete oldValgrindHandle;
4101       app.Unlock();
4102       return 0;
4103    }
4104 }
4105
4106 class GdbThread : Thread
4107 {
4108    Debugger debugger;
4109
4110    unsigned int Main()
4111    {
4112       static char output[4096];
4113       Array<char> dynamicBuffer { minAllocSize = 4096 };
4114       DualPipe oldGdbHandle = gdbHandle;
4115       incref oldGdbHandle;
4116
4117       app.Lock();
4118       while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
4119       {
4120          int result;
4121          app.Unlock();
4122          result = gdbHandle.Read(output, 1, sizeof(output));
4123          app.Lock();
4124          if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
4125             break;
4126          if(result)
4127          {
4128             int c;
4129             int start = 0;
4130
4131             for(c = 0; c<result; c++)
4132             {
4133                if(output[c] == '\n')
4134                {
4135                   int pos = dynamicBuffer.size;
4136                   dynamicBuffer.size += c - start;
4137                   memcpy(&dynamicBuffer[pos], output + start, c - start);
4138                   if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4139                   // COMMENTED OUT DUE TO ISSUE #135, FIXED
4140                   //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4141                      dynamicBuffer.size++;
4142                   dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4143 #ifdef _DEBUG
4144                   // _dpl(0, dynamicBuffer.array);
4145 #endif
4146                   debugger.GdbThreadMain(&dynamicBuffer[0]);
4147                   dynamicBuffer.size = 0;
4148                   start = c + 1;
4149                }
4150             }
4151             if(c == result)
4152             {
4153                int pos = dynamicBuffer.size;
4154                dynamicBuffer.size += c - start;
4155                memcpy(&dynamicBuffer[pos], output + start, c - start);
4156             }
4157          }
4158          else
4159          {
4160 #ifdef _DEBUG
4161             _dpl(0, "Got end of file from GDB!");
4162 #endif
4163          }
4164       }
4165       delete dynamicBuffer;
4166       //if(oldGdbHandle == gdbHandle)
4167          debugger.GdbThreadExit();
4168       delete oldGdbHandle;
4169       app.Unlock();
4170       return 0;
4171    }
4172 }
4173
4174 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
4175 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
4176
4177 #if defined(__unix__)
4178 #define uint _uint
4179 #include <errno.h>
4180 #include <stdio.h>
4181 #include <fcntl.h>
4182 #include <sys/types.h>
4183 #undef uint
4184
4185 File fifoFile;
4186
4187 class ProgramThread : Thread
4188 {
4189    bool terminate;
4190    unsigned int Main()
4191    {
4192       bool result = true;
4193       bool fileCreated = false;
4194       mode_t mask = 0600;
4195       static char output[1000];
4196       int fd;
4197
4198       /*if(!mkfifo(progFifoPath, mask))
4199       {
4200          fileCreated = true;
4201       }
4202       else
4203       {
4204          app.Lock();
4205          ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
4206          app.Unlock();
4207       }*/
4208
4209       if(FileExists(progFifoPath)) //fileCreated)
4210       {
4211          fifoFile = FileOpen(progFifoPath, read);
4212          if(!fifoFile)
4213          {
4214             app.Lock();
4215             ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
4216             app.Unlock();
4217          }
4218          else
4219          {
4220             fd = fileno((FILE *)fifoFile.input);
4221             //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
4222          }
4223       }
4224
4225       while(!terminate && fifoFile && !fifoFile.Eof())
4226       {
4227          fd_set rs, es;
4228          struct timeval time;
4229          int selectResult;
4230          time.tv_sec = 1;
4231          time.tv_usec = 0;
4232          FD_ZERO(&rs);
4233          FD_ZERO(&es);
4234          FD_SET(fd, &rs);
4235          FD_SET(fd, &es);
4236          selectResult = select(fd + 1, &rs, null, null, &time);
4237          if(FD_ISSET(fd, &rs))
4238          {
4239             int result = (int)read(fd, output, sizeof(output)-1);
4240             if(!result || (result < 0 && errno != EAGAIN))
4241                break;
4242             if(result > 0)
4243             {
4244                output[result] = '\0';
4245                if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
4246                {
4247                   app.Lock();
4248                   ide.outputView.debugBox.Log(output);
4249                   app.Unlock();
4250                }
4251             }
4252          }
4253       }
4254
4255       //if(fifoFile)
4256       {
4257          //fifoFile.CloseInput();
4258          //delete fifoFile;
4259          app.Lock();
4260          ide.outputView.debugBox.Log("\n");
4261          app.Unlock();
4262       }
4263       /*
4264       if(FileExists(progFifoPath)) //fileCreated)
4265       {
4266          DeleteFile(progFifoPath);
4267          progFifoPath[0] = '\0';
4268       }
4269       */
4270       return 0;
4271    }
4272 }
4273 #endif
4274
4275 class Argument : struct
4276 {
4277    Argument prev, next;
4278    char * name;
4279    property char * name { set { delete name; if(value) name = CopyString(value); } }
4280    char * val;
4281    property char * val { set { delete val; if(value) val = CopyString(value); } }
4282
4283    void Free()
4284    {
4285       delete name;
4286       delete val;
4287    }
4288
4289    ~Argument()
4290    {
4291       Free();
4292    }
4293 }
4294
4295 class Frame : struct
4296 {
4297    Frame prev, next;
4298    int level;
4299    char * addr;
4300    property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4301    char * func;
4302    property char * func { set { delete func; if(value) func = CopyString(value); } }
4303    int argsCount;
4304    OldList args;
4305    char * from;
4306    property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
4307    char * file;
4308    property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4309    char * absoluteFile;
4310    property char * absoluteFile { set { delete absoluteFile; if(value) absoluteFile = CopyUnescapedUnixPath(value); } }
4311    int line;
4312
4313    void Free()
4314    {
4315       delete addr;
4316       delete func;
4317       delete from;
4318       delete file;
4319       delete absoluteFile;
4320       args.Free(Argument::Free);
4321    }
4322
4323    ~Frame()
4324    {
4325       Free();
4326    }
4327 }
4328
4329 class GdbDataStop : struct
4330 {
4331    char * reason;
4332    int threadid;
4333    union
4334    {
4335       struct
4336       {
4337          int bkptno;
4338       };
4339       struct
4340       {
4341          char * name;
4342          char * meaning;
4343       };
4344       struct
4345       {
4346          char * gdbResultVar;
4347          char * returnValue;
4348       };
4349    };
4350    Frame frame { };
4351
4352    void Free()
4353    {
4354       if(reason)
4355       {
4356          if(!strcmp(reason, "signal-received"))
4357          {
4358             delete name;
4359             delete meaning;
4360          }
4361          else if(!strcmp(reason, "function-finished"))
4362          {
4363             delete gdbResultVar;
4364             delete returnValue;
4365          }
4366          delete reason;
4367       }
4368       if(frame) frame.Free();
4369    }
4370
4371    ~GdbDataStop()
4372    {
4373       Free();
4374    }
4375 }
4376
4377 class GdbDataBreakpoint : struct
4378 {
4379    int id;
4380    char * number;
4381    property char * number { set { delete number; if(value) number = CopyString(value); } }
4382    char * type;
4383    property char * type { set { delete type; if(value) type = CopyString(value); } }
4384    char * disp;
4385    property char * disp { set { delete disp; if(value) disp = CopyString(value); } }
4386    bool enabled;
4387    char * addr;
4388    property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4389    char * func;
4390    property char * func { set { delete func; if(value) func = CopyString(value); } }
4391    char * file;
4392    property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4393    char * fullname;
4394    property char * fullname { set { delete fullname; if(value) fullname = CopyUnescapedUnixPath(value); } }
4395    int line;
4396    char * at;
4397    property char * at { set { delete at; if(value) at = CopyString(value); } }
4398    int times;
4399
4400    Array<GdbDataBreakpoint> multipleBPs;
4401
4402    void Print()
4403    {
4404    _dpl(0, "");
4405       PrintLn("{", "#", number, " T", type, " D", disp, " E", enabled, " H", times, " (", func, ") (", file, ":", line, ") (", fullname, ") (", addr, ") (", at, ")", "}");
4406    }
4407
4408    void Free()
4409    {
4410       delete type;
4411       delete disp;
4412       delete addr;
4413       delete func;
4414       delete file;
4415       delete at;
4416       if(multipleBPs) multipleBPs.Free();
4417       delete multipleBPs;
4418    }
4419
4420    ~GdbDataBreakpoint()
4421    {
4422       Free();
4423    }
4424 }
4425
4426 class Breakpoint : struct
4427 {
4428    class_no_expansion;
4429
4430    char * function;
4431    property char * function { set { delete function; if(value) function = CopyString(value); } }
4432    char * relativeFilePath;
4433    property char * relativeFilePath { set { delete relativeFilePath; if(value) relativeFilePath = CopyString(value); } }
4434    char * absoluteFilePath;
4435    property char * absoluteFilePath { set { delete absoluteFilePath; if(value) absoluteFilePath = CopyString(value); } }
4436    int line;
4437    bool enabled;
4438    int hits;
4439    int breaks;
4440    int ignore;
4441    int level;
4442    Watch condition;
4443    bool inserted;
4444    BreakpointType type;
4445    DataRow row;
4446    GdbDataBreakpoint bp;
4447
4448    char * CopyLocationString(bool removePath)
4449    {
4450       char * location;
4451       char * file = relativeFilePath ? relativeFilePath : absoluteFilePath;
4452       bool removingPath = removePath && file;
4453       if(removingPath)
4454       {
4455          char * fileName = new char[MAX_FILENAME];
4456          GetLastDirectory(file, fileName);
4457          file = fileName;
4458       }
4459       if(function)
4460       {
4461          if(file)
4462             location = PrintString(file, ":", function);
4463          else
4464             location = CopyString(function);
4465       }
4466       else
4467          location = PrintString(file, ":", line);
4468       if(removingPath)
4469          delete file;
4470       return location;
4471    }
4472
4473    char * CopyUserLocationString()
4474    {
4475       char * location;
4476       char * loc = CopyLocationString(false);
4477       Project prj = null;
4478       for(p : ide.workspace.projects)
4479       {
4480          if(p.topNode.FindByFullPath(absoluteFilePath, false))
4481          {
4482             prj = p;
4483             break;
4484          }
4485       }
4486       if(prj)
4487       {
4488          location = PrintString("(", prj.name, ")", loc);
4489          delete loc;
4490       }
4491       else
4492          location = loc;
4493       return location;
4494    }
4495
4496    void Save(File f)
4497    {
4498       if(relativeFilePath && relativeFilePath[0])
4499       {
4500          f.Printf("    * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
4501          if(condition)
4502             f.Printf("       ~ %s\n", condition.expression);
4503       }
4504    }
4505
4506    void Free()
4507    {
4508       if(bp)
4509          bp.Free();
4510       delete bp;
4511       delete function;
4512       delete relativeFilePath;
4513       delete absoluteFilePath;
4514    }
4515
4516    ~Breakpoint()
4517    {
4518       Free();
4519    }
4520
4521 }
4522
4523 class Watch : struct
4524 {
4525    class_no_expansion;
4526    
4527    Type type;
4528    char * expression;
4529    char * value;
4530    DataRow row;
4531
4532    void Save(File f)
4533    {
4534       f.Printf("    ~ %s\n", expression);
4535    }
4536
4537    void Free()
4538    {
4539       delete expression;
4540       delete value;
4541       FreeType(type);
4542       type = null;
4543    }
4544
4545    void Reset()
4546    {
4547       delete value;
4548       FreeType(type);
4549       type = null;
4550    }
4551
4552    ~Watch()
4553    {
4554       Free();
4555    }
4556 }
4557
4558 class DebugListItem : struct
4559 {
4560    char * name;
4561    char * value;
4562 }
4563
4564 struct DebugEvaluationData
4565 {
4566    bool active;
4567    char * result;
4568    int bytes;
4569    uint64 nextBlockAddress;
4570
4571    DebuggerEvaluationError error;
4572 };
4573
4574 class CodeLocation : struct
4575 {
4576    char * file;
4577    char * absoluteFile;
4578    int line;
4579
4580    CodeLocation ::ParseCodeLocation(char * location)
4581    {
4582       if(location)
4583       {
4584          char * colon = null;
4585          char * temp;
4586          char loc[MAX_LOCATION];
4587          strcpy(loc, location);
4588          for(temp = loc; temp = strstr(temp, ":"); temp++)
4589             colon = temp;
4590          if(colon)
4591          {
4592             colon[0] = '\0';
4593             colon++;
4594             if(colon)
4595             {
4596                int line = atoi(colon);
4597                if(line)
4598                {
4599                   CodeLocation codloc { line = line };
4600                   codloc.file = CopyString(loc);
4601                   codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
4602                   return codloc;
4603                }
4604             }
4605          }
4606       }
4607       return null;
4608    }
4609
4610    void Free()
4611    {
4612       delete file;
4613       delete absoluteFile;
4614    }
4615
4616    ~CodeLocation()
4617    {
4618       Free();
4619    }
4620 }
4621
4622 void GDBFallBack(Expression exp, String expString)
4623 {
4624    char * result;
4625    ExpressionType evalError = dummyExp;
4626    result = Debugger::EvaluateExpression(expString, &evalError);
4627    if(result)
4628    {
4629       exp.constant = result;
4630       exp.type = constantExp;
4631    }
4632 }