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