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