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