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