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