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