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