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