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