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