2 public import static "ecere"
3 public import static "ec"
15 #define GDB_DEBUG_CONSOLE
18 extern char * strrchr(const char * s, int c);
21 #define strlen _strlen
31 #include <sys/time.h> // Required on Apple...
45 sprintf(s[0], "%04d", now.year);
46 sprintf(s[1], "%02d", now.month+1);
47 sprintf(s[2], "%02d", now.day);
48 sprintf(s[3], "%02d", now.hour);
49 sprintf(s[4], "%02d", now.minute);
50 sprintf(s[5], "%02d", now.second);
51 time = PrintString("*", s[0], s[1], s[2], "-", s[3], s[4], s[5], "*");
57 // use =0 to disable printing of specific channels
59 static enum dplchan { none, gdbProtoIgnored=0/*1*/, gdbProtoUnknown=2, gdbOutput=3/*3*/, gdbCommand=4/*4*/, debuggerCall=5, debuggerProblem=6, debuggerTemp=7 };
61 static enum dplchan { none, gdbProtoIgnored=0, gdbProtoUnknown=0, gdbOutput=0, gdbCommand=0, debuggerCall=0, debuggerProblem=0, debuggerTemp=0 };
63 static char * _dpct[] = {
65 "GDB Protocol Ignored",
66 "GDB Protocol ***Unknown***",
70 "Debugger ***Problem***",
71 "-----> Temporary Message",
75 // TODO if(strlen(item.value) < MAX_F_STRING)
77 #define _dpl2(...) __dpl2(__FILE__, __LINE__, ##__VA_ARGS__)
78 static void __dpl2(char * file, int line, char ** channels, int channel, int indent, typed_object object, ...)
80 bool chan = channel && channels && channels[channel];
83 char string[MAX_F_STRING];
85 char * time = PrintNow();
87 //ide.outputView.debugBox.Logf();
88 Logf("%s %s:% 5d: %s%s", time, file, line, chan ? channels[channel] : "", chan && channels[channel][0] ? ": " : "");
89 va_start(args, object);
90 len = PrintStdArgsToBuffer(string, sizeof(string), object, args);
98 #define _dpl(...) __dpl(__FILE__, __LINE__, ##__VA_ARGS__)
99 static void __dpl(char * file, int line, int indent, char * format, ...)
102 char string[MAX_F_STRING];
104 char * time = PrintNow();
105 //static File f = null;
106 va_start(args, format);
107 vsnprintf(string, sizeof(string), format, args);
108 string[sizeof(string)-1] = 0;
111 char * time = PrintNow();
113 logName = PrintString(time, ".log");
115 f = FileOpen(logName, write);
118 /*f.Printf("%s %s:% 5d: ", time, file, line);
119 for(c = 0; c<indent; c++)
121 f.Printf("%s\n", string);*/
122 Logf("%s %s:% 5d: ", time, file, line);
123 for(c = 0; c<indent; c++)
125 Logf("%s\n", string);
130 public char * StripQuotes2(char * string, char * output)
134 bool quoted = false, escaped = false;
136 for(c = 0; ch = string[c]; c++)
140 if(escaped || ch != '\"')
143 escaped = !escaped && ch == '\\';
157 // String Escape Copy
158 static void strescpy(char * d, char * s)
211 static char * CopyUnescapedSystemPath(char * p)
213 char * d = new char[strlen(p) + 1];
215 #if defined(__WIN32__)
216 ChangeCh(d, '/', '\\');
221 static char * CopyUnescapedUnixPath(char * p)
223 char * d = new char[strlen(p) + 1];
225 #if defined(__WIN32__)
226 ChangeCh(d, '\\', '/');
231 static char * CopyUnescapedString(char * s)
233 char * d = new char[strlen(s) + 1];
238 // String Unescape Copy
240 // TOFIX: THIS DOESN'T HANDLE NUMERIC ESCAPE CODES (OCTAL/HEXADECIMAL...)?
241 // Seems very similar to ReadString in pass15.ec (which also misses numeric escape codes :) )
243 static void struscpy(char * d, char * s)
295 static char * StripBrackets(char * string)
297 int length = strlen(string);
298 if(length > 1 && *string == '[' && string[length - 1] == ']')
301 string[length - 1] = '\0';
308 static char * StripCurlies(char * string)
310 int length = strlen(string);
311 if(length > 1 && *string == '{' && string[length - 1] == '}')
314 string[length - 1] = '\0';
321 static int StringGetInt(char * string, int start)
324 int i, len = strlen(string);
326 for(i = start; i < len && i < start + 8; i++)
328 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')
329 strncat(number, &string[i], 1);
336 static int TokenizeList(char * string, const char seperator, Array<char *> tokens)
340 bool quoted = false, escaped = false;
341 char * start = string, ch;
343 for(; (ch = *string); string++)
350 if(escaped || ch != '\"')
351 escaped = !escaped && ch == '\\';
357 else if(ch == '{' || ch == '[' || ch == '(' || ch == '<')
359 else if(ch == '}' || ch == ']' || ch == ')' || ch == '>')
361 else if(ch == seperator && !level)
370 //tokens[count] = start;
371 //tokens[count++] = start;
378 static bool TokenizeListItem(char * string, DebugListItem item)
380 char * equal = strstr(string, "=");
394 static bool CheckCommandAvailable(const char * command)
396 bool available = false;
398 char * name = new char[MAX_FILENAME];
399 char * pathVar = new char[maxPathLen];
401 GetEnvironment("PATH", pathVar, maxPathLen);
402 count = TokenizeWith(pathVar, sizeof(paths) / sizeof(char *), paths, pathListSep, false);
403 strcpy(name, command);
407 const char * extensions[] = { "exe", "com", "bat", null };
408 for(e=0; extensions[e]; e++)
410 ChangeExtension(name, extensions[e], name);
412 for(c=0; c<count; c++)
414 FileListing fl { paths[c] };
417 if(fl.stats.attribs.isFile && !fstrcmp(fl.name, name))
435 // define GdbGetLineSize = 1638400;
436 define GdbGetLineSize = 5638400;
437 #if defined(__unix__)
438 char progFifoPath[MAX_LOCATION];
439 char progFifoDir[MAX_LOCATION];
442 enum DebuggerState { none, prompt, loaded, running, stopped, terminated, error };
445 none, hit, breakEvent, signal, stepEnd, functionEnd, exit;
447 property bool canBeMonitored { get { return (this == hit || this == breakEvent || this == signal || this == stepEnd || this == functionEnd); } };
449 enum DebuggerAction { none, internal, restart, stop, selectFrame }; //, bpValidation
452 none, internalMain, internalWinMain, internalModulesLoaded, user, runToCursor, internalModuleLoad;
454 property bool isInternal { get { return (this == internalMain || this == internalWinMain || this == internalModulesLoaded || this == internalModuleLoad); } };
455 property bool isUser { get { return (this == user || this == runToCursor); } };
457 enum DebuggerEvaluationError { none, symbolNotFound, memoryCantBeRead, unknown };
459 FileDialog debuggerFileDialog { type = selectDir };
461 static DualPipe vgTargetHandle;
462 static File vgLogFile;
463 static char vgLogPath[MAX_LOCATION];
464 static DualPipe gdbHandle;
465 static DebugEvaluationData eval { };
467 static int targetProcessId;
469 static bool gdbReady;
470 static bool breakpointError;
474 Semaphore serialSemaphore { };
480 bool sentBreakInsert;
481 bool ignoreBreakpoints;
482 bool userBreakOnInternalBreakpoint;
483 //bool runToCursorDebugStart;
492 int activeFrameLevel;
503 DebuggerAction breakType;
504 //DebuggerCommand lastCommand; // THE COMPILER COMPILES STUFF THAT DOES NOT EXIST???
506 GdbDataStop stopItem;
507 GdbDataBreakpoint bpItem;
510 List<Breakpoint> sysBPs { };
511 Breakpoint bpRunToCursor;
515 CompilerConfig currentCompiler;
516 ProjectConfig prjConfig;
519 CodeEditor codeEditor;
521 ValgrindLogThread vgLogThread { debugger = this };
522 ValgrindTargetThread vgTargetThread { debugger = this };
523 GdbThread gdbThread { debugger = this };
526 delay = 0.0, userData = this;
530 bool monitor = false;
531 DebuggerEvent curEvent = event;
532 GdbDataStop stopItem = this.stopItem;
533 Breakpoint bpUser = null;
534 Breakpoint bpInternal = null;
541 this.stopItem = null;
544 if(curEvent && curEvent != exit)
547 _dpl(0, "No stop item");
555 Restart(currentCompiler, prjConfig, bitDepth, usingValgrind);
564 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
565 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
566 if(activeFrame.level == activeFrameLevel)
572 // GdbCommand(false, "-break-info %s", bpItem.number);
582 activeThread = stopItem.threadid;
583 GdbCommand(false, "-thread-list-ids");
589 Breakpoint bp = GetBreakpointById(stopItem.bkptno, &isInternal);
590 if(bp && bp.inserted && bp.bp.addr)
592 if(bp.type.isInternal)
596 if(stopItem && stopItem.frame)
598 if(bpInternal && bpRunToCursor && bpRunToCursor.inserted && !strcmp(bpRunToCursor.bp.addr, bp.bp.addr))
599 bpUser = bpRunToCursor;
602 for(item : (bpInternal ? ide.workspace.breakpoints : sysBPs); item.inserted)
604 if(item.bp && item.bp.addr && !strcmp(item.bp.addr, bp.bp.addr))
616 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Invalid stopItem!");
619 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Breakpoint bkptno(", stopItem.bkptno, ") invalid or not found!");
620 if(bpUser && bpUser.type == runToCursor)
621 ignoreBreakpoints = false;
622 if((bpUser && !ignoreBreakpoints) || (bpInternal && userBreakOnInternalBreakpoint))
624 hitThread = stopItem.threadid;
628 signalThread = stopItem.threadid;
632 ignoreBreakpoints = false;
641 activeThread = stopItem.threadid;
642 GdbCommand(false, "-thread-list-ids");
644 if(activeFrameLevel > 0)
645 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
647 WatchesCodeEditorLinkInit();
651 if(curEvent == signal)
655 ide.outputView.debugBox.Logf($"Signal received: %s - %s\n", stopItem.name, stopItem.meaning);
656 ide.outputView.debugBox.Logf(" %s:%d\n", (s = CopySystemPath(stopItem.frame.file)), stopItem.frame.line);
657 ide.outputView.Show();
658 ide.callStackView.Show();
661 else if(curEvent == breakEvent)
663 ide.threadsView.Show();
664 ide.callStackView.Show();
665 ide.callStackView.Activate();
668 if(monitor && curEvent.canBeMonitored)
670 SelectFrame(activeFrameLevel);
671 GoToStackFrameLine(activeFrameLevel, true);
672 ide.ShowCodeEditor();
673 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
678 EventHit(stopItem, bpInternal, bpUser);
685 if(userBreakOnInternalBreakpoint)
686 userBreakOnInternalBreakpoint = false;
691 #ifdef GDB_DEBUG_CONSOLE
692 char lastGdbOutput[GdbGetLineSize];
694 #if defined(__unix__)
695 ProgramThread progThread { };
698 void ChangeState(DebuggerState value)
700 bool same = value == state;
701 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ChangeState (", state, same ? " *** == *** " : " -> ", value, ")");
703 if(!same && ide) ide.AdjustDebugMenus();
708 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::CleanUp");
710 stackFrames.Free(Frame::Free);
720 waitingForPID = false;
725 sentBreakInsert = false;
726 ignoreBreakpoints = false;
727 userBreakOnInternalBreakpoint = false;
728 //runToCursorDebugStart = false;
731 activeFrameLevel = 0;
748 bpRunToCursor = null;
750 delete currentCompiler;
754 /*GdbThread gdbThread
760 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::constructor");
761 ideProcessId = Process_GetCurrentProcessId();
763 sysBPs.Add(Breakpoint { type = internalMain, function = "main", enabled = true, level = -1 });
764 #if defined(__WIN32__)
765 sysBPs.Add(Breakpoint { type = internalWinMain, function = "WinMain", enabled = true, level = -1 });
767 sysBPs.Add(Breakpoint { type = internalModulesLoaded, enabled = true, level = -1 });
768 sysBPs.Add(Breakpoint { type = internalModuleLoad, function = "InternalModuleLoadBreakpoint", enabled = true, level = -1 });
773 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::destructor");
781 property bool isActive { get { return state == running || state == stopped; } }
782 property bool isPrepared { get { return state == loaded || state == running || state == stopped; } }
786 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Resume");
787 GdbExecContinue(true);
792 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Break");
796 GdbDebugBreak(false);
802 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Stop");
809 GdbDebugBreak(false);
823 void Restart(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
825 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Restart");
826 if(StartSession(compiler, config, bitDepth, useValgrind, true, false, false/*, false*/) == loaded)
830 bool GoToCodeLine(char * location)
833 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GoToCodeLine(", location, ")");
834 codloc = CodeLocation::ParseCodeLocation(location);
837 CodeEditor editor = (CodeEditor)ide.OpenFile(codloc.absoluteFile, normal, true, null, no, normal, false);
840 EditBox editBox = editor.editBox;
841 editBox.GoToLineNum(codloc.line - 1);
842 editBox.GoToPosition(editBox.line, codloc.line - 1, 0);
849 bool GoToStackFrameLine(int stackLevel, bool askForLocation)
851 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GoToStackFrameLine(", stackLevel, ", ", askForLocation, ")");
854 char filePath[MAX_LOCATION];
855 char sourceDir[MAX_LOCATION];
857 CodeEditor editor = null;
858 if(stackLevel == -1) // this (the two lines) is part of that fix that I would not put in for some time
860 for(frame = stackFrames.first; frame; frame = frame.next)
861 if(frame.level == stackLevel)
865 ide.callStackView.Show();
867 if(!frame.absoluteFile && frame.file)
868 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
869 if(!frame.absoluteFile && askForLocation && frame.file)
872 char title[MAX_LOCATION];
873 snprintf(title, sizeof(title), $"Provide source file location for %s", (s = CopySystemPath(frame.file)));
874 title[sizeof(title)-1] = 0;
876 if(SourceDirDialog(title, ide.workspace.projectDir, frame.file, sourceDir))
878 AddSourceDir(sourceDir);
879 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
882 if(frame.absoluteFile)
883 editor = (CodeEditor)ide.OpenFile(frame.absoluteFile, normal, true, null, no, normal, false);
885 if(editor && frame.line)
887 EditBox editBox = editor.editBox;
888 editBox.GoToLineNum(frame.line - 1);
889 editBox.GoToPosition(editBox.line, frame.line - 1, 0);
897 void SelectThread(int thread)
899 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SelectThread(", thread, ")");
902 if(thread != activeThread)
904 activeFrameLevel = -1;
905 ide.callStackView.Clear();
906 GdbCommand(false, "-thread-select %d", thread);
908 // Why was SelectFrame missing here?
909 SelectFrame(activeFrameLevel);
910 GoToStackFrameLine(activeFrameLevel, true);
911 WatchesCodeEditorLinkRelease();
912 WatchesCodeEditorLinkInit();
916 ide.callStackView.Show();
920 void SelectFrame(int frame)
922 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SelectFrame(", frame, ")");
925 if(frame != activeFrameLevel || !codeEditor || !codeEditor.visible)
927 activeFrameLevel = frame; // there is no active frame number in the gdb reply
928 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
929 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
930 if(activeFrame.level == activeFrameLevel)
933 WatchesCodeEditorLinkRelease();
934 WatchesCodeEditorLinkInit();
941 void HandleExit(char * reason, char * code)
943 bool returnedExitCode = false;
944 char verboseExitCode[128];
946 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::HandleExit(", reason, ", ", code, ")");
947 ChangeState(loaded); // this state change seems to be superfluous, might be in case of gdb crash
952 snprintf(verboseExitCode, sizeof(verboseExitCode), $" with exit code %s", code);
953 verboseExitCode[sizeof(verboseExitCode)-1] = 0;
956 verboseExitCode[0] = '\0';
960 // ClearBreakDisplay();
964 for(wh : ide.workspace.watches)
966 if(wh.type) FreeType(wh.type);
969 ide.watchesView.UpdateWatch(wh);
973 #if defined(__unix__)
976 progThread.terminate = true;
979 fifoFile.CloseInput();
989 char program[MAX_LOCATION];
990 GetSystemPathBuffer(program, targetFile);
992 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
993 else if(!strcmp(reason, "exited-normally"))
994 ide.outputView.debugBox.Logf($"The program %s has exited normally%s.\n", program, verboseExitCode);
995 else if(!strcmp(reason, "exited"))
996 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
997 else if(!strcmp(reason, "exited-signalled"))
998 ide.outputView.debugBox.Logf($"The program %s has exited with a signal%s.\n", program, verboseExitCode);
1000 ide.outputView.debugBox.Logf($"The program %s has exited (gdb provided an unknown reason)%s.\n", program, verboseExitCode);
1005 DebuggerState StartSession(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool restart, bool userBreakOnInternalBreakpoint, bool ignoreBreakpoints/*, bool runToCursorDebugStart*/)
1007 DebuggerState result = none;
1008 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StartSession(restart(", restart, "), userBreakOnInternalBreakpoint(", userBreakOnInternalBreakpoint, "), ignoreBreakpoints(", ignoreBreakpoints, ")"/*, runToCursorDebugStart(", runToCursorDebugStart, ")"*/);
1009 if(restart && state == running && targetProcessId)
1011 breakType = DebuggerAction::restart;
1012 GdbDebugBreak(false);
1016 if(restart && state == stopped)
1018 if(needReset && state == loaded)
1019 GdbExit(); // this reset is to get a clean state with all the breakpoints until a better state can be maintained on program exit
1021 if(result == none || result == terminated)
1023 ide.outputView.ShowClearSelectTab(debug);
1024 ide.outputView.debugBox.Logf($"Starting debug mode\n");
1031 for(bp : ide.workspace.breakpoints)
1037 //this.runToCursorDebugStart = runToCursorDebugStart;
1039 if(GdbInit(compiler, config, bitDepth, useValgrind))
1044 this.ignoreBreakpoints = ignoreBreakpoints;
1045 this.userBreakOnInternalBreakpoint = userBreakOnInternalBreakpoint;
1046 if(ignoreBreakpoints && (result == loaded || result == stopped))
1047 GdbBreakpointsDelete(false, false);
1052 void Start(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
1054 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Start()");
1055 if(StartSession(compiler, config, bitDepth, useValgrind, true, false, false/*, false*/) == loaded)
1059 void StepInto(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
1061 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepInto()");
1062 switch(StartSession(compiler, config, bitDepth, useValgrind, false, true, false/*, false*/))
1064 case loaded: GdbExecRun(); break;
1065 case stopped: GdbExecStep(); break;
1069 void StepOver(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool ignoreBreakpoints)
1071 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepOver()");
1072 switch(StartSession(compiler, config, bitDepth, useValgrind, false, true, ignoreBreakpoints/*, false*/))
1074 case loaded: GdbExecRun(); break;
1075 case stopped: GdbExecNext(); break;
1079 void StepOut(bool ignoreBreakpoints)
1081 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepOut()");
1082 if(state == stopped)
1084 this.ignoreBreakpoints = ignoreBreakpoints;
1085 if(ignoreBreakpoints)
1086 GdbBreakpointsDelete(true, false);
1091 void RunToCursor(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, char * absoluteFilePath, int lineNumber, bool ignoreBreakpoints, bool atSameLevel)
1093 char relativeFilePath[MAX_LOCATION];
1094 DebuggerState st = state;
1095 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::RunToCursor()");
1098 // ide.outputView.ShowClearSelectTab(debug);
1099 // ide.outputView.debugBox.Logf($"Starting debug mode\n");
1101 if(!ide.projectView.project.GetRelativePath(absoluteFilePath, relativeFilePath))
1102 strcpy(relativeFilePath, absoluteFilePath);
1104 if(bpRunToCursor && bpRunToCursor.inserted && symbols)
1106 UnsetBreakpoint(bpRunToCursor);
1107 delete bpRunToCursor;
1110 bpRunToCursor = Breakpoint { };
1111 bpRunToCursor.absoluteFilePath = absoluteFilePath;
1112 bpRunToCursor.relativeFilePath = relativeFilePath;
1113 bpRunToCursor.line = lineNumber;
1114 bpRunToCursor.type = runToCursor;
1115 bpRunToCursor.enabled = true;
1116 bpRunToCursor.level = atSameLevel ? frameCount - activeFrameLevel -1 : -1;
1118 switch(StartSession(compiler, config, bitDepth, useValgrind, false, false, ignoreBreakpoints/*, true*/))
1124 GdbExecContinue(true);
1129 void GetCallStackCursorLine(bool * error, int * lineCursor, int * lineTopFrame)
1131 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetCallStackCursorLine()");
1132 if(activeFrameLevel == -1)
1140 *error = signalOn && activeThread == signalThread;
1141 *lineCursor = activeFrameLevel - ((frameCount > 192 && activeFrameLevel > 191) ? frameCount - 192 - 1 : 0) + 1;
1142 *lineTopFrame = activeFrameLevel ? 1 : 0;
1146 int GetMarginIconsLineNumbers(char * fileName, int lines[], bool enabled[], int max, bool * error, int * lineCursor, int * lineTopFrame)
1148 char winFilePath[MAX_LOCATION];
1149 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1151 Iterator<Breakpoint> it { ide.workspace.breakpoints };
1152 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetMarginIconsLineNumbers()");
1153 while(it.Next() && count < max)
1155 Breakpoint bp = it.data;
1158 if(bp.absoluteFilePath && bp.absoluteFilePath[0] && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1160 lines[count] = bp.line;
1161 enabled[count] = bp.enabled;
1166 if(activeFrameLevel == -1)
1174 *error = signalOn && activeThread == signalThread;
1175 if(activeFrame && activeFrame.absoluteFile && !fstrcmp(absoluteFilePath, activeFrame.absoluteFile))
1176 *lineCursor = activeFrame.line;
1179 if(activeFrame && stopItem && stopItem.frame && activeFrame.level == stopItem.frame.level)
1181 else if(stopItem && stopItem.frame && stopItem.frame.absoluteFile && !fstrcmp(absoluteFilePath, stopItem.frame.absoluteFile))
1182 *lineTopFrame = stopItem.frame.line;
1186 if(*lineTopFrame == *lineCursor && *lineTopFrame)
1192 void ChangeWatch(DataRow row, char * expression)
1194 Watch wh = (Watch)row.tag;
1195 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ChangeWatch(", expression, ")");
1198 delete wh.expression;
1200 wh.expression = CopyString(expression);
1203 Iterator<Watch> it { ide.workspace.watches };
1205 ide.workspace.watches.Delete(it.pointer);
1211 row.tag = (int64)wh;
1212 ide.workspace.watches.Add(wh);
1214 wh.expression = CopyString(expression);
1216 ide.workspace.Save();
1217 //if(expression && state == stopped)
1222 void MoveIcons(char * fileName, int lineNumber, int move, bool start)
1224 char winFilePath[MAX_LOCATION];
1225 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1228 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::MoveIcons()");
1229 for(bpLink = ide.workspace.breakpoints.first; bpLink; bpLink = next)
1231 Breakpoint bp = (Breakpoint)bpLink.data;
1234 if(bp.type == user && bp.absoluteFilePath && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1236 if(bp.line > lineNumber || (bp.line == lineNumber && start))
1238 if(move < 0 && (bp.line < lineNumber - move))
1239 ide.workspace.RemoveBreakpoint(bp);
1243 ide.breakpointsView.UpdateBreakpoint(bp.row);
1244 ide.workspace.Save();
1250 // moving code cursors is futile, on next step, stop, hit, cursors will be offset anyways
1253 bool SourceDirDialog(char * title, char * startDir, char * test, char * sourceDir)
1257 String srcDir = null;
1259 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SourceDirDialog()");
1260 debuggerFileDialog.text = title;
1261 debuggerFileDialog.currentDirectory = startDir;
1262 debuggerFileDialog.master = ide;
1264 while(debuggerFileDialog.Modal())
1266 strcpy(sourceDir, debuggerFileDialog.filePath);
1267 if(!fstrcmp(ide.workspace.projectDir, sourceDir) &&
1268 MessageBox { type = yesNo, master = ide,
1269 contents = $"This is the project directory.\nWould you like to try again?",
1270 text = $"Invalid Source Directory" }.Modal() == no)
1274 for(dir : ide.workspace.sourceDirs)
1276 if(!fstrcmp(dir, sourceDir))
1284 MessageBox { type = yesNo, master = ide,
1285 contents = $"This source directory is already specified.\nWould you like to try again?",
1286 text = $"Invalid Source Directory" }.Modal() == no)
1292 char file[MAX_LOCATION];
1293 strcpy(file, sourceDir);
1294 PathCat(file, test);
1295 result = FileExists(file);
1297 MessageBox { type = yesNo, master = ide,
1298 contents = $"Unable to locate source file.\nWould you like to try again?",
1299 text = $"Invalid Source Directory" }.Modal() == no)
1313 void AddSourceDir(char * sourceDir)
1315 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::AddSourceDir(", sourceDir, ")");
1316 ide.workspace.sourceDirs.Add(CopyString(sourceDir));
1317 ide.workspace.Save();
1321 DebuggerState oldState = state;
1326 GdbDebugBreak(true);
1329 GdbCommand(false, "-environment-directory \"%s\"", sourceDir);
1332 if(oldState == running)
1333 GdbExecContinue(false);
1337 void ToggleBreakpoint(char * fileName, int lineNumber, Project prj)
1339 char winFilePath[MAX_LOCATION];
1340 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1341 char absolutePath[MAX_LOCATION];
1342 char relativePath[MAX_LOCATION];
1343 char sourceDir[MAX_LOCATION];
1344 Breakpoint bp = null;
1346 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ToggleBreakpoint(", fileName, ":", lineNumber, ")");
1347 strcpy(absolutePath, absoluteFilePath);
1348 for(i : ide.workspace.breakpoints; i.type == user && i.absoluteFilePath && !fstrcmp(i.absoluteFilePath, absolutePath) && i.line == lineNumber)
1357 ide.workspace.RemoveBreakpoint(bp);
1365 // FIXED: This is how it should have been... Source locations are only for files not in project
1366 // if(IsPathInsideOf(absolutePath, ide.workspace.projectDir))
1367 // MakePathRelative(absolutePath, ide.workspace.projectDir, relativePath);
1368 bool result = false;
1370 result = prj.GetRelativePath(absolutePath, relativePath);
1372 result = ide.projectView.project.GetRelativePath(absolutePath, relativePath);
1373 //if(ide.projectView.project.GetRelativePath(absolutePath, relativePath));
1377 char title[MAX_LOCATION];
1378 char directory[MAX_LOCATION];
1379 StripLastDirectory(absolutePath, directory);
1380 snprintf(title, sizeof(title), $"Provide source files location directory for %s", absolutePath);
1381 title[sizeof(title)-1] = 0;
1384 String srcDir = null;
1385 for(dir : ide.workspace.sourceDirs)
1387 if(IsPathInsideOf(absolutePath, dir))
1389 MakePathRelative(absoluteFilePath, dir, relativePath);
1397 if(SourceDirDialog(title, directory, null, sourceDir))
1399 if(IsPathInsideOf(absolutePath, sourceDir))
1401 AddSourceDir(sourceDir);
1402 MakePathRelative(absoluteFilePath, sourceDir, relativePath);
1405 else if(MessageBox { type = yesNo, master = ide,
1406 contents = $"You must provide a valid source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1407 text = $"Invalid Source Directory" }.Modal() == no)
1410 else if(MessageBox { type = yesNo, master = ide,
1411 contents = $"You must provide a source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1412 text = $"No Source Directory Provided" }.Modal() == no)
1416 ide.workspace.bpCount++;
1417 bp = { line = lineNumber, type = user, enabled = true, level = -1 };
1418 ide.workspace.breakpoints.Add(bp);
1419 bp.absoluteFilePath = absolutePath;
1420 bp.relativeFilePath = relativePath;
1421 ide.breakpointsView.AddBreakpoint(bp);
1426 DebuggerState oldState = state;
1431 GdbDebugBreak(true);
1434 SetBreakpoint(bp, false);
1437 if(oldState == running)
1438 GdbExecContinue(false);
1441 ide.workspace.Save();
1444 void UpdateRemovedBreakpoint(Breakpoint bp)
1446 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::UpdateRemovedBreakpoint()");
1447 if(targeted && bp.inserted)
1449 DebuggerState oldState = state;
1454 GdbDebugBreak(true);
1457 UnsetBreakpoint(bp);
1460 if(oldState == running)
1461 GdbExecContinue(false);
1467 void ParseFrame(Frame frame, char * string)
1470 Array<char *> frameTokens { minAllocSize = 50 };
1471 Array<char *> argsTokens { minAllocSize = 50 };
1472 Array<char *> argumentTokens { minAllocSize = 50 };
1473 DebugListItem item { };
1476 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ParseFrame()");
1477 TokenizeList(string, ',', frameTokens);
1478 for(i = 0; i < frameTokens.count; i++)
1480 if(TokenizeListItem(frameTokens[i], item))
1482 StripQuotes(item.value, item.value);
1483 if(!strcmp(item.name, "level"))
1484 frame.level = atoi(item.value);
1485 else if(!strcmp(item.name, "addr"))
1486 frame.addr = item.value;
1487 else if(!strcmp(item.name, "func"))
1488 frame.func = item.value;
1489 else if(!strcmp(item.name, "args"))
1491 if(!strcmp(item.value, "[]"))
1492 frame.argsCount = 0;
1495 item.value = StripBrackets(item.value);
1496 TokenizeList(item.value, ',', argsTokens);
1497 for(j = 0; j < argsTokens.count; j++)
1499 argsTokens[j] = StripCurlies(argsTokens[j]);
1500 TokenizeList(argsTokens[j], ',', argumentTokens);
1501 for(k = 0; k < argumentTokens.count; k++)
1504 frame.args.Add(arg);
1505 if(TokenizeListItem(argumentTokens[k], item))
1507 if(!strcmp(item.name, "name"))
1509 StripQuotes(item.value, item.value);
1510 arg.name = item.value;
1512 else if(!strcmp(item.name, "value"))
1514 StripQuotes(item.value, item.value);
1515 arg.val = item.value;
1518 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "frame args item (", item.name, "=", item.value, ") is unheard of");
1521 _dpl(0, "Bad frame args item");
1523 argumentTokens.RemoveAll();
1525 frame.argsCount = argsTokens.count;
1526 argsTokens.RemoveAll();
1529 else if(!strcmp(item.name, "from"))
1530 frame.from = item.value;
1531 else if(!strcmp(item.name, "file"))
1532 frame.file = item.value;
1533 else if(!strcmp(item.name, "line"))
1534 frame.line = atoi(item.value);
1535 else if(!strcmp(item.name, "fullname"))
1536 frame.absoluteFile = item.value;
1538 // GDB 6.3 on OS X is giving "fullname" and "dir", all in absolute, but file name only in 'file'
1539 String path = ide.workspace.GetPathWorkspaceRelativeOrAbsolute(item.value);
1540 if(strcmp(frame.file, path))
1543 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1548 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "frame member (", item.name, "=", item.value, ") is unheard of");
1551 _dpl(0, "Bad frame");
1556 delete argumentTokens;
1560 Breakpoint GetBreakpointById(int id, bool * isInternal)
1562 Breakpoint bp = null;
1563 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetBreakpointById(", id, ")");
1565 *isInternal = false;
1568 for(i : sysBPs; i.bp && i.bp.id == id)
1575 if(!bp && bpRunToCursor && bpRunToCursor.bp && bpRunToCursor.bp.id == id)
1579 for(i : ide.workspace.breakpoints; i.bp && i.bp.id == id)
1589 GdbDataBreakpoint ParseBreakpoint(char * string, bool * parseMultipleBreakpoints)
1592 GdbDataBreakpoint bp { };
1593 DebugListItem item { };
1594 Array<char *> bpTokens { minAllocSize = 16 };
1595 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ParseBreakpoint()");
1596 string = StripCurlies(string);
1597 TokenizeList(string, ',', bpTokens);
1598 for(i = 0; i < bpTokens.count; i++)
1600 if(TokenizeListItem(bpTokens[i], item))
1602 StripQuotes(item.value, item.value);
1603 if(!strcmp(item.name, "number"))
1605 if(!strchr(item.value, '.'))
1606 bp.id = atoi(item.value);
1607 bp.number = item.value;
1609 else if(!strcmp(item.name, "type"))
1610 bp.type = item.value;
1611 else if(!strcmp(item.name, "disp"))
1612 bp.disp = item.value;
1613 else if(!strcmp(item.name, "enabled"))
1614 bp.enabled = (!strcmpi(item.value, "y"));
1615 else if(!strcmp(item.name, "addr"))
1617 if(!strcmp(item.value, "<MULTIPLE>"))
1619 if(parseMultipleBreakpoints)
1620 *parseMultipleBreakpoints = true;
1623 bp.addr = item.value;
1625 else if(!strcmp(item.name, "func"))
1626 bp.func = item.value;
1627 else if(!strcmp(item.name, "file"))
1628 bp.file = item.value;
1629 else if(!strcmp(item.name, "fullname"))
1630 bp.fullname = item.value;
1631 else if(!strcmp(item.name, "line"))
1632 bp.line = atoi(item.value);
1633 else if(!strcmp(item.name, "at"))
1635 else if(!strcmp(item.name, "times"))
1636 bp.times = atoi(item.value);
1637 else if(!strcmp(item.name, "original-location") || !strcmp(item.name, "thread-groups"))
1638 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "breakpoint member (", item.name, "=", item.value, ") is ignored");
1640 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "breakpoint member (", item.name, "=", item.value, ") is unheard of");
1646 void ShowDebuggerViews()
1648 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ShowDebuggerViews()");
1649 ide.outputView.Show();
1650 ide.outputView.SelectTab(debug);
1651 ide.threadsView.Show();
1652 ide.callStackView.Show();
1653 ide.watchesView.Show();
1657 void HideDebuggerViews()
1659 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::HideDebuggerViews()");
1660 ide.RepositionWindows(true);
1663 void ::GdbCommand(bool focus, char * format, ...)
1667 // TODO: Improve this limit
1668 static char string[MAX_F_STRING*4];
1670 va_start(args, format);
1671 vsnprintf(string, sizeof(string), format, args);
1672 string[sizeof(string)-1] = 0;
1676 ide.debugger.serialSemaphore.TryWait();
1678 #ifdef GDB_DEBUG_CONSOLE
1679 _dpl2(_dpct, dplchan::gdbCommand, 0, string);
1681 #ifdef GDB_DEBUG_OUTPUT
1682 ide.outputView.gdbBox.Logf("cmd: %s\n", string);
1684 #ifdef GDB_DEBUG_GUI
1686 ide.gdbDialog.AddCommand(string);
1689 strcat(string,"\n");
1690 gdbHandle.Puts(string);
1693 Process_ShowWindows(targetProcessId);
1696 ide.debugger.serialSemaphore.Wait();
1701 bool ValidateBreakpoint(Breakpoint bp)
1703 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ValidateBreakpoint()");
1704 if(modules && bp.bp)
1706 if(bp.bp.line != bp.line)
1712 ide.outputView.debugBox.Logf("WOULD HAVE -- Invalid breakpoint disabled: %s:%d\n", bp.relativeFilePath, bp.line);
1714 //UnsetBreakpoint(bp);
1715 //bp.enabled = false;
1721 ide.outputView.debugBox.Logf("Debugger Error: ValidateBreakpoint error\n");
1722 bp.line = bp.bp.line;
1729 void GdbBreakpointsInsert()
1731 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbBreakpointsInsert()");
1734 DirExpression objDir = ide.project.GetObjDir(currentCompiler, prjConfig, bitDepth);
1735 for(bp : sysBPs; !bp.inserted)
1737 bool insert = false;
1738 if(bp.type == internalModulesLoaded)
1740 char path[MAX_LOCATION];
1741 char name[MAX_LOCATION];
1742 char fixedModuleName[MAX_FILENAME];
1745 bool moduleLoadBlock = false;
1747 ReplaceSpaces(fixedModuleName, ide.project.moduleName);
1748 snprintf(name, sizeof(name),"%s.main.ec", fixedModuleName);
1749 name[sizeof(name)-1] = 0;
1750 strcpy(path, ide.workspace.projectDir);
1751 PathCatSlash(path, objDir.dir);
1752 PathCatSlash(path, name);
1753 f = FileOpen(path, read);
1756 for(lineNumber = 1; !f.Eof(); lineNumber++)
1758 if(f.GetLine(line, sizeof(line) - 1))
1760 bool moduleLoadLine;
1761 TrimLSpaces(line, line);
1762 moduleLoadLine = !strncmp(line, "eModule_Load", strlen("eModule_Load"));
1763 if(!moduleLoadBlock && moduleLoadLine)
1764 moduleLoadBlock = true;
1765 else if(moduleLoadBlock && !moduleLoadLine && strlen(line) > 0)
1771 char relative[MAX_LOCATION];
1772 bp.absoluteFilePath = path;
1773 MakePathRelative(path, ide.workspace.projectDir, relative);
1774 bp.relativeFilePath = relative;
1775 bp.line = lineNumber;
1781 else if(bp.type == internalModuleLoad)
1785 for(prj : ide.workspace.projects)
1787 if(!strcmp(prj.moduleName, "ecere"))
1789 ProjectNode node = prj.topNode.Find("instance.c", false);
1792 char path[MAX_LOCATION];
1793 char relative[MAX_LOCATION];
1794 node.GetFullFilePath(path);
1795 bp.absoluteFilePath = path;
1796 MakePathRelative(path, prj.topNode.path, relative);
1797 bp.relativeFilePath = relative;
1808 SetBreakpoint(bp, false);
1812 if(bpRunToCursor && !bpRunToCursor.inserted)
1813 SetBreakpoint(bpRunToCursor, false);
1815 if(!ignoreBreakpoints)
1817 for(bp : ide.workspace.breakpoints; !bp.inserted && bp.type == user)
1821 if(!SetBreakpoint(bp, false))
1822 SetBreakpoint(bp, true);
1830 bp.bp = GdbDataBreakpoint { };
1837 void UnsetBreakpoint(Breakpoint bp)
1839 char * s; _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::UnsetBreakpoint(", s=bp.CopyLocationString(false), ")"); delete s;
1840 if(symbols && bp.inserted)
1842 GdbCommand(false, "-break-delete %s", bp.bp.number);
1843 bp.inserted = false;
1848 bool SetBreakpoint(Breakpoint bp, bool removePath)
1850 char * s; _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SetBreakpoint(", s=bp.CopyLocationString(false), ", ", removePath ? "**** removePath(true) ****" : "", ")"); delete s;
1851 breakpointError = false;
1854 char * location = bp.CopyLocationString(removePath);
1855 sentBreakInsert = true;
1856 GdbCommand(false, "-break-insert %s", location);
1858 if(!breakpointError)
1862 bp.inserted = (bp.bp && bp.bp.number && strcmp(bp.bp.number, "0"));
1863 ValidateBreakpoint(bp);
1864 /*if(bp == bpRunToCursor)
1865 runToCursorDebugStart = false;*/
1868 return !breakpointError;
1871 void GdbBreakpointsDelete(bool deleteRunToCursor, bool deleteInternalBreakpoints)
1873 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbBreakpointsDelete(deleteRunToCursor(", deleteRunToCursor, "))");
1876 if(deleteInternalBreakpoints)
1879 UnsetBreakpoint(bp);
1881 for(bp : ide.workspace.breakpoints)
1882 UnsetBreakpoint(bp);
1883 if(deleteRunToCursor && bpRunToCursor)
1884 UnsetBreakpoint(bpRunToCursor);
1890 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbGetStack()");
1892 stackFrames.Free(Frame::Free);
1893 GdbCommand(false, "-stack-info-depth");
1895 GdbCommand(false, "-stack-info-depth 192");
1896 if(frameCount && frameCount <= 192)
1897 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 191));
1900 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 95));
1901 GdbCommand(false, "-stack-list-frames %d %d", Max(frameCount - 96, 96), frameCount - 1);
1903 GdbCommand(false, "");
1908 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbTargetSet()");
1911 char escaped[MAX_LOCATION];
1912 strescpy(escaped, targetFile);
1913 GdbCommand(false, "file \"%s\"", escaped); //GDB/MI Missing Implementation -symbol-file, -target-attach
1920 const char *vgdbCommand = "/usr/bin/vgdb"; // TODO: vgdb command config option
1921 //GdbCommand(false, "-target-select remote | %s --pid=%d", "vgdb", targetProcessId);
1922 printf("target remote | %s --pid=%d\n", vgdbCommand, targetProcessId);
1923 GdbCommand(false, "target remote | %s --pid=%d", vgdbCommand, targetProcessId); // TODO: vgdb command config option
1926 /*for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
1927 GdbCommand(false, "-environment-directory \"%s\"", prj.topNode.path);*/
1929 for(dir : ide.workspace.sourceDirs; dir && dir[0])
1931 bool interference = false;
1932 for(prj : ide.workspace.projects)
1934 if(!fstrcmp(prj.topNode.path, dir))
1936 interference = true;
1940 if(!interference && dir[0])
1941 GdbCommand(false, "-environment-directory \"%s\"", dir);
1949 /*void GdbTargetRelease()
1953 GdbBreakpointsDelete(true, true);
1954 GdbCommand(false, "file"); //GDB/MI Missing Implementation -target-detach
1960 void GdbDebugBreak(bool internal)
1962 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbDebugBreak()");
1966 breakType = DebuggerAction::internal;
1968 if(ide) ide.Update(null);
1970 if(Process_Break(targetProcessId)) //GdbCommand(false, "-exec-interrupt");
1971 serialSemaphore.Wait();
1974 ChangeState(loaded);
1975 targetProcessId = 0;
1980 ide.outputView.debugBox.Logf("Debugger Error: GdbDebugBreak with not target id should never happen\n");
1985 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecRun()");
1988 ShowDebuggerViews();
1990 GdbCommand(true, "-exec-continue");
1992 GdbCommand(true, "-exec-run");
1995 void GdbExecContinue(bool focus)
1997 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecContinue()");
1999 GdbCommand(focus, "-exec-continue");
2004 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecNext()");
2006 GdbCommand(true, "-exec-next");
2011 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecStep()");
2013 GdbCommand(true, "-exec-step");
2016 void GdbExecFinish()
2018 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecFinish()");
2020 GdbCommand(true, "-exec-finish");
2023 void GdbExecCommon()
2025 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecCommon()");
2026 ClearBreakDisplay();
2027 GdbBreakpointsInsert();
2030 #ifdef GDB_DEBUG_GUI
2031 void SendGDBCommand(char * command)
2033 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SendGDBCommand()");
2034 DebuggerState oldState = state;
2039 GdbDebugBreak(true);
2042 GdbCommand(false, command);
2045 if(oldState == running)
2046 GdbExecContinue(false);
2050 void ClearBreakDisplay()
2052 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ClearBreakDisplay()");
2054 activeFrameLevel = -1;
2064 stackFrames.Free(Frame::Free);
2065 WatchesCodeEditorLinkRelease();
2066 ide.callStackView.Clear();
2067 ide.threadsView.Clear();
2073 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbAbortExec()");
2075 GdbCommand(false, "-interpreter-exec console \"kill\""); // should use -exec-abort -- GDB/MI implementation incomplete
2079 bool GdbInit(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
2082 char oldDirectory[MAX_LOCATION];
2083 char tempPath[MAX_LOCATION];
2084 char command[MAX_F_STRING*4];
2085 bool vgFullLeakCheck = ide.workspace.vgFullLeakCheck;
2086 Project project = ide.project;
2087 DirExpression targetDirExp = project.GetTargetDir(compiler, config, bitDepth);
2088 PathBackup pathBackup { };
2090 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbInit()");
2091 if(currentCompiler != compiler)
2093 delete currentCompiler;
2094 currentCompiler = compiler;
2095 incref currentCompiler;
2098 this.bitDepth = bitDepth;
2099 usingValgrind = useValgrind;
2101 ChangeState(loaded);
2103 sentBreakInsert = false;
2104 breakpointError = false;
2105 ignoreBreakpoints = false;
2111 ide.outputView.ShowClearSelectTab(debug);
2112 ide.outputView.debugBox.Logf($"Starting debug mode\n");
2114 #ifdef GDB_DEBUG_OUTPUT
2115 ide.outputView.gdbBox.Logf("run: Starting GDB\n");
2118 strcpy(tempPath, ide.workspace.projectDir);
2119 PathCatSlash(tempPath, targetDirExp.dir);
2121 targetDir = CopyString(tempPath);
2122 project.CatTargetFileName(tempPath, compiler, config);
2124 targetFile = CopyString(tempPath);
2126 GetWorkingDir(oldDirectory, MAX_LOCATION);
2127 if(ide.workspace.debugDir && ide.workspace.debugDir[0])
2129 char temp[MAX_LOCATION];
2130 strcpy(temp, ide.workspace.projectDir);
2131 PathCatSlash(temp, ide.workspace.debugDir);
2132 ChangeWorkingDir(temp);
2135 ChangeWorkingDir(ide.workspace.projectDir);
2137 ide.SetPath(true, compiler, config, bitDepth);
2139 // TODO: This pollutes the environment, but at least it works
2140 // It shouldn't really affect the IDE as the PATH gets restored and other variables set for testing will unlikely cause problems
2141 // What is the proper solution for this? DualPipeOpenEnv?
2142 // gdb set environment commands don't seem to take effect
2143 for(e : ide.workspace.environmentVars)
2145 SetEnvironment(e.name, e.string);
2150 char * clArgs = ide.workspace.commandLineArgs;
2151 const char *valgrindCommand = "valgrind"; // TODO: valgrind command config option //TODO: valgrind options
2152 vgLogFile = CreateTemporaryFile(vgLogPath, "ecereidevglog");
2156 vgLogThread.Create();
2160 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't open temporary log file for Valgrind output\n");
2163 if(result && !CheckCommandAvailable(valgrindCommand))
2165 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for Valgrind is not available.\n", valgrindCommand);
2170 sprintf(command, "%s --vgdb=yes --vgdb-error=0 --log-file=%s%s %s%s%s",
2171 valgrindCommand, vgLogPath, vgFullLeakCheck ? " --leak-check=full" : "", targetFile, clArgs ? " " : "", clArgs ? clArgs : "");
2172 vgTargetHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
2175 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start Valgrind\n");
2181 incref vgTargetHandle;
2182 vgTargetThread.Create();
2184 targetProcessId = vgTargetHandle.GetProcessID();
2185 waitingForPID = false;
2186 if(!targetProcessId)
2188 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get Valgrind process ID\n");
2195 serialSemaphore.Wait();
2203 (compiler.targetPlatform == win32 && bitDepth == 64) ? "x86_64-w64-mingw32-gdb" :
2204 (compiler.targetPlatform == win32 && bitDepth == 32) ? "i686-w64-mingw32-gdb" :
2206 if(!CheckCommandAvailable(command))
2208 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for GDB is not available.\n", command);
2213 strcat(command, " -n -silent --interpreter=mi2"); //-async //\"%s\"
2215 gdbHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
2218 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start GDB\n");
2228 gdbProcessId = gdbHandle.GetProcessID();
2231 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get GDB process ID\n");
2238 serialSemaphore.Wait();
2241 GdbCommand(false, "-gdb-set verbose off");
2242 //GdbCommand(false, "-gdb-set exec-done-display on");
2243 GdbCommand(false, "-gdb-set step-mode off");
2244 GdbCommand(false, "-gdb-set unwindonsignal on");
2245 //GdbCommand(false, "-gdb-set shell on");
2246 GdbCommand(false, "set print elements 992");
2247 GdbCommand(false, "-gdb-set backtrace limit 100000");
2251 //ChangeState(terminated);
2257 #if defined(__unix__)
2259 CreateTemporaryDir(progFifoDir, "ecereide");
2260 strcpy(progFifoPath, progFifoDir);
2261 PathCat(progFifoPath, "ideprogfifo");
2262 if(!mkfifo(progFifoPath, 0600))
2264 //fileCreated = true;
2269 ide.outputView.debugBox.Logf(createFIFOMsg, progFifoPath);
2276 progThread.terminate = false;
2277 progThread.Create();
2281 #if defined(__WIN32__)
2282 GdbCommand(false, "-gdb-set new-console on");
2285 #if defined(__unix__)
2287 GdbCommand(false, "-inferior-tty-set %s", progFifoPath);
2291 GdbCommand(false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
2293 for(e : ide.workspace.environmentVars)
2295 GdbCommand(false, "set environment %s=%s", e.name, e.string);
2300 ChangeWorkingDir(oldDirectory);
2306 delete targetDirExp;
2312 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExit()");
2313 if(gdbHandle && gdbProcessId)
2315 GdbCommand(false, "-gdb-exit");
2330 ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
2336 for(bp : ide.workspace.breakpoints)
2338 bp.inserted = false;
2344 bp.inserted = false;
2349 bpRunToCursor.inserted = false;
2350 delete bpRunToCursor.bp;
2353 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2354 ClearBreakDisplay();
2357 #if defined(__unix__)
2358 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
2360 progThread.terminate = true;
2363 fifoFile.CloseInput();
2369 DeleteFile(progFifoPath);
2370 progFifoPath[0] = '\0';
2376 void WatchesCodeEditorLinkInit()
2378 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkInit()");
2380 char tempPath[MAX_LOCATION];
2381 char path[MAX_LOCATION];
2383 //void MakeFilePathProjectRelative(char * path, char * relativePath)
2384 if(!ide.projectView.project.GetRelativePath(activeFrame.file, tempPath))
2385 strcpy(tempPath, activeFrame.file);
2387 strcpy(path, ide.workspace.projectDir);
2388 PathCat(path, tempPath);
2389 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2392 for(srcDir : ide.workspace.sourceDirs)
2394 strcpy(path, srcDir);
2395 PathCat(path, tempPath);
2396 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2397 if(codeEditor) break;
2402 /*if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2403 activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);*/
2404 if(!activeFrame || !activeFrame.absoluteFile)
2407 codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal, false);
2410 codeEditor.inUseDebug = true;
2413 //watchesInit = true;
2416 void WatchesCodeEditorLinkRelease()
2418 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkRelease()");
2423 codeEditor.inUseDebug = false;
2424 if(!codeEditor.visible)
2425 codeEditor.Destroy(0);
2431 bool ResolveWatch(Watch wh)
2433 bool result = false;
2435 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ResolveWatch()");
2447 char watchmsg[MAX_F_STRING];
2448 if(state == stopped && !codeEditor)
2449 wh.value = CopyString($"No source file found for selected frame");
2450 //if(codeEditor && state == stopped || state != stopped)
2453 Module backupPrivateModule;
2454 Context backupContext;
2455 Class backupThisClass;
2459 backupPrivateModule = GetPrivateModule();
2460 backupContext = GetCurrentContext();
2461 backupThisClass = GetThisClass();
2464 SetPrivateModule(codeEditor.privateModule);
2465 SetCurrentContext(codeEditor.globalContext);
2466 SetTopContext(codeEditor.globalContext);
2467 SetGlobalContext(codeEditor.globalContext);
2468 SetGlobalData(&codeEditor.globalData);
2471 exp = ParseExpressionString(wh.expression);
2473 if(exp && !parseError)
2475 char expString[4096];
2477 PrintExpression(exp, expString);
2479 if(GetPrivateModule())
2482 DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2483 ProcessExpressionType(exp);
2485 wh.type = exp.expType;
2488 DebugComputeExpression(exp);
2489 if(ExpressionIsError(exp))
2491 GDBFallBack(exp, expString);
2494 /*if(exp.hasAddress)
2496 char temp[MAX_F_STRING];
2497 sprintf(temp, "0x%x", exp.address);
2498 wh.address = CopyString(temp);
2499 // wh.address = CopyStringf("0x%x", exp.address);
2504 Type dataType = exp.expType;
2507 char temp[MAX_F_STRING];
2508 switch(dataType.kind)
2511 sprintf(temp, "%i", exp.val.c);
2514 sprintf(temp, "%i", exp.val.s);
2519 sprintf(temp, "%i", exp.val.i);
2522 sprintf(temp, "%i", exp.val.i64);
2525 sprintf(temp, "%i", exp.val.p);
2530 long v = (long)exp.val.f;
2531 sprintf(temp, "%i", v);
2536 long v = (long)exp.val.d;
2537 sprintf(temp, "%i", v);
2542 wh.intVal = CopyString(temp);
2543 switch(dataType.kind)
2546 sprintf(temp, "0x%x", exp.val.c);
2549 sprintf(temp, "0x%x", exp.val.s);
2553 sprintf(temp, "0x%x", exp.val.i);
2556 sprintf(temp, "0x%x", exp.val.i64);
2559 sprintf(temp, "0x%x", exp.val.i64);
2562 sprintf(temp, "0x%x", exp.val.p);
2567 long v = (long)exp.val.f;
2568 sprintf(temp, "0x%x", v);
2573 long v = (long)exp.val.d;
2574 sprintf(temp, "0x%x", v);
2579 wh.hexVal = CopyString(temp);
2580 switch(dataType.kind)
2583 sprintf(temp, "0o%o", exp.val.c);
2586 sprintf(temp, "0o%o", exp.val.s);
2590 sprintf(temp, "0o%o", exp.val.i);
2593 sprintf(temp, "0o%o", exp.val.i64);
2596 sprintf(temp, "0o%o", exp.val.i64);
2599 sprintf(temp, "0o%o", exp.val.p);
2604 long v = (long)exp.val.f;
2605 sprintf(temp, "0o%o", v);
2610 long v = (long)exp.val.d;
2611 sprintf(temp, "0o%o", v);
2616 wh.octVal = CopyString(temp);
2619 // WHATS THIS HERE ?
2620 if(exp.type == constantExp && exp.constant)
2621 wh.constant = CopyString(exp.constant);
2627 case symbolErrorExp:
2628 snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2630 case structMemberSymbolErrorExp:
2631 // todo get info as in next case (ExpClassMemberSymbolError)
2632 snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2634 case classMemberSymbolErrorExp:
2637 Expression memberExp = exp.member.exp;
2638 Identifier memberID = exp.member.member;
2639 Type type = memberExp.expType;
2642 _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2645 char string[256] = "";
2647 PrintTypeNoConst(type, string, false, true);
2648 classSym = FindClass(string);
2649 _class = classSym ? classSym.registered : null;
2652 snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2654 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2657 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2660 case memoryErrorExp:
2661 // Need to ensure when set to memoryErrorExp, constant is set
2662 snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2664 case dereferenceErrorExp:
2665 snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2667 case unknownErrorExp:
2668 snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2670 case noDebuggerErrorExp:
2671 snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2673 case debugStateErrorExp:
2674 snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2677 snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2681 // Temporary Code for displaying Strings
2682 if((exp.expType && ((exp.expType.kind == pointerType ||
2683 exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) ||
2684 (wh.type && wh.type.kind == classType && wh.type._class &&
2685 wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2686 !strcmp(wh.type._class.registered.name, "String")))
2689 if(exp.expType.kind != arrayType || exp.hasAddress)
2695 //char temp[MAX_F_STRING * 32];
2697 ExpressionType evalError = dummyExp;
2698 /*if(exp.expType.kind == arrayType)
2699 sprintf(temp, "(char*)0x%x", exp.address);
2701 sprintf(temp, "(char*)%s", exp.constant);*/
2703 //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2704 // address = strtoul(exp.constant, null, 0);
2705 address = _strtoui64(exp.constant, null, 0);
2706 //_dpl(0, "0x", address);
2707 // snprintf(value, sizeof(value), "0x%08x ", address);
2709 if(address > 0xFFFFFFFFLL)
2710 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2712 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2713 value[sizeof(value)-1] = 0;
2716 strcat(value, $"Null string");
2720 len = strlen(value);
2722 while(!string && size > 2)
2724 string = GdbReadMemory(address, size);
2727 if(string && string[0])
2730 if(UTF8Validate(string))
2735 for(c = 0; (ch = string[c]) && c<4096; c++)
2738 value[len++] = '\0';
2743 ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2744 strcat(value, ") (ISO8859-1)");
2751 strcat(value, $"Empty string");
2755 strcat(value, $"Couldn't read memory");
2757 wh.value = CopyString(value);
2760 else if(wh.type && wh.type.kind == classType && wh.type._class &&
2761 wh.type._class.registered && wh.type._class.registered.type == enumClass)
2763 uint64 value = strtoul(exp.constant, null, 0);
2764 Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2765 EnumClassData enumeration = (EnumClassData)enumClass.data;
2767 for(item = enumeration.values.first; item; item = item.next)
2768 if((int)item.data == value)
2771 wh.value = CopyString(item.name);
2773 wh.value = CopyString($"Invalid Enum Value");
2776 else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class &&
2777 wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2784 if(exp.constant[0] == '\'')
2786 if((int)((byte *)exp.constant)[1] > 127)
2789 value = UTF8GetChar(exp.constant + 1, &nb);
2790 if(nb < 2) value = exp.constant[1];
2791 signedValue = value;
2795 signedValue = exp.constant[1];
2797 // Precomp Syntax error with boot strap here:
2798 byte b = (byte)(char)signedValue;
2799 value = (unichar) b;
2805 if(wh.type.kind == charType && wh.type.isSigned)
2807 signedValue = (int)(char)strtol(exp.constant, null, 0);
2809 // Precomp Syntax error with boot strap here:
2810 byte b = (byte)(char)signedValue;
2811 value = (unichar) b;
2816 value = (uint)strtoul(exp.constant, null, 0);
2817 signedValue = (int)value;
2821 UTF32toUTF8Len(&value, 1, charString, 5);
2823 snprintf(string, sizeof(string), "\'\\0' (0)");
2824 else if(value == '\t')
2825 snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2826 else if(value == '\n')
2827 snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2828 else if(value == '\r')
2829 snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2830 else if(wh.type.kind == charType && wh.type.isSigned)
2831 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2832 else if(value > 256 || wh.type.kind != charType)
2834 if(value > 0x10FFFF || !GetCharCategory(value))
2835 snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2837 snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2840 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2841 string[sizeof(string)-1] = 0;
2843 wh.value = CopyString(string);
2848 wh.value = CopyString(exp.constant);
2855 wh.value = PrintHexUInt64(exp.address);
2860 char tempString[256];
2861 if(exp.member.memberType == propertyMember)
2862 snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2864 snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression,
2865 exp.type.OnGetString(tempString, null, null));
2871 snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2872 if(exp) FreeExpression(exp);
2875 SetPrivateModule(backupPrivateModule);
2876 SetCurrentContext(backupContext);
2877 SetTopContext(backupContext);
2878 SetGlobalContext(backupContext);
2879 SetThisClass(backupThisClass);
2882 // wh.value = CopyString("No source file found for selected frame");
2884 watchmsg[sizeof(watchmsg)-1] = 0;
2886 wh.value = CopyString(watchmsg);
2888 ide.watchesView.UpdateWatch(wh);
2892 void EvaluateWatches()
2894 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateWatches()");
2895 for(wh : ide.workspace.watches)
2899 char * ::GdbEvaluateExpression(char * expression)
2901 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbEvaluateExpression(", expression, ")");
2904 GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2906 ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2910 // to be removed... use GdbReadMemory that returns a byte array instead
2911 char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
2913 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemoryString(", address, ")");
2918 _dpl(0, "GdbReadMemoryString called with size = 0!");
2920 // GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
2921 if(GetRuntimePlatform() == win32)
2922 GdbCommand(false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
2924 GdbCommand(false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
2926 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
2930 byte * ::GdbReadMemory(uint64 address, int bytes)
2932 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemory(", address, ")");
2935 //GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
2936 if(GetRuntimePlatform() == win32)
2937 GdbCommand(false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
2939 GdbCommand(false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
2942 _dpl(0, "GdbReadMemory called with bytes = 0!");
2945 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
2946 else if(eval.result && strcmp(eval.result, "N/A"))
2948 byte * result = new byte[bytes];
2949 byte * string = eval.result;
2953 result[c++] = (byte)strtol(string, &string, 10);
2969 void EventHit(GdbDataStop stopItem, Breakpoint bpInternal, Breakpoint bpUser)
2971 bool conditionMet = true;
2973 char * s1 = null; char * s2 = null;
2974 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EventHit(",
2975 "bpInternal(", bpInternal ? s1=bpInternal.CopyLocationString(false) : null, "), ",
2976 "bpUser(", bpUser ? s2=bpUser.CopyLocationString(false) : null, ")) -- ",
2977 "ignoreBreakpoints(", ignoreBreakpoints, "), ",
2978 "hitCursorBreakpoint(", bpUser && bpUser.type == runToCursor, ")");
2979 delete s1; delete s2;
2980 if(bpUser && stopItem.frame.line && bpUser.line != stopItem.frame.line)
2982 // updating user breakpoint on hit location difference
2983 // todo, print something?
2984 bpUser.line = stopItem.frame.line;
2985 ide.breakpointsView.UpdateBreakpoint(bpUser.row);
2986 ide.workspace.Save();
2991 if(bpInternal.type == internalModulesLoaded)
2993 if(!bpUser && !userBreakOnInternalBreakpoint)
2994 GdbExecContinue(false);
2998 if(bpUser.condition)
2999 conditionMet = ResolveWatch(bpUser.condition);
3001 if(!ignoreBreakpoints && (bpUser.level == -1 || bpUser.level == frameCount-1) && conditionMet)
3008 GdbExecContinue(false);
3012 GdbExecContinue(false);
3013 ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3014 if(bpUser == bpRunToCursor)
3016 UnsetBreakpoint(bpUser);
3017 delete bpRunToCursor;
3022 void ValgrindTargetThreadExit()
3024 ide.outputView.debugBox.Logf($"ValgrindTargetThreadExit\n");
3027 vgTargetHandle.Wait();
3028 delete vgTargetHandle;
3030 HandleExit(null, null);
3033 void GdbThreadExit()
3035 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbThreadExit()");
3036 if(state != terminated)
3038 ChangeState(terminated);
3039 targetProcessId = 0;
3040 ClearBreakDisplay();
3046 serialSemaphore.Release();
3051 ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
3052 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3054 HideDebuggerViews();
3056 //ChangeState(terminated);
3060 void GdbThreadMain(char * output)
3063 Array<char *> outTokens { minAllocSize = 50 };
3064 Array<char *> subTokens { minAllocSize = 50 };
3065 DebugListItem item { };
3066 DebugListItem item2 { };
3067 bool setWaitingForPID = false;
3069 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
3070 #ifdef GDB_DEBUG_CONSOLE
3071 _dpl2(_dpct, dplchan::gdbOutput, 0, output);
3073 #ifdef GDB_DEBUG_OUTPUT
3075 int len = strlen(output);
3083 for(c = 0; c < len / 1024; c++)
3085 strncpy(tmp, start, 1024);
3086 ide.outputView.gdbBox.Logf("out: %s\n", tmp);
3089 ide.outputView.gdbBox.Logf("out: %s\n", start);
3093 ide.outputView.gdbBox.Logf("out: %s\n", output);
3097 #ifdef GDB_DEBUG_CONSOLE
3098 strcpy(lastGdbOutput, output);
3100 #ifdef GDB_DEBUG_GUI
3101 if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
3108 if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
3111 ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
3117 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
3119 //if(outTokens.count == 1)
3124 ChangeState(loaded);
3125 targetProcessId = 0;
3126 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3128 if(!strcmp(item.name, "reason"))
3130 char * reason = item.value;
3131 StripQuotes(reason, reason);
3132 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3135 if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
3137 StripQuotes(item2.value, item2.value);
3138 if(!strcmp(item2.name, "exit-code"))
3139 exitCode = item2.value;
3145 HandleExit(reason, exitCode);
3149 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "kill reply (", item.name, "=", item.value, ") is unheard of");
3152 HandleExit(null, null);
3155 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3157 if(!strcmp(item.name, "bkpt"))
3159 sentBreakInsert = false;
3164 bpItem = ParseBreakpoint(item.value, null);
3165 //breakType = bpValidation;
3166 //app.SignalEvent();
3167 subTokens.RemoveAll();
3169 else if(!strcmp(item.name, "depth"))
3171 StripQuotes(item.value, item.value);
3172 frameCount = atoi(item.value);
3174 stackFrames.Free(Frame::Free);
3176 else if(!strcmp(item.name, "stack"))
3179 if(stackFrames.count)
3180 ide.callStackView.Logf("...\n");
3183 item.value = StripBrackets(item.value);
3184 TokenizeList(item.value, ',', subTokens);
3185 for(i = 0; i < subTokens.count; i++)
3187 if(TokenizeListItem(subTokens[i], item))
3189 if(!strcmp(item.name, "frame"))
3192 stackFrames.Add(frame);
3193 item.value = StripCurlies(item.value);
3194 ParseFrame(frame, item.value);
3195 if(frame.file && frame.from)
3196 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "unexpected frame file and from members present");
3200 if(activeFrameLevel == -1)
3202 if(ide.projectView.IsModuleInProject(frame.file));
3204 if(frame.level != 0)
3206 //stopItem.frame = frame;
3207 breakType = selectFrame;
3210 activeFrame = frame;
3211 activeFrameLevel = frame.level;
3214 ide.callStackView.Logf("%3d ", frame.level);
3215 if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
3216 ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
3217 else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
3218 ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
3219 else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
3220 ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
3221 else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
3222 ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
3224 ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
3229 ide.callStackView.Logf("%3d ", frame.level);
3234 ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
3238 ide.callStackView.Logf("%s\n", frame.func);
3240 ide.callStackView.Logf($"unknown source\n");
3244 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "stack content (", item.name, "=", item.value, ") is unheard of");
3247 if(activeFrameLevel == -1)
3249 activeFrameLevel = 0;
3250 activeFrame = stackFrames.first;
3252 ide.callStackView.Home();
3254 subTokens.RemoveAll();
3256 /*else if(!strcmp(item.name, "frame"))
3259 item.value = StripCurlies(item.value);
3260 ParseFrame(&frame, item.value);
3262 else if(!strcmp(item.name, "thread-ids"))
3264 ide.threadsView.Clear();
3265 item.value = StripCurlies(item.value);
3266 TokenizeList(item.value, ',', subTokens);
3267 for(i = subTokens.count - 1; ; i--)
3269 if(TokenizeListItem(subTokens[i], item))
3271 if(!strcmp(item.name, "thread-id"))
3274 StripQuotes(item.value, item.value);
3275 value = atoi(item.value);
3276 ide.threadsView.Logf("%3d \n", value);
3279 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "threads content (", item.name, "=", item.value, ") is unheard of");
3284 ide.threadsView.Home();
3286 subTokens.RemoveAll();
3287 //if(!strcmp(outTokens[2], "number-of-threads"))
3289 else if(!strcmp(item.name, "new-thread-id"))
3291 StripQuotes(item.value, item.value);
3292 activeThread = atoi(item.value);
3294 else if(!strcmp(item.name, "value"))
3296 StripQuotes(item.value, item.value);
3297 eval.result = CopyString(item.value);
3298 eval.active = false;
3300 else if(!strcmp(item.name, "addr"))
3302 for(i = 2; i < outTokens.count; i++)
3304 if(TokenizeListItem(outTokens[i], item))
3306 if(!strcmp(item.name, "total-bytes"))
3308 StripQuotes(item.value, item.value);
3309 eval.bytes = atoi(item.value);
3311 else if(!strcmp(item.name, "next-row"))
3313 StripQuotes(item.value, item.value);
3314 eval.nextBlockAddress = _strtoui64(item.value, null, 0);
3316 else if(!strcmp(item.name, "memory"))
3320 //StripQuotes(item.value, item.value);
3321 item.value = StripBrackets(item.value);
3322 // this should be treated as a list...
3323 item.value = StripCurlies(item.value);
3324 TokenizeList(item.value, ',', subTokens);
3325 for(j = 0; j < subTokens.count; j++)
3327 if(TokenizeListItem(subTokens[j], item))
3329 if(!strcmp(item.name, "data"))
3331 item.value = StripBrackets(item.value);
3332 StripQuotes2(item.value, item.value);
3333 eval.result = CopyString(item.value);
3334 eval.active = false;
3338 subTokens.RemoveAll();
3343 else if(!strcmp(item.name, "source-path") || !strcmp(item.name, "BreakpointTable"))
3344 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "command reply (", item.name, "=", item.value, ") is ignored");
3346 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "command reply (", item.name, "=", item.value, ") is unheard of");
3349 else if(!strcmp(outTokens[0], "^running"))
3351 waitingForPID = true;
3352 setWaitingForPID = true;
3354 else if(!strcmp(outTokens[0], "^exit"))
3356 ChangeState(terminated);
3357 // ide.outputView.debugBox.Logf("Exit\n");
3358 // ide.Update(null);
3360 serialSemaphore.Release();
3362 else if(!strcmp(outTokens[0], "^error"))
3366 sentBreakInsert = false;
3367 breakpointError = true;
3370 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3372 if(!strcmp(item.name, "msg"))
3374 StripQuotes(item.value, item.value);
3377 eval.active = false;
3379 if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3380 eval.error = symbolNotFound;
3381 else if(strstr(item.value, "Cannot access memory at address"))
3382 eval.error = memoryCantBeRead;
3384 eval.error = unknown;
3386 else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3389 else if(!strncmp(item.value, "Cannot access memory at address", 31))
3392 else if(!strcmp(item.value, "Cannot find bounds of current function"))
3394 ChangeState(stopped);
3395 gdbHandle.Printf("-exec-continue\n");
3397 else if(!strcmp(item.value, "ptrace: No such process."))
3399 ChangeState(loaded);
3400 targetProcessId = 0;
3402 else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3405 else if(!strcmp(item.value, "You can't do that without a process to debug."))
3407 ChangeState(loaded);
3408 targetProcessId = 0;
3410 else if(strstr(item.value, "No such file or directory."))
3412 ChangeState(loaded);
3413 targetProcessId = 0;
3415 else if(strstr(item.value, "During startup program exited with code "))
3417 ChangeState(loaded);
3418 targetProcessId = 0;
3423 if(strlen(item.value) < MAX_F_STRING)
3426 ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3430 ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3436 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "error content (", item.name, "=", item.value, ") is unheard of");
3439 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "result-record: ", outTokens[0]);
3440 outTokens.RemoveAll();
3443 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "status-async-output: ", outTokens[0]);
3446 if(TokenizeList(output, ',', outTokens))
3448 if(!strcmp(outTokens[0], "=library-loaded"))
3449 FGODetectLoadedLibraryForAddedProjectIssues(outTokens);
3450 else if(!strcmp(outTokens[0], "=thread-group-created") || !strcmp(outTokens[0], "=thread-group-added") ||
3451 !strcmp(outTokens[0], "=thread-group-started") || !strcmp(outTokens[0], "=thread-group-exited") ||
3452 !strcmp(outTokens[0], "=thread-created") || !strcmp(outTokens[0], "=thread-exited") ||
3453 !strcmp(outTokens[0], "=cmd-param-changed") || !strcmp(outTokens[0], "=library-unloaded") ||
3454 !strcmp(outTokens[0], "=breakpoint-modified"))
3455 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, outTokens[0], outTokens.count>1 ? outTokens[1] : "",
3456 outTokens.count>2 ? outTokens[2] : "", outTokens.count>3 ? outTokens[3] : "",
3457 outTokens.count>4 ? outTokens[4] : "", outTokens.count>5 ? outTokens[5] : "",
3458 outTokens.count>6 ? outTokens[6] : "", outTokens.count>7 ? outTokens[7] : "",
3459 outTokens.count>8 ? outTokens[8] : "", outTokens.count>9 ? outTokens[9] : "");
3461 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "notify-async-output: ", outTokens[0]);
3463 outTokens.RemoveAll();
3467 if(TokenizeList(output, ',', outTokens))
3469 if(!strcmp(outTokens[0],"*running"))
3471 waitingForPID = true;
3472 setWaitingForPID = true;
3474 else if(!strcmp(outTokens[0], "*stopped"))
3477 ChangeState(stopped);
3479 for(tk = 1; tk < outTokens.count; tk++)
3481 if(TokenizeListItem(outTokens[tk], item))
3483 if(!strcmp(item.name, "reason"))
3485 char * reason = item.value;
3486 StripQuotes(reason, reason);
3487 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3490 if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3493 StripQuotes(item2.value, item2.value);
3494 if(!strcmp(item2.name, "exit-code"))
3495 exitCode = item2.value;
3501 HandleExit(reason, exitCode);
3504 else if(!strcmp(reason, "breakpoint-hit"))
3510 stopItem = GdbDataStop { };
3512 for(i = tk+1; i < outTokens.count; i++)
3514 TokenizeListItem(outTokens[i], item);
3515 StripQuotes(item.value, item.value);
3516 if(!strcmp(item.name, "bkptno"))
3517 stopItem.bkptno = atoi(item.value);
3518 else if(!strcmp(item.name, "thread-id"))
3519 stopItem.threadid = atoi(item.value);
3520 else if(!strcmp(item.name, "frame"))
3522 item.value = StripCurlies(item.value);
3523 ParseFrame(stopItem.frame, item.value);
3525 else if(!strcmp(item.name, "disp") || !strcmp(item.name, "stopped-threads") || !strcmp(item.name, "core"))
3526 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3528 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown breakpoint hit item name (", item.name, "=", item.value, ")");
3533 else if(!strcmp(reason, "end-stepping-range"))
3539 stopItem = GdbDataStop { };
3541 for(i = tk+1; i < outTokens.count; i++)
3543 TokenizeListItem(outTokens[i], item);
3544 StripQuotes(item.value, item.value);
3545 if(!strcmp(item.name, "thread-id"))
3546 stopItem.threadid = atoi(item.value);
3547 else if(!strcmp(item.name, "frame"))
3549 item.value = StripCurlies(item.value);
3550 ParseFrame(stopItem.frame, item.value);
3552 else if(!strcmp(item.name, "reason") || !strcmp(item.name, "bkptno"))
3553 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3555 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown end of stepping range item name (", item.name, "=", item.value, ")");
3561 else if(!strcmp(reason, "function-finished"))
3567 stopItem = GdbDataStop { };
3568 stopItem.reason = CopyString(reason);
3570 for(i = tk+1; i < outTokens.count; i++)
3572 TokenizeListItem(outTokens[i], item);
3573 StripQuotes(item.value, item.value);
3574 if(!strcmp(item.name, "thread-id"))
3575 stopItem.threadid = atoi(item.value);
3576 else if(!strcmp(item.name, "frame"))
3578 item.value = StripCurlies(item.value);
3579 ParseFrame(stopItem.frame, item.value);
3581 else if(!strcmp(item.name, "gdb-result-var"))
3582 stopItem.gdbResultVar = CopyString(item.value);
3583 else if(!strcmp(item.name, "return-value"))
3584 stopItem.returnValue = CopyString(item.value);
3586 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown function finished item name (", item.name, "=", item.value, ")");
3589 event = functionEnd;
3592 else if(!strcmp(reason, "signal-received"))
3598 stopItem = GdbDataStop { };
3599 stopItem.reason = CopyString(reason);
3601 for(i = tk+1; i < outTokens.count; i++)
3603 TokenizeListItem(outTokens[i], item);
3604 StripQuotes(item.value, item.value);
3605 if(!strcmp(item.name, "signal-name"))
3606 stopItem.name = CopyString(item.value);
3607 else if(!strcmp(item.name, "signal-meaning"))
3608 stopItem.meaning = CopyString(item.value);
3609 else if(!strcmp(item.name, "thread-id"))
3610 stopItem.threadid = atoi(item.value);
3611 else if(!strcmp(item.name, "frame"))
3613 item.value = StripCurlies(item.value);
3614 ParseFrame(stopItem.frame, item.value);
3617 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown signal reveived item name (", item.name, "=", item.value, ")");
3619 if(!strcmp(stopItem.name, "SIGTRAP"))
3638 else if(!strcmp(reason, "watchpoint-trigger"))
3639 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint trigger not handled");
3640 else if(!strcmp(reason, "read-watchpoint-trigger"))
3641 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason read watchpoint trigger not handled");
3642 else if(!strcmp(reason, "access-watchpoint-trigger"))
3643 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason access watchpoint trigger not handled");
3644 else if(!strcmp(reason, "watchpoint-scope"))
3645 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint scope not handled");
3646 else if(!strcmp(reason, "location-reached"))
3647 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason location reached not handled");
3649 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown reason: ", reason);
3661 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown exec-async-output: ", outTokens[0]);
3662 outTokens.RemoveAll();
3665 if(!strcmpi(output, "(gdb) "))
3669 char exeFile[MAX_LOCATION];
3670 int oldProcessID = targetProcessId;
3671 GetLastDirectory(targetFile, exeFile);
3673 while(!targetProcessId/*true*/)
3675 targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3676 if(targetProcessId || gdbHandle.Peek()) break;
3681 ChangeState(running);
3682 else if(!oldProcessID)
3684 ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3685 // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3686 gdbHandle.Printf("-gdb-exit\n");
3688 ChangeState(terminated); //loaded;
3693 for(bp : ide.workspace.breakpoints)
3694 bp.inserted = false;
3697 bp.inserted = false;
3699 bpRunToCursor.inserted = false;
3701 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3702 ClearBreakDisplay();
3704 #if defined(__unix__)
3705 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
3707 progThread.terminate = true;
3710 fifoFile.CloseInput();
3717 DeleteFile(progFifoPath);
3718 progFifoPath[0] = '\0';
3725 serialSemaphore.Release();
3728 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown prompt", output);
3732 if(!strncmp(output, "&\"warning:", 10))
3735 content = strstr(output, "\"");
3736 StripQuotes(content, content);
3737 content = strstr(content, ":");
3743 ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3750 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown output: ", output);
3752 if(!setWaitingForPID)
3753 waitingForPID = false;
3754 setWaitingForPID = false;
3762 // From GDB Output functions
3763 void FGODetectLoadedLibraryForAddedProjectIssues(Array<char *> outTokens)
3765 char path[MAX_LOCATION] = "";
3766 char file[MAX_FILENAME] = "";
3768 DebugListItem item { };
3769 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGODetectLoadedLibraryForAddedProjectIssues()");
3770 for(token : outTokens)
3772 if(TokenizeListItem(token, item))
3774 if(!strcmp(item.name, "target-name"))
3776 StripQuotes(item.value, path);
3777 MakeSystemPath(path);
3778 GetLastDirectory(path, file);
3780 else if(!strcmp(item.name, "symbols-loaded"))
3782 symbolsLoaded = (atoi(item.value) == 1);
3787 if(path[0] && file[0])
3789 for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
3793 char prjTargetPath[MAX_LOCATION];
3794 char prjTargetFile[MAX_FILENAME];
3795 DirExpression targetDirExp = prj.GetTargetDir(currentCompiler, prj.config, bitDepth);
3796 strcpy(prjTargetPath, prj.topNode.path);
3797 PathCat(prjTargetPath, targetDirExp.dir);
3798 prjTargetFile[0] = '\0';
3799 prj.CatTargetFileName(prjTargetFile, currentCompiler, prj.config);
3800 PathCat(prjTargetPath, prjTargetFile);
3801 MakeSystemPath(prjTargetPath);
3803 match = !fstrcmp(prjTargetFile, file);
3804 if(!match && (dot = strstr(prjTargetFile, ".so.")))
3806 char * dot3 = strstr(dot+4, ".");
3810 match = !fstrcmp(prjTargetFile, file);
3815 match = !fstrcmp(prjTargetFile, file);
3820 // TODO: nice visual feedback to better warn user. use some ide notification system or other means.
3821 /* -- 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)
3823 ide.outputView.debugBox.Logf($"Attention! No symbols for loaded library %s matched to the %s added project.\n", path, prj.topNode.name);
3825 match = !fstrcmp(prjTargetPath, path);
3826 if(!match && (dot = strstr(prjTargetPath, ".so.")))
3828 char * dot3 = strstr(dot+4, ".");
3832 match = !fstrcmp(prjTargetPath, path);
3837 match = !fstrcmp(prjTargetPath, path);
3841 ide.outputView.debugBox.Logf($"Loaded library %s doesn't match the %s target of the %s added project.\n", path, prjTargetPath, prj.topNode.name);
3848 ExpressionType ::DebugEvalExpTypeError(char * result)
3850 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::DebugEvalExpTypeError()");
3855 case symbolNotFound:
3856 return symbolErrorExp;
3857 case memoryCantBeRead:
3858 return memoryErrorExp;
3860 return unknownErrorExp;
3863 char * ::EvaluateExpression(char * expression, ExpressionType * error)
3866 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateExpression(", expression, ")");
3867 if(ide.projectView && ide.debugger.state == stopped)
3869 result = GdbEvaluateExpression(expression);
3870 *error = DebugEvalExpTypeError(result);
3875 *error = noDebuggerErrorExp;
3880 char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
3883 char * result = GdbReadMemoryString(address, size, format, 1, 1);
3884 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ReadMemory(", address, ")");
3885 if(!result || !strcmp(result, "N/A"))
3886 *error = memoryErrorExp;
3888 *error = DebugEvalExpTypeError(result);
3893 class ValgrindLogThread : Thread
3899 static char output[4096];
3900 Array<char> dynamicBuffer { minAllocSize = 4096 };
3901 File oldValgrindHandle = vgLogFile;
3902 incref oldValgrindHandle;
3905 while(debugger.state != terminated && vgLogFile)
3909 result = vgLogFile.Read(output, 1, sizeof(output));
3911 if(debugger.state == terminated || !vgLogFile/* || vgLogFile.Eof()*/)
3918 for(c = 0; c<result; c++)
3920 if(output[c] == '\n')
3922 int pos = dynamicBuffer.size;
3923 dynamicBuffer.size += c - start;
3924 memcpy(&dynamicBuffer[pos], output + start, c - start);
3925 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3926 // COMMENTED OUT DUE TO ISSUE #135, FIXED
3927 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
3928 dynamicBuffer.size++;
3929 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
3931 // printf("%s\n", dynamicBuffer.array);
3933 if(strstr(&dynamicBuffer[0], "vgdb me"))
3934 debugger.serialSemaphore.Release();
3935 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
3936 dynamicBuffer.size = 0;
3942 int pos = dynamicBuffer.size;
3943 dynamicBuffer.size += c - start;
3944 memcpy(&dynamicBuffer[pos], output + start, c - start);
3947 else if(debugger.state == stopped)
3950 printf("Got end of file from GDB!\n");
3957 delete dynamicBuffer;
3958 ide.outputView.debugBox.Logf($"ValgrindLogThreadExit\n");
3959 //if(oldValgrindHandle == vgLogFile)
3960 debugger.GdbThreadExit/*ValgrindLogThreadExit*/();
3961 delete oldValgrindHandle;
3967 class ValgrindTargetThread : Thread
3973 static char output[4096];
3974 Array<char> dynamicBuffer { minAllocSize = 4096 };
3975 DualPipe oldValgrindHandle = vgTargetHandle;
3976 incref oldValgrindHandle;
3979 while(debugger.state != terminated && vgTargetHandle && !vgTargetHandle.Eof())
3983 result = vgTargetHandle.Read(output, 1, sizeof(output));
3985 if(debugger.state == terminated || !vgTargetHandle || vgTargetHandle.Eof())
3992 for(c = 0; c<result; c++)
3994 if(output[c] == '\n')
3996 int pos = dynamicBuffer.size;
3997 dynamicBuffer.size += c - start;
3998 memcpy(&dynamicBuffer[pos], output + start, c - start);
3999 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4000 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4001 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4002 dynamicBuffer.size++;
4003 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4005 // printf("%s\n", dynamicBuffer.array);
4007 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4009 dynamicBuffer.size = 0;
4015 int pos = dynamicBuffer.size;
4016 dynamicBuffer.size += c - start;
4017 memcpy(&dynamicBuffer[pos], output + start, c - start);
4023 printf("Got end of file from GDB!\n");
4027 delete dynamicBuffer;
4028 //if(oldValgrindHandle == vgTargetHandle)
4029 debugger.ValgrindTargetThreadExit();
4030 delete oldValgrindHandle;
4036 class GdbThread : Thread
4042 static char output[4096];
4043 Array<char> dynamicBuffer { minAllocSize = 4096 };
4044 DualPipe oldGdbHandle = gdbHandle;
4045 incref oldGdbHandle;
4048 while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
4052 result = gdbHandle.Read(output, 1, sizeof(output));
4054 if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
4061 for(c = 0; c<result; c++)
4063 if(output[c] == '\n')
4065 int pos = dynamicBuffer.size;
4066 dynamicBuffer.size += c - start;
4067 memcpy(&dynamicBuffer[pos], output + start, c - start);
4068 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4069 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4070 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4071 dynamicBuffer.size++;
4072 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4074 // _dpl(0, dynamicBuffer.array);
4076 debugger.GdbThreadMain(&dynamicBuffer[0]);
4077 dynamicBuffer.size = 0;
4083 int pos = dynamicBuffer.size;
4084 dynamicBuffer.size += c - start;
4085 memcpy(&dynamicBuffer[pos], output + start, c - start);
4091 _dpl(0, "Got end of file from GDB!");
4095 delete dynamicBuffer;
4096 //if(oldGdbHandle == gdbHandle)
4097 debugger.GdbThreadExit();
4098 delete oldGdbHandle;
4104 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
4105 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
4107 #if defined(__unix__)
4112 #include <sys/types.h>
4117 class ProgramThread : Thread
4123 bool fileCreated = false;
4125 static char output[1000];
4128 /*if(!mkfifo(progFifoPath, mask))
4135 ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
4139 if(FileExists(progFifoPath)) //fileCreated)
4141 fifoFile = FileOpen(progFifoPath, read);
4145 ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
4150 fd = fileno((FILE *)fifoFile.input);
4151 //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
4155 while(!terminate && fifoFile && !fifoFile.Eof())
4158 struct timeval time;
4166 selectResult = select(fd + 1, &rs, null, null, &time);
4167 if(FD_ISSET(fd, &rs))
4169 int result = (int)read(fd, output, sizeof(output)-1);
4170 if(!result || (result < 0 && errno != EAGAIN))
4174 output[result] = '\0';
4175 if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
4178 ide.outputView.debugBox.Log(output);
4187 //fifoFile.CloseInput();
4190 ide.outputView.debugBox.Log("\n");
4194 if(FileExists(progFifoPath)) //fileCreated)
4196 DeleteFile(progFifoPath);
4197 progFifoPath[0] = '\0';
4205 class Argument : struct
4207 Argument prev, next;
4209 property char * name { set { delete name; if(value) name = CopyString(value); } }
4211 property char * val { set { delete val; if(value) val = CopyString(value); } }
4225 class Frame : struct
4230 property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4232 property char * func { set { delete func; if(value) func = CopyString(value); } }
4236 property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
4238 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4239 char * absoluteFile;
4240 property char * absoluteFile { set { delete absoluteFile; if(value) absoluteFile = CopyUnescapedUnixPath(value); } }
4249 delete absoluteFile;
4250 args.Free(Argument::Free);
4259 class GdbDataStop : struct
4276 char * gdbResultVar;
4286 if(!strcmp(reason, "signal-received"))
4291 else if(!strcmp(reason, "function-finished"))
4293 delete gdbResultVar;
4298 if(frame) frame.Free();
4307 class GdbDataBreakpoint : struct
4311 property char * number { set { delete number; if(value) number = CopyString(value); } }
4313 property char * type { set { delete type; if(value) type = CopyString(value); } }
4315 property char * disp { set { delete disp; if(value) disp = CopyString(value); } }
4318 property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4320 property char * func { set { delete func; if(value) func = CopyString(value); } }
4322 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4324 property char * fullname { set { delete fullname; if(value) fullname = CopyUnescapedUnixPath(value); } }
4327 property char * at { set { delete at; if(value) at = CopyString(value); } }
4340 ~GdbDataBreakpoint()
4346 class Breakpoint : struct
4351 property char * function { set { delete function; if(value) function = CopyString(value); } }
4352 char * relativeFilePath;
4353 property char * relativeFilePath { set { delete relativeFilePath; if(value) relativeFilePath = CopyString(value); } }
4354 char * absoluteFilePath;
4355 property char * absoluteFilePath { set { delete absoluteFilePath; if(value) absoluteFilePath = CopyString(value); } }
4364 BreakpointType type;
4366 GdbDataBreakpoint bp;
4368 char * CopyLocationString(bool removePath)
4371 char * file = relativeFilePath ? relativeFilePath : absoluteFilePath;
4372 bool removingPath = removePath && file;
4375 char * fileName = new char[MAX_FILENAME];
4376 GetLastDirectory(file, fileName);
4382 location = PrintString(file, ":", function);
4384 location = CopyString(function);
4387 location = PrintString(file, ":", line);
4395 if(relativeFilePath && relativeFilePath[0])
4397 f.Printf(" * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
4399 f.Printf(" ~ %s\n", condition.expression);
4409 delete relativeFilePath;
4410 delete absoluteFilePath;
4420 class Watch : struct
4431 f.Printf(" ~ %s\n", expression);
4455 class DebugListItem : struct
4461 struct DebugEvaluationData
4466 uint64 nextBlockAddress;
4468 DebuggerEvaluationError error;
4471 class CodeLocation : struct
4474 char * absoluteFile;
4477 CodeLocation ::ParseCodeLocation(char * location)
4481 char * colon = null;
4483 char loc[MAX_LOCATION];
4484 strcpy(loc, location);
4485 for(temp = loc; temp = strstr(temp, ":"); temp++)
4493 int line = atoi(colon);
4496 CodeLocation codloc { line = line };
4497 codloc.file = CopyString(loc);
4498 codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
4510 delete absoluteFile;
4520 void GDBFallBack(Expression exp, String expString)
4523 ExpressionType evalError = dummyExp;
4524 result = Debugger::EvaluateExpression(expString, &evalError);
4527 exp.constant = result;
4528 exp.type = constantExp;