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