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