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