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, valgrindStartPause;
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 = stopItem ? GetBreakpointById(stopItem.bkptno, &isInternal) : null;
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!");
617 if(bpUser && strcmp(stopItem.frame.addr, bpUser.bp.addr))
621 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Breakpoint bkptno(", stopItem.bkptno, ") invalid or not found!");
622 if(bpUser && bpUser.type == runToCursor)
623 ignoreBreakpoints = false;
624 if((bpUser && !ignoreBreakpoints) || (bpInternal && userBreakOnInternalBreakpoint))
626 hitThread = stopItem.threadid;
630 signalThread = stopItem.threadid;
634 ignoreBreakpoints = false;
636 case valgrindStartPause:
637 GdbExecContinue(true);
646 activeThread = stopItem.threadid;
647 GdbCommand(false, "-thread-list-ids");
649 if(activeFrameLevel > 0)
650 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
652 WatchesCodeEditorLinkInit();
656 if(curEvent == signal)
660 ide.outputView.debugBox.Logf($"Signal received: %s - %s\n", stopItem.name, stopItem.meaning);
661 ide.outputView.debugBox.Logf(" %s:%d\n", (s = CopySystemPath(stopItem.frame.file)), stopItem.frame.line);
662 ide.outputView.Show();
663 ide.callStackView.Show();
666 else if(curEvent == breakEvent)
668 ide.threadsView.Show();
669 ide.callStackView.Show();
670 ide.callStackView.Activate();
673 if(monitor && curEvent.canBeMonitored)
675 SelectFrame(activeFrameLevel);
676 GoToStackFrameLine(activeFrameLevel, true);
677 ide.ShowCodeEditor();
678 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
683 EventHit(stopItem, bpInternal, bpUser);
690 if(userBreakOnInternalBreakpoint)
691 userBreakOnInternalBreakpoint = false;
696 #ifdef GDB_DEBUG_CONSOLE
697 char lastGdbOutput[GdbGetLineSize];
699 #if defined(__unix__)
700 ProgramThread progThread { };
703 void ChangeState(DebuggerState value)
705 bool same = value == state;
706 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ChangeState (", state, same ? " *** == *** " : " -> ", value, ")");
708 if(!same && ide) ide.AdjustDebugMenus();
713 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::CleanUp");
715 stackFrames.Free(Frame::Free);
725 waitingForPID = false;
730 sentBreakInsert = false;
731 ignoreBreakpoints = false;
732 userBreakOnInternalBreakpoint = false;
733 //runToCursorDebugStart = false;
736 activeFrameLevel = 0;
753 bpRunToCursor = null;
755 delete currentCompiler;
759 /*GdbThread gdbThread
765 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::constructor");
766 ideProcessId = Process_GetCurrentProcessId();
768 sysBPs.Add(Breakpoint { type = internalMain, function = "main", enabled = true, level = -1 });
769 #if defined(__WIN32__)
770 sysBPs.Add(Breakpoint { type = internalWinMain, function = "WinMain", enabled = true, level = -1 });
772 sysBPs.Add(Breakpoint { type = internalModulesLoaded, enabled = true, level = -1 });
773 sysBPs.Add(Breakpoint { type = internalModuleLoad, function = "InternalModuleLoadBreakpoint", enabled = true, level = -1 });
778 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::destructor");
786 property bool isActive { get { return state == running || state == stopped; } }
787 property bool isPrepared { get { return state == loaded || state == running || state == stopped; } }
791 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Resume");
792 GdbExecContinue(true);
797 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Break");
801 GdbDebugBreak(false);
807 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Stop");
814 GdbDebugBreak(false);
828 void Restart(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
830 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Restart");
831 if(StartSession(compiler, config, bitDepth, useValgrind, true, false, false/*, false*/) == loaded)
835 bool GoToCodeLine(char * location)
838 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GoToCodeLine(", location, ")");
839 codloc = CodeLocation::ParseCodeLocation(location);
842 CodeEditor editor = (CodeEditor)ide.OpenFile(codloc.absoluteFile, normal, true, null, no, normal, false);
845 EditBox editBox = editor.editBox;
846 editBox.GoToLineNum(codloc.line - 1);
847 editBox.GoToPosition(editBox.line, codloc.line - 1, 0);
854 bool GoToStackFrameLine(int stackLevel, bool askForLocation)
856 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GoToStackFrameLine(", stackLevel, ", ", askForLocation, ")");
859 char filePath[MAX_LOCATION];
860 char sourceDir[MAX_LOCATION];
862 CodeEditor editor = null;
863 if(stackLevel == -1) // this (the two lines) is part of that fix that I would not put in for some time
865 for(frame = stackFrames.first; frame; frame = frame.next)
866 if(frame.level == stackLevel)
870 ide.callStackView.Show();
872 if(!frame.absoluteFile && frame.file)
873 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
874 if(!frame.absoluteFile && askForLocation && frame.file)
877 char title[MAX_LOCATION];
878 snprintf(title, sizeof(title), $"Provide source file location for %s", (s = CopySystemPath(frame.file)));
879 title[sizeof(title)-1] = 0;
881 if(SourceDirDialog(title, ide.workspace.projectDir, frame.file, sourceDir))
883 AddSourceDir(sourceDir);
884 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
887 if(frame.absoluteFile)
888 editor = (CodeEditor)ide.OpenFile(frame.absoluteFile, normal, true, null, no, normal, false);
890 if(editor && frame.line)
892 EditBox editBox = editor.editBox;
893 editBox.GoToLineNum(frame.line - 1);
894 editBox.GoToPosition(editBox.line, frame.line - 1, 0);
902 void SelectThread(int thread)
904 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SelectThread(", thread, ")");
907 if(thread != activeThread)
909 activeFrameLevel = -1;
910 ide.callStackView.Clear();
911 GdbCommand(false, "-thread-select %d", thread);
913 // Why was SelectFrame missing here?
914 SelectFrame(activeFrameLevel);
915 GoToStackFrameLine(activeFrameLevel, true);
916 WatchesCodeEditorLinkRelease();
917 WatchesCodeEditorLinkInit();
921 ide.callStackView.Show();
925 void SelectFrame(int frame)
927 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SelectFrame(", frame, ")");
930 if(frame != activeFrameLevel || !codeEditor || !codeEditor.visible)
932 activeFrameLevel = frame; // there is no active frame number in the gdb reply
933 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
934 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
935 if(activeFrame.level == activeFrameLevel)
938 WatchesCodeEditorLinkRelease();
939 WatchesCodeEditorLinkInit();
946 void HandleExit(char * reason, char * code)
948 bool returnedExitCode = false;
949 char verboseExitCode[128];
951 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::HandleExit(", reason, ", ", code, ")");
952 ChangeState(loaded); // this state change seems to be superfluous, might be in case of gdb crash
957 snprintf(verboseExitCode, sizeof(verboseExitCode), $" with exit code %s", code);
958 verboseExitCode[sizeof(verboseExitCode)-1] = 0;
961 verboseExitCode[0] = '\0';
965 // ClearBreakDisplay();
969 for(wh : ide.workspace.watches)
971 if(wh.type) FreeType(wh.type);
974 ide.watchesView.UpdateWatch(wh);
978 #if defined(__unix__)
981 progThread.terminate = true;
984 fifoFile.CloseInput();
994 char program[MAX_LOCATION];
995 GetSystemPathBuffer(program, targetFile);
997 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
998 else if(!strcmp(reason, "exited-normally"))
999 ide.outputView.debugBox.Logf($"The program %s has exited normally%s.\n", program, verboseExitCode);
1000 else if(!strcmp(reason, "exited"))
1001 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
1002 else if(!strcmp(reason, "exited-signalled"))
1003 ide.outputView.debugBox.Logf($"The program %s has exited with a signal%s.\n", program, verboseExitCode);
1005 ide.outputView.debugBox.Logf($"The program %s has exited (gdb provided an unknown reason)%s.\n", program, verboseExitCode);
1010 DebuggerState StartSession(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool restart, bool userBreakOnInternalBreakpoint, bool ignoreBreakpoints/*, bool runToCursorDebugStart*/)
1012 DebuggerState result = none;
1013 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StartSession(restart(", restart, "), userBreakOnInternalBreakpoint(", userBreakOnInternalBreakpoint, "), ignoreBreakpoints(", ignoreBreakpoints, ")"/*, runToCursorDebugStart(", runToCursorDebugStart, ")"*/);
1014 if(restart && state == running && targetProcessId)
1016 breakType = DebuggerAction::restart;
1017 GdbDebugBreak(false);
1021 if(restart && state == stopped)
1023 if(needReset && state == loaded)
1024 GdbExit(); // this reset is to get a clean state with all the breakpoints until a better state can be maintained on program exit
1026 if(result == none || result == terminated)
1028 ide.outputView.ShowClearSelectTab(debug);
1029 ide.outputView.debugBox.Logf($"Starting debug mode\n");
1036 for(bp : ide.workspace.breakpoints)
1042 //this.runToCursorDebugStart = runToCursorDebugStart;
1044 if(GdbInit(compiler, config, bitDepth, useValgrind))
1049 this.ignoreBreakpoints = ignoreBreakpoints;
1050 this.userBreakOnInternalBreakpoint = userBreakOnInternalBreakpoint;
1051 if(ignoreBreakpoints && (result == loaded || result == stopped))
1052 GdbBreakpointsDelete(false, false);
1057 void Start(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
1059 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Start()");
1060 if(StartSession(compiler, config, bitDepth, useValgrind, true, false, false/*, false*/) == loaded)
1064 void StepInto(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
1066 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepInto()");
1067 switch(StartSession(compiler, config, bitDepth, useValgrind, false, true, false/*, false*/))
1069 case loaded: GdbExecRun(); break;
1070 case stopped: GdbExecStep(); break;
1074 void StepOver(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool ignoreBreakpoints)
1076 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepOver()");
1077 switch(StartSession(compiler, config, bitDepth, useValgrind, false, true, ignoreBreakpoints/*, false*/))
1079 case loaded: GdbExecRun(); break;
1080 case stopped: GdbExecNext(); break;
1084 void StepOut(bool ignoreBreakpoints)
1086 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepOut()");
1087 if(state == stopped)
1089 this.ignoreBreakpoints = ignoreBreakpoints;
1090 if(ignoreBreakpoints)
1091 GdbBreakpointsDelete(true, false);
1096 void RunToCursor(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, char * absoluteFilePath, int lineNumber, bool ignoreBreakpoints, bool atSameLevel)
1098 char relativeFilePath[MAX_LOCATION];
1099 DebuggerState st = state;
1100 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::RunToCursor()");
1103 // ide.outputView.ShowClearSelectTab(debug);
1104 // ide.outputView.debugBox.Logf($"Starting debug mode\n");
1106 if(!ide.projectView.project.GetRelativePath(absoluteFilePath, relativeFilePath))
1107 strcpy(relativeFilePath, absoluteFilePath);
1109 if(bpRunToCursor && bpRunToCursor.inserted && symbols)
1111 UnsetBreakpoint(bpRunToCursor);
1112 delete bpRunToCursor;
1115 bpRunToCursor = Breakpoint { };
1116 bpRunToCursor.absoluteFilePath = absoluteFilePath;
1117 bpRunToCursor.relativeFilePath = relativeFilePath;
1118 bpRunToCursor.line = lineNumber;
1119 bpRunToCursor.type = runToCursor;
1120 bpRunToCursor.enabled = true;
1121 bpRunToCursor.level = atSameLevel ? frameCount - activeFrameLevel -1 : -1;
1123 switch(StartSession(compiler, config, bitDepth, useValgrind, false, false, ignoreBreakpoints/*, true*/))
1129 GdbExecContinue(true);
1134 void GetCallStackCursorLine(bool * error, int * lineCursor, int * lineTopFrame)
1136 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetCallStackCursorLine()");
1137 if(activeFrameLevel == -1)
1145 *error = signalOn && activeThread == signalThread;
1146 *lineCursor = activeFrameLevel - ((frameCount > 192 && activeFrameLevel > 191) ? frameCount - 192 - 1 : 0) + 1;
1147 *lineTopFrame = activeFrameLevel ? 1 : 0;
1151 int GetMarginIconsLineNumbers(char * fileName, int lines[], bool enabled[], int max, bool * error, int * lineCursor, int * lineTopFrame)
1153 char winFilePath[MAX_LOCATION];
1154 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1156 Iterator<Breakpoint> it { ide.workspace.breakpoints };
1157 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetMarginIconsLineNumbers()");
1158 while(it.Next() && count < max)
1160 Breakpoint bp = it.data;
1163 if(bp.absoluteFilePath && bp.absoluteFilePath[0] && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1165 lines[count] = bp.line;
1166 enabled[count] = bp.enabled;
1171 if(activeFrameLevel == -1)
1179 *error = signalOn && activeThread == signalThread;
1180 if(activeFrame && activeFrame.absoluteFile && !fstrcmp(absoluteFilePath, activeFrame.absoluteFile))
1181 *lineCursor = activeFrame.line;
1184 if(activeFrame && stopItem && stopItem.frame && activeFrame.level == stopItem.frame.level)
1186 else if(stopItem && stopItem.frame && stopItem.frame.absoluteFile && !fstrcmp(absoluteFilePath, stopItem.frame.absoluteFile))
1187 *lineTopFrame = stopItem.frame.line;
1191 if(*lineTopFrame == *lineCursor && *lineTopFrame)
1197 void ChangeWatch(DataRow row, char * expression)
1199 Watch wh = (Watch)row.tag;
1200 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ChangeWatch(", expression, ")");
1203 delete wh.expression;
1205 wh.expression = CopyString(expression);
1208 Iterator<Watch> it { ide.workspace.watches };
1210 ide.workspace.watches.Delete(it.pointer);
1216 row.tag = (int64)wh;
1217 ide.workspace.watches.Add(wh);
1219 wh.expression = CopyString(expression);
1221 ide.workspace.Save();
1222 //if(expression && state == stopped)
1227 void MoveIcons(char * fileName, int lineNumber, int move, bool start)
1229 char winFilePath[MAX_LOCATION];
1230 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1233 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::MoveIcons()");
1234 for(bpLink = ide.workspace.breakpoints.first; bpLink; bpLink = next)
1236 Breakpoint bp = (Breakpoint)bpLink.data;
1239 if(bp.type == user && bp.absoluteFilePath && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1241 if(bp.line > lineNumber || (bp.line == lineNumber && start))
1243 if(move < 0 && (bp.line < lineNumber - move))
1244 ide.workspace.RemoveBreakpoint(bp);
1248 ide.breakpointsView.UpdateBreakpoint(bp.row);
1249 ide.workspace.Save();
1255 // moving code cursors is futile, on next step, stop, hit, cursors will be offset anyways
1258 bool SourceDirDialog(char * title, char * startDir, char * test, char * sourceDir)
1262 String srcDir = null;
1264 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SourceDirDialog()");
1265 debuggerFileDialog.text = title;
1266 debuggerFileDialog.currentDirectory = startDir;
1267 debuggerFileDialog.master = ide;
1269 while(debuggerFileDialog.Modal())
1271 strcpy(sourceDir, debuggerFileDialog.filePath);
1272 if(!fstrcmp(ide.workspace.projectDir, sourceDir) &&
1273 MessageBox { type = yesNo, master = ide,
1274 contents = $"This is the project directory.\nWould you like to try again?",
1275 text = $"Invalid Source Directory" }.Modal() == no)
1279 for(dir : ide.workspace.sourceDirs)
1281 if(!fstrcmp(dir, sourceDir))
1289 MessageBox { type = yesNo, master = ide,
1290 contents = $"This source directory is already specified.\nWould you like to try again?",
1291 text = $"Invalid Source Directory" }.Modal() == no)
1297 char file[MAX_LOCATION];
1298 strcpy(file, sourceDir);
1299 PathCat(file, test);
1300 result = FileExists(file);
1302 MessageBox { type = yesNo, master = ide,
1303 contents = $"Unable to locate source file.\nWould you like to try again?",
1304 text = $"Invalid Source Directory" }.Modal() == no)
1318 void AddSourceDir(char * sourceDir)
1320 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::AddSourceDir(", sourceDir, ")");
1321 ide.workspace.sourceDirs.Add(CopyString(sourceDir));
1322 ide.workspace.Save();
1326 DebuggerState oldState = state;
1331 GdbDebugBreak(true);
1334 GdbCommand(false, "-environment-directory \"%s\"", sourceDir);
1337 if(oldState == running)
1338 GdbExecContinue(false);
1342 void ToggleBreakpoint(char * fileName, int lineNumber, Project prj)
1344 char winFilePath[MAX_LOCATION];
1345 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1346 char absolutePath[MAX_LOCATION];
1347 char relativePath[MAX_LOCATION];
1348 char sourceDir[MAX_LOCATION];
1349 Breakpoint bp = null;
1351 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ToggleBreakpoint(", fileName, ":", lineNumber, ")");
1352 strcpy(absolutePath, absoluteFilePath);
1353 for(i : ide.workspace.breakpoints; i.type == user && i.absoluteFilePath && !fstrcmp(i.absoluteFilePath, absolutePath) && i.line == lineNumber)
1362 ide.workspace.RemoveBreakpoint(bp);
1370 // FIXED: This is how it should have been... Source locations are only for files not in project
1371 // if(IsPathInsideOf(absolutePath, ide.workspace.projectDir))
1372 // MakePathRelative(absolutePath, ide.workspace.projectDir, relativePath);
1373 bool result = false;
1375 result = prj.GetRelativePath(absolutePath, relativePath);
1377 result = ide.projectView.project.GetRelativePath(absolutePath, relativePath);
1378 //if(ide.projectView.project.GetRelativePath(absolutePath, relativePath));
1382 char title[MAX_LOCATION];
1383 char directory[MAX_LOCATION];
1384 StripLastDirectory(absolutePath, directory);
1385 snprintf(title, sizeof(title), $"Provide source files location directory for %s", absolutePath);
1386 title[sizeof(title)-1] = 0;
1389 String srcDir = null;
1390 for(dir : ide.workspace.sourceDirs)
1392 if(IsPathInsideOf(absolutePath, dir))
1394 MakePathRelative(absoluteFilePath, dir, relativePath);
1402 if(SourceDirDialog(title, directory, null, sourceDir))
1404 if(IsPathInsideOf(absolutePath, sourceDir))
1406 AddSourceDir(sourceDir);
1407 MakePathRelative(absoluteFilePath, sourceDir, relativePath);
1410 else if(MessageBox { type = yesNo, master = ide,
1411 contents = $"You must provide a valid source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1412 text = $"Invalid Source Directory" }.Modal() == no)
1415 else if(MessageBox { type = yesNo, master = ide,
1416 contents = $"You must provide a source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1417 text = $"No Source Directory Provided" }.Modal() == no)
1421 ide.workspace.bpCount++;
1422 bp = { line = lineNumber, type = user, enabled = true, level = -1 };
1423 ide.workspace.breakpoints.Add(bp);
1424 bp.absoluteFilePath = absolutePath;
1425 bp.relativeFilePath = relativePath;
1426 ide.breakpointsView.AddBreakpoint(bp);
1431 DebuggerState oldState = state;
1436 GdbDebugBreak(true);
1439 SetBreakpoint(bp, false);
1442 if(oldState == running)
1443 GdbExecContinue(false);
1446 ide.workspace.Save();
1449 void UpdateRemovedBreakpoint(Breakpoint bp)
1451 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::UpdateRemovedBreakpoint()");
1452 if(targeted && bp.inserted)
1454 DebuggerState oldState = state;
1459 GdbDebugBreak(true);
1462 UnsetBreakpoint(bp);
1465 if(oldState == running)
1466 GdbExecContinue(false);
1472 void ParseFrame(Frame frame, char * string)
1475 Array<char *> frameTokens { minAllocSize = 50 };
1476 Array<char *> argsTokens { minAllocSize = 50 };
1477 Array<char *> argumentTokens { minAllocSize = 50 };
1478 DebugListItem item { };
1481 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ParseFrame()");
1482 TokenizeList(string, ',', frameTokens);
1483 for(i = 0; i < frameTokens.count; i++)
1485 if(TokenizeListItem(frameTokens[i], item))
1487 StripQuotes(item.value, item.value);
1488 if(!strcmp(item.name, "level"))
1489 frame.level = atoi(item.value);
1490 else if(!strcmp(item.name, "addr"))
1491 frame.addr = item.value;
1492 else if(!strcmp(item.name, "func"))
1493 frame.func = item.value;
1494 else if(!strcmp(item.name, "args"))
1496 if(!strcmp(item.value, "[]"))
1497 frame.argsCount = 0;
1500 item.value = StripBrackets(item.value);
1501 TokenizeList(item.value, ',', argsTokens);
1502 for(j = 0; j < argsTokens.count; j++)
1504 argsTokens[j] = StripCurlies(argsTokens[j]);
1505 TokenizeList(argsTokens[j], ',', argumentTokens);
1506 for(k = 0; k < argumentTokens.count; k++)
1509 frame.args.Add(arg);
1510 if(TokenizeListItem(argumentTokens[k], item))
1512 if(!strcmp(item.name, "name"))
1514 StripQuotes(item.value, item.value);
1515 arg.name = item.value;
1517 else if(!strcmp(item.name, "value"))
1519 StripQuotes(item.value, item.value);
1520 arg.val = item.value;
1523 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "frame args item (", item.name, "=", item.value, ") is unheard of");
1526 _dpl(0, "Bad frame args item");
1528 argumentTokens.RemoveAll();
1530 frame.argsCount = argsTokens.count;
1531 argsTokens.RemoveAll();
1534 else if(!strcmp(item.name, "from"))
1535 frame.from = item.value;
1536 else if(!strcmp(item.name, "file"))
1537 frame.file = item.value;
1538 else if(!strcmp(item.name, "line"))
1539 frame.line = atoi(item.value);
1540 else if(!strcmp(item.name, "fullname"))
1541 frame.absoluteFile = item.value;
1543 // GDB 6.3 on OS X is giving "fullname" and "dir", all in absolute, but file name only in 'file'
1544 String path = ide.workspace.GetPathWorkspaceRelativeOrAbsolute(item.value);
1545 if(strcmp(frame.file, path))
1548 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1553 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "frame member (", item.name, "=", item.value, ") is unheard of");
1556 _dpl(0, "Bad frame");
1561 delete argumentTokens;
1565 Breakpoint GetBreakpointById(int id, bool * isInternal)
1567 Breakpoint bp = null;
1568 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetBreakpointById(", id, ")");
1570 *isInternal = false;
1573 for(i : sysBPs; i.bp && i.bp.id == id)
1580 if(!bp && bpRunToCursor && bpRunToCursor.bp && bpRunToCursor.bp.id == id)
1584 for(i : ide.workspace.breakpoints; i.bp && i.bp.id == id)
1594 GdbDataBreakpoint ParseBreakpoint(char * string, Array<char *> outTokens)
1597 GdbDataBreakpoint bp { };
1598 DebugListItem item { };
1599 Array<char *> bpTokens { minAllocSize = 16 };
1600 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ParseBreakpoint()");
1601 string = StripCurlies(string);
1602 TokenizeList(string, ',', bpTokens);
1603 for(i = 0; i < bpTokens.count; i++)
1605 if(TokenizeListItem(bpTokens[i], item))
1607 StripQuotes(item.value, item.value);
1608 if(!strcmp(item.name, "number"))
1610 if(!strchr(item.value, '.'))
1611 bp.id = atoi(item.value);
1612 bp.number = item.value;
1614 else if(!strcmp(item.name, "type"))
1615 bp.type = item.value;
1616 else if(!strcmp(item.name, "disp"))
1617 bp.disp = item.value;
1618 else if(!strcmp(item.name, "enabled"))
1619 bp.enabled = (!strcmpi(item.value, "y"));
1620 else if(!strcmp(item.name, "addr"))
1622 if(outTokens && !strcmp(item.value, "<MULTIPLE>"))
1625 Array<GdbDataBreakpoint> bpArray = bp.multipleBPs = { };
1626 while(outTokens.count > ++c)
1628 GdbDataBreakpoint multBp = ParseBreakpoint(outTokens[c], null);
1629 bpArray.Add(multBp);
1633 bp.addr = item.value;
1635 else if(!strcmp(item.name, "func"))
1636 bp.func = item.value;
1637 else if(!strcmp(item.name, "file"))
1638 bp.file = item.value;
1639 else if(!strcmp(item.name, "fullname"))
1640 bp.fullname = item.value;
1641 else if(!strcmp(item.name, "line"))
1642 bp.line = atoi(item.value);
1643 else if(!strcmp(item.name, "at"))
1645 else if(!strcmp(item.name, "times"))
1646 bp.times = atoi(item.value);
1647 else if(!strcmp(item.name, "original-location") || !strcmp(item.name, "thread-groups"))
1648 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "breakpoint member (", item.name, "=", item.value, ") is ignored");
1650 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "breakpoint member (", item.name, "=", item.value, ") is unheard of");
1656 void ShowDebuggerViews()
1658 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ShowDebuggerViews()");
1659 ide.outputView.Show();
1660 ide.outputView.SelectTab(debug);
1661 ide.threadsView.Show();
1662 ide.callStackView.Show();
1663 ide.watchesView.Show();
1667 void HideDebuggerViews()
1669 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::HideDebuggerViews()");
1670 ide.RepositionWindows(true);
1673 void ::GdbCommand(bool focus, char * format, ...)
1677 // TODO: Improve this limit
1678 static char string[MAX_F_STRING*4];
1680 va_start(args, format);
1681 vsnprintf(string, sizeof(string), format, args);
1682 string[sizeof(string)-1] = 0;
1686 ide.debugger.serialSemaphore.TryWait();
1688 #ifdef GDB_DEBUG_CONSOLE
1689 _dpl2(_dpct, dplchan::gdbCommand, 0, string);
1691 #ifdef GDB_DEBUG_OUTPUT
1692 ide.outputView.gdbBox.Logf("cmd: %s\n", string);
1694 #ifdef GDB_DEBUG_GUI
1696 ide.gdbDialog.AddCommand(string);
1699 strcat(string,"\n");
1700 gdbHandle.Puts(string);
1703 Process_ShowWindows(targetProcessId);
1706 ide.debugger.serialSemaphore.Wait();
1711 bool ValidateBreakpoint(Breakpoint bp)
1713 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ValidateBreakpoint()");
1714 if(modules && bp.line && bp.bp)
1716 if(bp.bp.line != bp.line)
1722 ide.outputView.debugBox.Logf("WOULD HAVE -- Invalid breakpoint disabled: %s:%d\n", bp.relativeFilePath, bp.line);
1724 //UnsetBreakpoint(bp);
1725 //bp.enabled = false;
1731 ide.outputView.debugBox.Logf("Debugger Error: ValidateBreakpoint error\n");
1732 bp.line = bp.bp.line;
1739 void GdbBreakpointsInsert()
1741 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbBreakpointsInsert()");
1744 DirExpression objDir = ide.project.GetObjDir(currentCompiler, prjConfig, bitDepth);
1745 for(bp : sysBPs; !bp.inserted)
1747 bool insert = false;
1748 if(bp.type == internalModulesLoaded)
1750 char path[MAX_LOCATION];
1751 char name[MAX_LOCATION];
1752 char fixedModuleName[MAX_FILENAME];
1755 bool moduleLoadBlock = false;
1757 ReplaceSpaces(fixedModuleName, ide.project.moduleName);
1758 snprintf(name, sizeof(name),"%s.main.ec", fixedModuleName);
1759 name[sizeof(name)-1] = 0;
1760 strcpy(path, ide.workspace.projectDir);
1761 PathCatSlash(path, objDir.dir);
1762 PathCatSlash(path, name);
1763 f = FileOpen(path, read);
1766 for(lineNumber = 1; !f.Eof(); lineNumber++)
1768 if(f.GetLine(line, sizeof(line) - 1))
1770 bool moduleLoadLine;
1771 TrimLSpaces(line, line);
1772 moduleLoadLine = !strncmp(line, "eModule_Load", strlen("eModule_Load"));
1773 if(!moduleLoadBlock && moduleLoadLine)
1774 moduleLoadBlock = true;
1775 else if(moduleLoadBlock && !moduleLoadLine && strlen(line) > 0)
1781 char relative[MAX_LOCATION];
1782 bp.absoluteFilePath = path;
1783 MakePathRelative(path, ide.workspace.projectDir, relative);
1784 bp.relativeFilePath = relative;
1785 bp.line = lineNumber;
1791 else if(bp.type == internalModuleLoad)
1795 for(prj : ide.workspace.projects)
1797 if(!strcmp(prj.moduleName, "ecere"))
1799 ProjectNode node = prj.topNode.Find("instance.c", false);
1802 char path[MAX_LOCATION];
1803 char relative[MAX_LOCATION];
1804 node.GetFullFilePath(path);
1805 bp.absoluteFilePath = path;
1806 MakePathRelative(path, prj.topNode.path, relative);
1807 bp.relativeFilePath = relative;
1818 SetBreakpoint(bp, false);
1822 if(bpRunToCursor && !bpRunToCursor.inserted)
1823 SetBreakpoint(bpRunToCursor, false);
1825 if(!ignoreBreakpoints)
1827 for(bp : ide.workspace.breakpoints; !bp.inserted && bp.type == user)
1831 if(!SetBreakpoint(bp, false))
1832 SetBreakpoint(bp, true);
1840 bp.bp = GdbDataBreakpoint { };
1847 void UnsetBreakpoint(Breakpoint bp)
1849 char * s; _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::UnsetBreakpoint(", s=bp.CopyLocationString(false), ")"); delete s;
1850 if(symbols && bp.inserted)
1852 GdbCommand(false, "-break-delete %s", bp.bp.number);
1853 bp.inserted = false;
1858 bool SetBreakpoint(Breakpoint bp, bool removePath)
1860 char * s; _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SetBreakpoint(", s=bp.CopyLocationString(false), ", ", removePath ? "**** removePath(true) ****" : "", ")"); delete s;
1861 breakpointError = false;
1864 char * location = bp.CopyLocationString(removePath);
1865 sentBreakInsert = true;
1866 GdbCommand(false, "-break-insert %s", location);
1868 if(!breakpointError)
1870 if(bpItem.multipleBPs && bpItem.multipleBPs.count)
1873 GdbDataBreakpoint first = null;
1874 for(n : bpItem.multipleBPs)
1876 if(!fstrcmp(n.fullname, bp.absoluteFilePath))
1886 GdbCommand(false, "-break-disable %s", n.number);
1890 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error breakpoint already disabled.");
1895 bpItem.addr = first.addr;
1896 bpItem.func = first.func;
1897 bpItem.file = first.file;
1898 bpItem.fullname = first.fullname;
1899 bpItem.line = first.line;
1900 //bpItem.thread-groups = first.thread-groups;
1901 bpItem.multipleBPs.Free();
1902 delete bpItem.multipleBPs;
1905 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error multiple breakpoints all disabled.");
1907 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error multiple breakpoints in exact same file not supported.");
1911 bp.inserted = (bp.bp && bp.bp.number && strcmp(bp.bp.number, "0"));
1913 ValidateBreakpoint(bp);
1914 /*if(bp == bpRunToCursor)
1915 runToCursorDebugStart = false;*/
1918 return !breakpointError;
1921 void GdbBreakpointsDelete(bool deleteRunToCursor, bool deleteInternalBreakpoints)
1923 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbBreakpointsDelete(deleteRunToCursor(", deleteRunToCursor, "))");
1926 if(deleteInternalBreakpoints)
1929 UnsetBreakpoint(bp);
1931 for(bp : ide.workspace.breakpoints)
1932 UnsetBreakpoint(bp);
1933 if(deleteRunToCursor && bpRunToCursor)
1934 UnsetBreakpoint(bpRunToCursor);
1940 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbGetStack()");
1942 stackFrames.Free(Frame::Free);
1943 GdbCommand(false, "-stack-info-depth");
1945 GdbCommand(false, "-stack-info-depth 192");
1946 if(frameCount && frameCount <= 192)
1947 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 191));
1950 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 95));
1951 GdbCommand(false, "-stack-list-frames %d %d", Max(frameCount - 96, 96), frameCount - 1);
1953 GdbCommand(false, "");
1958 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbTargetSet()");
1961 char escaped[MAX_LOCATION];
1962 strescpy(escaped, targetFile);
1963 GdbCommand(false, "file \"%s\"", escaped); //GDB/MI Missing Implementation -symbol-file, -target-attach
1970 const char *vgdbCommand = "/usr/bin/vgdb"; // TODO: vgdb command config option
1971 //GdbCommand(false, "-target-select remote | %s --pid=%d", "vgdb", targetProcessId);
1972 printf("target remote | %s --pid=%d\n", vgdbCommand, targetProcessId);
1973 GdbCommand(false, "target remote | %s --pid=%d", vgdbCommand, targetProcessId); // TODO: vgdb command config option
1976 /*for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
1977 GdbCommand(false, "-environment-directory \"%s\"", prj.topNode.path);*/
1979 for(dir : ide.workspace.sourceDirs; dir && dir[0])
1981 bool interference = false;
1982 for(prj : ide.workspace.projects)
1984 if(!fstrcmp(prj.topNode.path, dir))
1986 interference = true;
1990 if(!interference && dir[0])
1991 GdbCommand(false, "-environment-directory \"%s\"", dir);
1999 /*void GdbTargetRelease()
2003 GdbBreakpointsDelete(true, true);
2004 GdbCommand(false, "file"); //GDB/MI Missing Implementation -target-detach
2010 void GdbDebugBreak(bool internal)
2012 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbDebugBreak()");
2016 breakType = DebuggerAction::internal;
2018 if(ide) ide.Update(null);
2020 if(Process_Break(targetProcessId)) //GdbCommand(false, "-exec-interrupt");
2021 serialSemaphore.Wait();
2024 ChangeState(loaded);
2025 targetProcessId = 0;
2030 ide.outputView.debugBox.Logf("Debugger Error: GdbDebugBreak with not target id should never happen\n");
2035 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecRun()");
2038 ShowDebuggerViews();
2040 GdbCommand(true, "-exec-continue");
2042 GdbCommand(true, "-exec-run");
2045 void GdbExecContinue(bool focus)
2047 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecContinue()");
2049 GdbCommand(focus, "-exec-continue");
2054 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecNext()");
2056 GdbCommand(true, "-exec-next");
2061 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecStep()");
2063 GdbCommand(true, "-exec-step");
2066 void GdbExecFinish()
2068 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecFinish()");
2070 GdbCommand(true, "-exec-finish");
2073 void GdbExecCommon()
2075 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecCommon()");
2076 ClearBreakDisplay();
2077 GdbBreakpointsInsert();
2080 #ifdef GDB_DEBUG_GUI
2081 void SendGDBCommand(char * command)
2083 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SendGDBCommand()");
2084 DebuggerState oldState = state;
2089 GdbDebugBreak(true);
2092 GdbCommand(false, command);
2095 if(oldState == running)
2096 GdbExecContinue(false);
2100 void ClearBreakDisplay()
2102 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ClearBreakDisplay()");
2104 activeFrameLevel = -1;
2114 stackFrames.Free(Frame::Free);
2115 WatchesCodeEditorLinkRelease();
2116 ide.callStackView.Clear();
2117 ide.threadsView.Clear();
2123 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbAbortExec()");
2125 GdbCommand(false, "-interpreter-exec console \"kill\""); // should use -exec-abort -- GDB/MI implementation incomplete
2129 bool GdbInit(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
2132 char oldDirectory[MAX_LOCATION];
2133 char tempPath[MAX_LOCATION];
2134 char command[MAX_F_STRING*4];
2135 Project project = ide.project;
2136 DirExpression targetDirExp = project.GetTargetDir(compiler, config, bitDepth);
2137 PathBackup pathBackup { };
2139 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbInit()");
2140 if(currentCompiler != compiler)
2142 delete currentCompiler;
2143 currentCompiler = compiler;
2144 incref currentCompiler;
2147 this.bitDepth = bitDepth;
2148 usingValgrind = useValgrind;
2150 ChangeState(loaded);
2152 sentBreakInsert = false;
2153 breakpointError = false;
2154 ignoreBreakpoints = false;
2160 ide.outputView.ShowClearSelectTab(debug);
2161 ide.outputView.debugBox.Logf($"Starting debug mode\n");
2163 #ifdef GDB_DEBUG_OUTPUT
2164 ide.outputView.gdbBox.Logf("run: Starting GDB\n");
2167 strcpy(tempPath, ide.workspace.projectDir);
2168 PathCatSlash(tempPath, targetDirExp.dir);
2170 targetDir = CopyString(tempPath);
2171 project.CatTargetFileName(tempPath, compiler, config);
2173 targetFile = CopyString(tempPath);
2175 GetWorkingDir(oldDirectory, MAX_LOCATION);
2176 if(ide.workspace.debugDir && ide.workspace.debugDir[0])
2178 char temp[MAX_LOCATION];
2179 strcpy(temp, ide.workspace.projectDir);
2180 PathCatSlash(temp, ide.workspace.debugDir);
2181 ChangeWorkingDir(temp);
2184 ChangeWorkingDir(ide.workspace.projectDir);
2186 ide.SetPath(true, compiler, config, bitDepth);
2188 // TODO: This pollutes the environment, but at least it works
2189 // It shouldn't really affect the IDE as the PATH gets restored and other variables set for testing will unlikely cause problems
2190 // What is the proper solution for this? DualPipeOpenEnv?
2191 // gdb set environment commands don't seem to take effect
2192 for(e : ide.workspace.environmentVars)
2194 SetEnvironment(e.name, e.string);
2199 char * clArgs = ide.workspace.commandLineArgs;
2200 const char *valgrindCommand = "valgrind"; // TODO: valgrind command config option //TODO: valgrind options
2201 ValgrindLeakCheck vgLeakCheck = ide.workspace.vgLeakCheck;
2202 int vgRedzoneSize = ide.workspace.vgRedzoneSize;
2203 bool vgTrackOrigins = ide.workspace.vgTrackOrigins;
2204 vgLogFile = CreateTemporaryFile(vgLogPath, "ecereidevglog");
2208 vgLogThread.Create();
2212 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't open temporary log file for Valgrind output\n");
2215 if(result && !CheckCommandAvailable(valgrindCommand))
2217 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for Valgrind is not available.\n", valgrindCommand);
2222 sprintf(command, "%s --vgdb=yes --vgdb-error=0 --log-file=%s --leak-check=%s --redzone-size=%d --track-origins=%s %s%s%s",
2223 valgrindCommand, vgLogPath, (char*)vgLeakCheck, vgRedzoneSize, vgTrackOrigins ? "yes" : "no", targetFile, clArgs ? " " : "", clArgs ? clArgs : "");
2224 vgTargetHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
2227 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start Valgrind\n");
2233 incref vgTargetHandle;
2234 vgTargetThread.Create();
2236 targetProcessId = vgTargetHandle.GetProcessID();
2237 waitingForPID = false;
2238 if(!targetProcessId)
2240 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get Valgrind process ID\n");
2247 serialSemaphore.Wait();
2255 (compiler.targetPlatform == win32 && bitDepth == 64) ? "x86_64-w64-mingw32-gdb" :
2256 (compiler.targetPlatform == win32 && bitDepth == 32) ? "i686-w64-mingw32-gdb" :
2258 if(!CheckCommandAvailable(command))
2260 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for GDB is not available.\n", command);
2265 strcat(command, " -n -silent --interpreter=mi2"); //-async //\"%s\"
2267 gdbHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
2270 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start GDB\n");
2280 gdbProcessId = gdbHandle.GetProcessID();
2283 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get GDB process ID\n");
2290 serialSemaphore.Wait();
2293 GdbCommand(false, "-gdb-set verbose off");
2294 //GdbCommand(false, "-gdb-set exec-done-display on");
2295 GdbCommand(false, "-gdb-set step-mode off");
2296 GdbCommand(false, "-gdb-set unwindonsignal on");
2297 //GdbCommand(false, "-gdb-set shell on");
2298 GdbCommand(false, "set print elements 992");
2299 GdbCommand(false, "-gdb-set backtrace limit 100000");
2303 //ChangeState(terminated);
2309 #if defined(__unix__)
2311 CreateTemporaryDir(progFifoDir, "ecereide");
2312 strcpy(progFifoPath, progFifoDir);
2313 PathCat(progFifoPath, "ideprogfifo");
2314 if(!mkfifo(progFifoPath, 0600))
2316 //fileCreated = true;
2321 ide.outputView.debugBox.Logf(createFIFOMsg, progFifoPath);
2328 progThread.terminate = false;
2329 progThread.Create();
2333 #if defined(__WIN32__)
2334 GdbCommand(false, "-gdb-set new-console on");
2337 #if defined(__unix__)
2339 GdbCommand(false, "-inferior-tty-set %s", progFifoPath);
2343 GdbCommand(false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
2345 for(e : ide.workspace.environmentVars)
2347 GdbCommand(false, "set environment %s=%s", e.name, e.string);
2352 ChangeWorkingDir(oldDirectory);
2358 delete targetDirExp;
2364 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExit()");
2365 if(gdbHandle && gdbProcessId)
2367 GdbCommand(false, "-gdb-exit");
2382 ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
2388 for(bp : ide.workspace.breakpoints)
2390 bp.inserted = false;
2396 bp.inserted = false;
2401 bpRunToCursor.inserted = false;
2402 delete bpRunToCursor.bp;
2405 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2406 ClearBreakDisplay();
2409 #if defined(__unix__)
2410 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
2412 progThread.terminate = true;
2415 fifoFile.CloseInput();
2421 DeleteFile(progFifoPath);
2422 progFifoPath[0] = '\0';
2428 void WatchesCodeEditorLinkInit()
2430 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkInit()");
2432 char tempPath[MAX_LOCATION];
2433 char path[MAX_LOCATION];
2435 //void MakeFilePathProjectRelative(char * path, char * relativePath)
2436 if(!ide.projectView.project.GetRelativePath(activeFrame.file, tempPath))
2437 strcpy(tempPath, activeFrame.file);
2439 strcpy(path, ide.workspace.projectDir);
2440 PathCat(path, tempPath);
2441 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2444 for(srcDir : ide.workspace.sourceDirs)
2446 strcpy(path, srcDir);
2447 PathCat(path, tempPath);
2448 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2449 if(codeEditor) break;
2454 /*if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2455 activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);*/
2456 if(!activeFrame || !activeFrame.absoluteFile)
2459 codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal, false);
2462 codeEditor.inUseDebug = true;
2465 //watchesInit = true;
2468 void WatchesCodeEditorLinkRelease()
2470 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkRelease()");
2475 codeEditor.inUseDebug = false;
2476 if(!codeEditor.visible)
2477 codeEditor.Destroy(0);
2483 bool ResolveWatch(Watch wh)
2485 bool result = false;
2487 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ResolveWatch()");
2499 char watchmsg[MAX_F_STRING];
2500 if(state == stopped && !codeEditor)
2501 wh.value = CopyString($"No source file found for selected frame");
2502 //if(codeEditor && state == stopped || state != stopped)
2505 Module backupPrivateModule;
2506 Context backupContext;
2507 Class backupThisClass;
2511 backupPrivateModule = GetPrivateModule();
2512 backupContext = GetCurrentContext();
2513 backupThisClass = GetThisClass();
2516 SetPrivateModule(codeEditor.privateModule);
2517 SetCurrentContext(codeEditor.globalContext);
2518 SetTopContext(codeEditor.globalContext);
2519 SetGlobalContext(codeEditor.globalContext);
2520 SetGlobalData(&codeEditor.globalData);
2523 exp = ParseExpressionString(wh.expression);
2525 if(exp && !parseError)
2527 char expString[4096];
2529 PrintExpression(exp, expString);
2531 if(GetPrivateModule())
2534 DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2535 ProcessExpressionType(exp);
2537 wh.type = exp.expType;
2540 DebugComputeExpression(exp);
2541 if(ExpressionIsError(exp))
2543 GDBFallBack(exp, expString);
2546 /*if(exp.hasAddress)
2548 char temp[MAX_F_STRING];
2549 sprintf(temp, "0x%x", exp.address);
2550 wh.address = CopyString(temp);
2551 // wh.address = CopyStringf("0x%x", exp.address);
2556 Type dataType = exp.expType;
2559 char temp[MAX_F_STRING];
2560 switch(dataType.kind)
2563 sprintf(temp, "%i", exp.val.c);
2566 sprintf(temp, "%i", exp.val.s);
2571 sprintf(temp, "%i", exp.val.i);
2574 sprintf(temp, "%i", exp.val.i64);
2577 sprintf(temp, "%i", exp.val.p);
2582 long v = (long)exp.val.f;
2583 sprintf(temp, "%i", v);
2588 long v = (long)exp.val.d;
2589 sprintf(temp, "%i", v);
2594 wh.intVal = CopyString(temp);
2595 switch(dataType.kind)
2598 sprintf(temp, "0x%x", exp.val.c);
2601 sprintf(temp, "0x%x", exp.val.s);
2605 sprintf(temp, "0x%x", exp.val.i);
2608 sprintf(temp, "0x%x", exp.val.i64);
2611 sprintf(temp, "0x%x", exp.val.i64);
2614 sprintf(temp, "0x%x", exp.val.p);
2619 long v = (long)exp.val.f;
2620 sprintf(temp, "0x%x", v);
2625 long v = (long)exp.val.d;
2626 sprintf(temp, "0x%x", v);
2631 wh.hexVal = CopyString(temp);
2632 switch(dataType.kind)
2635 sprintf(temp, "0o%o", exp.val.c);
2638 sprintf(temp, "0o%o", exp.val.s);
2642 sprintf(temp, "0o%o", exp.val.i);
2645 sprintf(temp, "0o%o", exp.val.i64);
2648 sprintf(temp, "0o%o", exp.val.i64);
2651 sprintf(temp, "0o%o", exp.val.p);
2656 long v = (long)exp.val.f;
2657 sprintf(temp, "0o%o", v);
2662 long v = (long)exp.val.d;
2663 sprintf(temp, "0o%o", v);
2668 wh.octVal = CopyString(temp);
2671 // WHATS THIS HERE ?
2672 if(exp.type == constantExp && exp.constant)
2673 wh.constant = CopyString(exp.constant);
2679 case symbolErrorExp:
2680 snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2682 case structMemberSymbolErrorExp:
2683 // todo get info as in next case (ExpClassMemberSymbolError)
2684 snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2686 case classMemberSymbolErrorExp:
2689 Expression memberExp = exp.member.exp;
2690 Identifier memberID = exp.member.member;
2691 Type type = memberExp.expType;
2694 _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2697 char string[256] = "";
2699 PrintTypeNoConst(type, string, false, true);
2700 classSym = FindClass(string);
2701 _class = classSym ? classSym.registered : null;
2704 snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2706 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2709 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2712 case memoryErrorExp:
2713 // Need to ensure when set to memoryErrorExp, constant is set
2714 snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2716 case dereferenceErrorExp:
2717 snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2719 case unknownErrorExp:
2720 snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2722 case noDebuggerErrorExp:
2723 snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2725 case debugStateErrorExp:
2726 snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2729 snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2733 // Temporary Code for displaying Strings
2734 if((exp.expType && ((exp.expType.kind == pointerType ||
2735 exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) ||
2736 (wh.type && wh.type.kind == classType && wh.type._class &&
2737 wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2738 !strcmp(wh.type._class.registered.name, "String")))
2741 if(exp.expType.kind != arrayType || exp.hasAddress)
2747 //char temp[MAX_F_STRING * 32];
2749 ExpressionType evalError = dummyExp;
2750 /*if(exp.expType.kind == arrayType)
2751 sprintf(temp, "(char*)0x%x", exp.address);
2753 sprintf(temp, "(char*)%s", exp.constant);*/
2755 //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2756 // address = strtoul(exp.constant, null, 0);
2757 address = _strtoui64(exp.constant, null, 0);
2758 //_dpl(0, "0x", address);
2759 // snprintf(value, sizeof(value), "0x%08x ", address);
2761 if(address > 0xFFFFFFFFLL)
2762 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2764 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2765 value[sizeof(value)-1] = 0;
2768 strcat(value, $"Null string");
2772 len = strlen(value);
2774 while(!string && size > 2)
2776 string = GdbReadMemory(address, size);
2779 if(string && string[0])
2782 if(UTF8Validate(string))
2787 for(c = 0; (ch = string[c]) && c<4096; c++)
2790 value[len++] = '\0';
2795 ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2796 strcat(value, ") (ISO8859-1)");
2803 strcat(value, $"Empty string");
2807 strcat(value, $"Couldn't read memory");
2809 wh.value = CopyString(value);
2812 else if(wh.type && wh.type.kind == classType && wh.type._class &&
2813 wh.type._class.registered && wh.type._class.registered.type == enumClass)
2815 uint64 value = strtoul(exp.constant, null, 0);
2816 Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2817 EnumClassData enumeration = (EnumClassData)enumClass.data;
2819 for(item = enumeration.values.first; item; item = item.next)
2820 if((int)item.data == value)
2823 wh.value = CopyString(item.name);
2825 wh.value = CopyString($"Invalid Enum Value");
2828 else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class &&
2829 wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2836 if(exp.constant[0] == '\'')
2838 if((int)((byte *)exp.constant)[1] > 127)
2841 value = UTF8GetChar(exp.constant + 1, &nb);
2842 if(nb < 2) value = exp.constant[1];
2843 signedValue = value;
2847 signedValue = exp.constant[1];
2849 // Precomp Syntax error with boot strap here:
2850 byte b = (byte)(char)signedValue;
2851 value = (unichar) b;
2857 if(wh.type.kind == charType && wh.type.isSigned)
2859 signedValue = (int)(char)strtol(exp.constant, null, 0);
2861 // Precomp Syntax error with boot strap here:
2862 byte b = (byte)(char)signedValue;
2863 value = (unichar) b;
2868 value = (uint)strtoul(exp.constant, null, 0);
2869 signedValue = (int)value;
2873 UTF32toUTF8Len(&value, 1, charString, 5);
2875 snprintf(string, sizeof(string), "\'\\0' (0)");
2876 else if(value == '\t')
2877 snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2878 else if(value == '\n')
2879 snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2880 else if(value == '\r')
2881 snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2882 else if(wh.type.kind == charType && wh.type.isSigned)
2883 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2884 else if(value > 256 || wh.type.kind != charType)
2886 if(value > 0x10FFFF || !GetCharCategory(value))
2887 snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2889 snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2892 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2893 string[sizeof(string)-1] = 0;
2895 wh.value = CopyString(string);
2900 wh.value = CopyString(exp.constant);
2907 wh.value = PrintHexUInt64(exp.address);
2912 char tempString[256];
2913 if(exp.member.memberType == propertyMember)
2914 snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2916 snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression,
2917 exp.type.OnGetString(tempString, null, null));
2923 snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2924 if(exp) FreeExpression(exp);
2927 SetPrivateModule(backupPrivateModule);
2928 SetCurrentContext(backupContext);
2929 SetTopContext(backupContext);
2930 SetGlobalContext(backupContext);
2931 SetThisClass(backupThisClass);
2934 // wh.value = CopyString("No source file found for selected frame");
2936 watchmsg[sizeof(watchmsg)-1] = 0;
2938 wh.value = CopyString(watchmsg);
2940 ide.watchesView.UpdateWatch(wh);
2944 void EvaluateWatches()
2946 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateWatches()");
2947 for(wh : ide.workspace.watches)
2951 char * ::GdbEvaluateExpression(char * expression)
2953 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbEvaluateExpression(", expression, ")");
2956 GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2958 ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2962 // to be removed... use GdbReadMemory that returns a byte array instead
2963 char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
2965 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemoryString(", address, ")");
2970 _dpl(0, "GdbReadMemoryString called with size = 0!");
2972 // GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
2973 if(GetRuntimePlatform() == win32)
2974 GdbCommand(false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
2976 GdbCommand(false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
2978 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
2982 byte * ::GdbReadMemory(uint64 address, int bytes)
2984 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemory(", address, ")");
2987 //GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
2988 if(GetRuntimePlatform() == win32)
2989 GdbCommand(false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
2991 GdbCommand(false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
2994 _dpl(0, "GdbReadMemory called with bytes = 0!");
2997 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
2998 else if(eval.result && strcmp(eval.result, "N/A"))
3000 byte * result = new byte[bytes];
3001 byte * string = eval.result;
3005 result[c++] = (byte)strtol(string, &string, 10);
3021 void EventHit(GdbDataStop stopItem, Breakpoint bpInternal, Breakpoint bpUser)
3023 char * s1 = null; char * s2 = null;
3024 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EventHit(",
3025 "bpInternal(", bpInternal ? s1=bpInternal.CopyLocationString(false) : null, "), ",
3026 "bpUser(", bpUser ? s2=bpUser.CopyLocationString(false) : null, ")) -- ",
3027 "ignoreBreakpoints(", ignoreBreakpoints, "), ",
3028 "hitCursorBreakpoint(", bpUser && bpUser.type == runToCursor, ")");
3029 delete s1; delete s2;
3031 if(bpUser && stopItem.frame.line && bpUser.line != stopItem.frame.line)
3033 // updating user breakpoint on hit location difference
3034 // todo, print something?
3035 bpUser.line = stopItem.frame.line;
3036 ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3037 ide.workspace.Save();
3042 if(bpInternal.type == internalModulesLoaded)
3044 if(!bpUser && !userBreakOnInternalBreakpoint)
3045 GdbExecContinue(false);
3049 bool conditionMet = true;
3050 if(bpUser.condition)
3051 conditionMet = ResolveWatch(bpUser.condition);
3053 if(!ignoreBreakpoints && (bpUser.level == -1 || bpUser.level == frameCount-1) && conditionMet)
3060 GdbExecContinue(false);
3064 GdbExecContinue(false);
3065 ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3066 if(bpUser == bpRunToCursor)
3068 UnsetBreakpoint(bpUser);
3069 delete bpRunToCursor;
3073 if(!bpUser && !bpInternal)
3074 GdbExecContinue(false);
3077 void ValgrindTargetThreadExit()
3079 ide.outputView.debugBox.Logf($"ValgrindTargetThreadExit\n");
3082 vgTargetHandle.Wait();
3083 delete vgTargetHandle;
3085 HandleExit(null, null);
3088 void GdbThreadExit()
3090 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbThreadExit()");
3091 if(state != terminated)
3093 ChangeState(terminated);
3094 targetProcessId = 0;
3095 ClearBreakDisplay();
3101 serialSemaphore.Release();
3106 ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
3107 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3109 HideDebuggerViews();
3111 //ChangeState(terminated);
3115 void GdbThreadMain(char * output)
3118 Array<char *> outTokens { minAllocSize = 50 };
3119 Array<char *> subTokens { minAllocSize = 50 };
3120 DebugListItem item { };
3121 DebugListItem item2 { };
3122 bool setWaitingForPID = false;
3124 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
3125 #ifdef GDB_DEBUG_CONSOLE
3126 _dpl2(_dpct, dplchan::gdbOutput, 0, output);
3128 #ifdef GDB_DEBUG_OUTPUT
3130 int len = strlen(output);
3138 for(c = 0; c < len / 1024; c++)
3140 strncpy(tmp, start, 1024);
3141 ide.outputView.gdbBox.Logf("out: %s\n", tmp);
3144 ide.outputView.gdbBox.Logf("out: %s\n", start);
3148 ide.outputView.gdbBox.Logf("out: %s\n", output);
3152 #ifdef GDB_DEBUG_CONSOLE
3153 strcpy(lastGdbOutput, output);
3155 #ifdef GDB_DEBUG_GUI
3156 if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
3163 if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
3166 ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
3172 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
3174 //if(outTokens.count == 1)
3179 ChangeState(loaded);
3180 targetProcessId = 0;
3181 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3183 if(!strcmp(item.name, "reason"))
3185 char * reason = item.value;
3186 StripQuotes(reason, reason);
3187 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3190 if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
3192 StripQuotes(item2.value, item2.value);
3193 if(!strcmp(item2.name, "exit-code"))
3194 exitCode = item2.value;
3200 HandleExit(reason, exitCode);
3204 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "kill reply (", item.name, "=", item.value, ") is unheard of");
3207 HandleExit(null, null);
3210 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3212 if(!strcmp(item.name, "bkpt"))
3214 sentBreakInsert = false;
3219 bpItem = ParseBreakpoint(item.value, outTokens);
3220 //breakType = bpValidation;
3222 else if(!strcmp(item.name, "depth"))
3224 StripQuotes(item.value, item.value);
3225 frameCount = atoi(item.value);
3227 stackFrames.Free(Frame::Free);
3229 else if(!strcmp(item.name, "stack"))
3232 if(stackFrames.count)
3233 ide.callStackView.Logf("...\n");
3236 item.value = StripBrackets(item.value);
3237 TokenizeList(item.value, ',', subTokens);
3238 for(i = 0; i < subTokens.count; i++)
3240 if(TokenizeListItem(subTokens[i], item))
3242 if(!strcmp(item.name, "frame"))
3245 stackFrames.Add(frame);
3246 item.value = StripCurlies(item.value);
3247 ParseFrame(frame, item.value);
3248 if(frame.file && frame.from)
3249 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "unexpected frame file and from members present");
3253 if(activeFrameLevel == -1)
3255 if(ide.projectView.IsModuleInProject(frame.file));
3257 if(frame.level != 0)
3259 //stopItem.frame = frame;
3260 breakType = selectFrame;
3263 activeFrame = frame;
3264 activeFrameLevel = frame.level;
3267 ide.callStackView.Logf("%3d ", frame.level);
3268 if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
3269 ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
3270 else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
3271 ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
3272 else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
3273 ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
3274 else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
3275 ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
3277 ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
3282 ide.callStackView.Logf("%3d ", frame.level);
3287 ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
3291 ide.callStackView.Logf("%s\n", frame.func);
3293 ide.callStackView.Logf($"unknown source\n");
3297 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "stack content (", item.name, "=", item.value, ") is unheard of");
3300 if(activeFrameLevel == -1)
3302 activeFrameLevel = 0;
3303 activeFrame = stackFrames.first;
3305 ide.callStackView.Home();
3307 subTokens.RemoveAll();
3309 /*else if(!strcmp(item.name, "frame"))
3312 item.value = StripCurlies(item.value);
3313 ParseFrame(&frame, item.value);
3315 else if(!strcmp(item.name, "thread-ids"))
3317 ide.threadsView.Clear();
3318 item.value = StripCurlies(item.value);
3319 TokenizeList(item.value, ',', subTokens);
3320 for(i = subTokens.count - 1; ; i--)
3322 if(TokenizeListItem(subTokens[i], item))
3324 if(!strcmp(item.name, "thread-id"))
3327 StripQuotes(item.value, item.value);
3328 value = atoi(item.value);
3329 ide.threadsView.Logf("%3d \n", value);
3332 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "threads content (", item.name, "=", item.value, ") is unheard of");
3337 ide.threadsView.Home();
3339 subTokens.RemoveAll();
3340 //if(!strcmp(outTokens[2], "number-of-threads"))
3342 else if(!strcmp(item.name, "new-thread-id"))
3344 StripQuotes(item.value, item.value);
3345 activeThread = atoi(item.value);
3347 else if(!strcmp(item.name, "value"))
3349 StripQuotes(item.value, item.value);
3350 eval.result = CopyString(item.value);
3351 eval.active = false;
3353 else if(!strcmp(item.name, "addr"))
3355 for(i = 2; i < outTokens.count; i++)
3357 if(TokenizeListItem(outTokens[i], item))
3359 if(!strcmp(item.name, "total-bytes"))
3361 StripQuotes(item.value, item.value);
3362 eval.bytes = atoi(item.value);
3364 else if(!strcmp(item.name, "next-row"))
3366 StripQuotes(item.value, item.value);
3367 eval.nextBlockAddress = _strtoui64(item.value, null, 0);
3369 else if(!strcmp(item.name, "memory"))
3373 //StripQuotes(item.value, item.value);
3374 item.value = StripBrackets(item.value);
3375 // this should be treated as a list...
3376 item.value = StripCurlies(item.value);
3377 TokenizeList(item.value, ',', subTokens);
3378 for(j = 0; j < subTokens.count; j++)
3380 if(TokenizeListItem(subTokens[j], item))
3382 if(!strcmp(item.name, "data"))
3384 item.value = StripBrackets(item.value);
3385 StripQuotes2(item.value, item.value);
3386 eval.result = CopyString(item.value);
3387 eval.active = false;
3391 subTokens.RemoveAll();
3396 else if(!strcmp(item.name, "source-path") || !strcmp(item.name, "BreakpointTable"))
3397 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "command reply (", item.name, "=", item.value, ") is ignored");
3399 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "command reply (", item.name, "=", item.value, ") is unheard of");
3402 else if(!strcmp(outTokens[0], "^running"))
3404 waitingForPID = true;
3405 setWaitingForPID = true;
3407 else if(!strcmp(outTokens[0], "^exit"))
3409 ChangeState(terminated);
3410 // ide.outputView.debugBox.Logf("Exit\n");
3411 // ide.Update(null);
3413 serialSemaphore.Release();
3415 else if(!strcmp(outTokens[0], "^error"))
3419 sentBreakInsert = false;
3420 breakpointError = true;
3423 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3425 if(!strcmp(item.name, "msg"))
3427 StripQuotes(item.value, item.value);
3430 eval.active = false;
3432 if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3433 eval.error = symbolNotFound;
3434 else if(strstr(item.value, "Cannot access memory at address"))
3435 eval.error = memoryCantBeRead;
3437 eval.error = unknown;
3439 else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3442 else if(!strncmp(item.value, "Cannot access memory at address", 31))
3445 else if(!strcmp(item.value, "Cannot find bounds of current function"))
3447 ChangeState(stopped);
3448 gdbHandle.Printf("-exec-continue\n");
3450 else if(!strcmp(item.value, "ptrace: No such process."))
3452 ChangeState(loaded);
3453 targetProcessId = 0;
3455 else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3458 else if(!strcmp(item.value, "You can't do that without a process to debug."))
3460 ChangeState(loaded);
3461 targetProcessId = 0;
3463 else if(strstr(item.value, "No such file or directory."))
3465 ChangeState(loaded);
3466 targetProcessId = 0;
3468 else if(strstr(item.value, "During startup program exited with code "))
3470 ChangeState(loaded);
3471 targetProcessId = 0;
3476 if(strlen(item.value) < MAX_F_STRING)
3479 ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3483 ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3489 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "error content (", item.name, "=", item.value, ") is unheard of");
3492 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "result-record: ", outTokens[0]);
3493 outTokens.RemoveAll();
3496 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "status-async-output: ", outTokens[0]);
3499 if(TokenizeList(output, ',', outTokens))
3501 if(!strcmp(outTokens[0], "=library-loaded"))
3502 FGODetectLoadedLibraryForAddedProjectIssues(outTokens);
3503 else if(!strcmp(outTokens[0], "=thread-group-created") || !strcmp(outTokens[0], "=thread-group-added") ||
3504 !strcmp(outTokens[0], "=thread-group-started") || !strcmp(outTokens[0], "=thread-group-exited") ||
3505 !strcmp(outTokens[0], "=thread-created") || !strcmp(outTokens[0], "=thread-exited") ||
3506 !strcmp(outTokens[0], "=cmd-param-changed") || !strcmp(outTokens[0], "=library-unloaded") ||
3507 !strcmp(outTokens[0], "=breakpoint-modified"))
3508 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, outTokens[0], outTokens.count>1 ? outTokens[1] : "",
3509 outTokens.count>2 ? outTokens[2] : "", outTokens.count>3 ? outTokens[3] : "",
3510 outTokens.count>4 ? outTokens[4] : "", outTokens.count>5 ? outTokens[5] : "",
3511 outTokens.count>6 ? outTokens[6] : "", outTokens.count>7 ? outTokens[7] : "",
3512 outTokens.count>8 ? outTokens[8] : "", outTokens.count>9 ? outTokens[9] : "");
3514 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "notify-async-output: ", outTokens[0]);
3516 outTokens.RemoveAll();
3520 if(TokenizeList(output, ',', outTokens))
3522 if(!strcmp(outTokens[0],"*running"))
3524 waitingForPID = true;
3525 setWaitingForPID = true;
3527 else if(!strcmp(outTokens[0], "*stopped"))
3530 ChangeState(stopped);
3532 for(tk = 1; tk < outTokens.count; tk++)
3534 if(TokenizeListItem(outTokens[tk], item))
3536 if(!strcmp(item.name, "reason"))
3538 char * reason = item.value;
3539 StripQuotes(reason, reason);
3540 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3543 if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3546 StripQuotes(item2.value, item2.value);
3547 if(!strcmp(item2.name, "exit-code"))
3548 exitCode = item2.value;
3554 HandleExit(reason, exitCode);
3557 else if(!strcmp(reason, "breakpoint-hit"))
3563 stopItem = GdbDataStop { };
3565 for(i = tk+1; i < outTokens.count; i++)
3567 TokenizeListItem(outTokens[i], item);
3568 StripQuotes(item.value, item.value);
3569 if(!strcmp(item.name, "bkptno"))
3570 stopItem.bkptno = atoi(item.value);
3571 else if(!strcmp(item.name, "thread-id"))
3572 stopItem.threadid = atoi(item.value);
3573 else if(!strcmp(item.name, "frame"))
3575 item.value = StripCurlies(item.value);
3576 ParseFrame(stopItem.frame, item.value);
3578 else if(!strcmp(item.name, "disp") || !strcmp(item.name, "stopped-threads") || !strcmp(item.name, "core"))
3579 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3581 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown breakpoint hit item name (", item.name, "=", item.value, ")");
3586 else if(!strcmp(reason, "end-stepping-range"))
3592 stopItem = GdbDataStop { };
3594 for(i = tk+1; i < outTokens.count; i++)
3596 TokenizeListItem(outTokens[i], item);
3597 StripQuotes(item.value, item.value);
3598 if(!strcmp(item.name, "thread-id"))
3599 stopItem.threadid = atoi(item.value);
3600 else if(!strcmp(item.name, "frame"))
3602 item.value = StripCurlies(item.value);
3603 ParseFrame(stopItem.frame, item.value);
3605 else if(!strcmp(item.name, "reason") || !strcmp(item.name, "bkptno"))
3606 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3608 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown end of stepping range item name (", item.name, "=", item.value, ")");
3614 else if(!strcmp(reason, "function-finished"))
3620 stopItem = GdbDataStop { };
3621 stopItem.reason = CopyString(reason);
3623 for(i = tk+1; i < outTokens.count; i++)
3625 TokenizeListItem(outTokens[i], item);
3626 StripQuotes(item.value, item.value);
3627 if(!strcmp(item.name, "thread-id"))
3628 stopItem.threadid = atoi(item.value);
3629 else if(!strcmp(item.name, "frame"))
3631 item.value = StripCurlies(item.value);
3632 ParseFrame(stopItem.frame, item.value);
3634 else if(!strcmp(item.name, "gdb-result-var"))
3635 stopItem.gdbResultVar = CopyString(item.value);
3636 else if(!strcmp(item.name, "return-value"))
3637 stopItem.returnValue = CopyString(item.value);
3639 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown function finished item name (", item.name, "=", item.value, ")");
3642 event = functionEnd;
3645 else if(!strcmp(reason, "signal-received"))
3651 stopItem = GdbDataStop { };
3652 stopItem.reason = CopyString(reason);
3654 for(i = tk+1; i < outTokens.count; i++)
3656 TokenizeListItem(outTokens[i], item);
3657 StripQuotes(item.value, item.value);
3658 if(!strcmp(item.name, "signal-name"))
3659 stopItem.name = CopyString(item.value);
3660 else if(!strcmp(item.name, "signal-meaning"))
3661 stopItem.meaning = CopyString(item.value);
3662 else if(!strcmp(item.name, "thread-id"))
3663 stopItem.threadid = atoi(item.value);
3664 else if(!strcmp(item.name, "frame"))
3666 item.value = StripCurlies(item.value);
3667 ParseFrame(stopItem.frame, item.value);
3670 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown signal reveived item name (", item.name, "=", item.value, ")");
3672 if(!strcmp(stopItem.name, "SIGTRAP"))
3691 else if(!strcmp(reason, "watchpoint-trigger"))
3692 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint trigger not handled");
3693 else if(!strcmp(reason, "read-watchpoint-trigger"))
3694 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason read watchpoint trigger not handled");
3695 else if(!strcmp(reason, "access-watchpoint-trigger"))
3696 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason access watchpoint trigger not handled");
3697 else if(!strcmp(reason, "watchpoint-scope"))
3698 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint scope not handled");
3699 else if(!strcmp(reason, "location-reached"))
3700 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason location reached not handled");
3702 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown reason: ", reason);
3710 if(usingValgrind && event == none && !stopItem)
3711 event = valgrindStartPause;
3716 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown exec-async-output: ", outTokens[0]);
3717 outTokens.RemoveAll();
3720 if(!strcmpi(output, "(gdb) "))
3724 char exeFile[MAX_LOCATION];
3725 int oldProcessID = targetProcessId;
3726 GetLastDirectory(targetFile, exeFile);
3728 while(!targetProcessId/*true*/)
3730 targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3731 if(targetProcessId || gdbHandle.Peek()) break;
3736 ChangeState(running);
3737 else if(!oldProcessID)
3739 ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3740 // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3741 gdbHandle.Printf("-gdb-exit\n");
3743 ChangeState(terminated); //loaded;
3748 for(bp : ide.workspace.breakpoints)
3749 bp.inserted = false;
3752 bp.inserted = false;
3754 bpRunToCursor.inserted = false;
3756 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3757 ClearBreakDisplay();
3759 #if defined(__unix__)
3760 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
3762 progThread.terminate = true;
3765 fifoFile.CloseInput();
3772 DeleteFile(progFifoPath);
3773 progFifoPath[0] = '\0';
3780 serialSemaphore.Release();
3783 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown prompt", output);
3787 if(!strncmp(output, "&\"warning:", 10))
3790 content = strstr(output, "\"");
3791 StripQuotes(content, content);
3792 content = strstr(content, ":");
3798 ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3805 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown output: ", output);
3807 if(!setWaitingForPID)
3808 waitingForPID = false;
3809 setWaitingForPID = false;
3817 // From GDB Output functions
3818 void FGODetectLoadedLibraryForAddedProjectIssues(Array<char *> outTokens)
3820 char path[MAX_LOCATION] = "";
3821 char file[MAX_FILENAME] = "";
3823 DebugListItem item { };
3824 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGODetectLoadedLibraryForAddedProjectIssues()");
3825 for(token : outTokens)
3827 if(TokenizeListItem(token, item))
3829 if(!strcmp(item.name, "target-name"))
3831 StripQuotes(item.value, path);
3832 MakeSystemPath(path);
3833 GetLastDirectory(path, file);
3835 else if(!strcmp(item.name, "symbols-loaded"))
3837 symbolsLoaded = (atoi(item.value) == 1);
3842 if(path[0] && file[0])
3844 for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
3848 char prjTargetPath[MAX_LOCATION];
3849 char prjTargetFile[MAX_FILENAME];
3850 DirExpression targetDirExp = prj.GetTargetDir(currentCompiler, prj.config, bitDepth);
3851 strcpy(prjTargetPath, prj.topNode.path);
3852 PathCat(prjTargetPath, targetDirExp.dir);
3853 prjTargetFile[0] = '\0';
3854 prj.CatTargetFileName(prjTargetFile, currentCompiler, prj.config);
3855 PathCat(prjTargetPath, prjTargetFile);
3856 MakeSystemPath(prjTargetPath);
3858 match = !fstrcmp(prjTargetFile, file);
3859 if(!match && (dot = strstr(prjTargetFile, ".so.")))
3861 char * dot3 = strstr(dot+4, ".");
3865 match = !fstrcmp(prjTargetFile, file);
3870 match = !fstrcmp(prjTargetFile, file);
3875 // TODO: nice visual feedback to better warn user. use some ide notification system or other means.
3876 /* -- 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)
3878 ide.outputView.debugBox.Logf($"Attention! No symbols for loaded library %s matched to the %s added project.\n", path, prj.topNode.name);
3880 match = !fstrcmp(prjTargetPath, path);
3881 if(!match && (dot = strstr(prjTargetPath, ".so.")))
3883 char * dot3 = strstr(dot+4, ".");
3887 match = !fstrcmp(prjTargetPath, path);
3892 match = !fstrcmp(prjTargetPath, path);
3896 ide.outputView.debugBox.Logf($"Loaded library %s doesn't match the %s target of the %s added project.\n", path, prjTargetPath, prj.topNode.name);
3903 void FGOBreakpointModified(Array<char *> outTokens)
3905 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGOBreakpointModified() -- TODO only if needed: support breakpoint modified");
3907 DebugListItem item { };
3908 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3910 if(!strcmp(item.name, "bkpt"))
3912 GdbDataBreakpoint modBp = ParseBreakpoint(item.value, outTokens);
3920 ExpressionType ::DebugEvalExpTypeError(char * result)
3922 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::DebugEvalExpTypeError()");
3927 case symbolNotFound:
3928 return symbolErrorExp;
3929 case memoryCantBeRead:
3930 return memoryErrorExp;
3932 return unknownErrorExp;
3935 char * ::EvaluateExpression(char * expression, ExpressionType * error)
3938 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateExpression(", expression, ")");
3939 if(ide.projectView && ide.debugger.state == stopped)
3941 result = GdbEvaluateExpression(expression);
3942 *error = DebugEvalExpTypeError(result);
3947 *error = noDebuggerErrorExp;
3952 char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
3955 char * result = GdbReadMemoryString(address, size, format, 1, 1);
3956 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ReadMemory(", address, ")");
3957 if(!result || !strcmp(result, "N/A"))
3958 *error = memoryErrorExp;
3960 *error = DebugEvalExpTypeError(result);
3965 class ValgrindLogThread : Thread
3971 static char output[4096];
3972 Array<char> dynamicBuffer { minAllocSize = 4096 };
3973 File oldValgrindHandle = vgLogFile;
3974 incref oldValgrindHandle;
3977 while(debugger.state != terminated && vgLogFile)
3981 result = vgLogFile.Read(output, 1, sizeof(output));
3983 if(debugger.state == terminated || !vgLogFile/* || vgLogFile.Eof()*/)
3990 for(c = 0; c<result; c++)
3992 if(output[c] == '\n')
3994 int pos = dynamicBuffer.size;
3995 dynamicBuffer.size += c - start;
3996 memcpy(&dynamicBuffer[pos], output + start, c - start);
3997 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3998 // COMMENTED OUT DUE TO ISSUE #135, FIXED
3999 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4000 dynamicBuffer.size++;
4001 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4003 // printf("%s\n", dynamicBuffer.array);
4005 if(strstr(&dynamicBuffer[0], "vgdb me"))
4006 debugger.serialSemaphore.Release();
4007 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4008 dynamicBuffer.size = 0;
4014 int pos = dynamicBuffer.size;
4015 dynamicBuffer.size += c - start;
4016 memcpy(&dynamicBuffer[pos], output + start, c - start);
4019 else if(debugger.state == stopped)
4022 printf("Got end of file from GDB!\n");
4029 delete dynamicBuffer;
4030 ide.outputView.debugBox.Logf($"ValgrindLogThreadExit\n");
4031 //if(oldValgrindHandle == vgLogFile)
4032 debugger.GdbThreadExit/*ValgrindLogThreadExit*/();
4033 delete oldValgrindHandle;
4039 class ValgrindTargetThread : Thread
4045 static char output[4096];
4046 Array<char> dynamicBuffer { minAllocSize = 4096 };
4047 DualPipe oldValgrindHandle = vgTargetHandle;
4048 incref oldValgrindHandle;
4051 while(debugger.state != terminated && vgTargetHandle && !vgTargetHandle.Eof())
4055 result = vgTargetHandle.Read(output, 1, sizeof(output));
4057 if(debugger.state == terminated || !vgTargetHandle || vgTargetHandle.Eof())
4064 for(c = 0; c<result; c++)
4066 if(output[c] == '\n')
4068 int pos = dynamicBuffer.size;
4069 dynamicBuffer.size += c - start;
4070 memcpy(&dynamicBuffer[pos], output + start, c - start);
4071 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4072 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4073 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4074 dynamicBuffer.size++;
4075 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4077 // printf("%s\n", dynamicBuffer.array);
4079 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4081 dynamicBuffer.size = 0;
4087 int pos = dynamicBuffer.size;
4088 dynamicBuffer.size += c - start;
4089 memcpy(&dynamicBuffer[pos], output + start, c - start);
4095 printf("Got end of file from GDB!\n");
4099 delete dynamicBuffer;
4100 //if(oldValgrindHandle == vgTargetHandle)
4101 debugger.ValgrindTargetThreadExit();
4102 delete oldValgrindHandle;
4108 class GdbThread : Thread
4114 static char output[4096];
4115 Array<char> dynamicBuffer { minAllocSize = 4096 };
4116 DualPipe oldGdbHandle = gdbHandle;
4117 incref oldGdbHandle;
4120 while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
4124 result = gdbHandle.Read(output, 1, sizeof(output));
4126 if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
4133 for(c = 0; c<result; c++)
4135 if(output[c] == '\n')
4137 int pos = dynamicBuffer.size;
4138 dynamicBuffer.size += c - start;
4139 memcpy(&dynamicBuffer[pos], output + start, c - start);
4140 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4141 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4142 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4143 dynamicBuffer.size++;
4144 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4146 // _dpl(0, dynamicBuffer.array);
4148 debugger.GdbThreadMain(&dynamicBuffer[0]);
4149 dynamicBuffer.size = 0;
4155 int pos = dynamicBuffer.size;
4156 dynamicBuffer.size += c - start;
4157 memcpy(&dynamicBuffer[pos], output + start, c - start);
4163 _dpl(0, "Got end of file from GDB!");
4167 delete dynamicBuffer;
4168 //if(oldGdbHandle == gdbHandle)
4169 debugger.GdbThreadExit();
4170 delete oldGdbHandle;
4176 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
4177 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
4179 #if defined(__unix__)
4184 #include <sys/types.h>
4189 class ProgramThread : Thread
4195 bool fileCreated = false;
4197 static char output[1000];
4200 /*if(!mkfifo(progFifoPath, mask))
4207 ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
4211 if(FileExists(progFifoPath)) //fileCreated)
4213 fifoFile = FileOpen(progFifoPath, read);
4217 ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
4222 fd = fileno((FILE *)fifoFile.input);
4223 //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
4227 while(!terminate && fifoFile && !fifoFile.Eof())
4230 struct timeval time;
4238 selectResult = select(fd + 1, &rs, null, null, &time);
4239 if(FD_ISSET(fd, &rs))
4241 int result = (int)read(fd, output, sizeof(output)-1);
4242 if(!result || (result < 0 && errno != EAGAIN))
4246 output[result] = '\0';
4247 if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
4250 ide.outputView.debugBox.Log(output);
4259 //fifoFile.CloseInput();
4262 ide.outputView.debugBox.Log("\n");
4266 if(FileExists(progFifoPath)) //fileCreated)
4268 DeleteFile(progFifoPath);
4269 progFifoPath[0] = '\0';
4277 class Argument : struct
4279 Argument prev, next;
4281 property char * name { set { delete name; if(value) name = CopyString(value); } }
4283 property char * val { set { delete val; if(value) val = CopyString(value); } }
4297 class Frame : struct
4302 property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4304 property char * func { set { delete func; if(value) func = CopyString(value); } }
4308 property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
4310 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4311 char * absoluteFile;
4312 property char * absoluteFile { set { delete absoluteFile; if(value) absoluteFile = CopyUnescapedUnixPath(value); } }
4321 delete absoluteFile;
4322 args.Free(Argument::Free);
4331 class GdbDataStop : struct
4348 char * gdbResultVar;
4358 if(!strcmp(reason, "signal-received"))
4363 else if(!strcmp(reason, "function-finished"))
4365 delete gdbResultVar;
4370 if(frame) frame.Free();
4379 class GdbDataBreakpoint : struct
4383 property char * number { set { delete number; if(value) number = CopyString(value); } }
4385 property char * type { set { delete type; if(value) type = CopyString(value); } }
4387 property char * disp { set { delete disp; if(value) disp = CopyString(value); } }
4390 property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4392 property char * func { set { delete func; if(value) func = CopyString(value); } }
4394 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4396 property char * fullname { set { delete fullname; if(value) fullname = CopyUnescapedUnixPath(value); } }
4399 property char * at { set { delete at; if(value) at = CopyString(value); } }
4402 Array<GdbDataBreakpoint> multipleBPs;
4407 PrintLn("{", "#", number, " T", type, " D", disp, " E", enabled, " H", times, " (", func, ") (", file, ":", line, ") (", fullname, ") (", addr, ") (", at, ")", "}");
4418 if(multipleBPs) multipleBPs.Free();
4422 ~GdbDataBreakpoint()
4428 class Breakpoint : struct
4433 property char * function { set { delete function; if(value) function = CopyString(value); } }
4434 char * relativeFilePath;
4435 property char * relativeFilePath { set { delete relativeFilePath; if(value) relativeFilePath = CopyString(value); } }
4436 char * absoluteFilePath;
4437 property char * absoluteFilePath { set { delete absoluteFilePath; if(value) absoluteFilePath = CopyString(value); } }
4446 BreakpointType type;
4448 GdbDataBreakpoint bp;
4450 char * CopyLocationString(bool removePath)
4453 char * file = relativeFilePath ? relativeFilePath : absoluteFilePath;
4454 bool removingPath = removePath && file;
4457 char * fileName = new char[MAX_FILENAME];
4458 GetLastDirectory(file, fileName);
4464 location = PrintString(file, ":", function);
4466 location = CopyString(function);
4469 location = PrintString(file, ":", line);
4475 char * CopyUserLocationString()
4478 char * loc = CopyLocationString(false);
4480 for(p : ide.workspace.projects)
4482 if(p.topNode.FindByFullPath(absoluteFilePath, false))
4490 location = PrintString("(", prj.name, ")", loc);
4500 if(relativeFilePath && relativeFilePath[0])
4502 f.Printf(" * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
4504 f.Printf(" ~ %s\n", condition.expression);
4514 delete relativeFilePath;
4515 delete absoluteFilePath;
4525 class Watch : struct
4536 f.Printf(" ~ %s\n", expression);
4560 class DebugListItem : struct
4566 struct DebugEvaluationData
4571 uint64 nextBlockAddress;
4573 DebuggerEvaluationError error;
4576 class CodeLocation : struct
4579 char * absoluteFile;
4582 CodeLocation ::ParseCodeLocation(char * location)
4586 char * colon = null;
4588 char loc[MAX_LOCATION];
4589 strcpy(loc, location);
4590 for(temp = loc; temp = strstr(temp, ":"); temp++)
4598 int line = atoi(colon);
4601 CodeLocation codloc { line = line };
4602 codloc.file = CopyString(loc);
4603 codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
4615 delete absoluteFile;
4624 void GDBFallBack(Expression exp, String expString)
4627 ExpressionType evalError = dummyExp;
4628 result = Debugger::EvaluateExpression(expString, &evalError);
4631 exp.constant = result;
4632 exp.type = constantExp;