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