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