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 bool vgFullLeakCheck = ide.workspace.vgFullLeakCheck;
2136 Project project = ide.project;
2137 DirExpression targetDirExp = project.GetTargetDir(compiler, config, bitDepth);
2138 PathBackup pathBackup { };
2140 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbInit()");
2141 if(currentCompiler != compiler)
2143 delete currentCompiler;
2144 currentCompiler = compiler;
2145 incref currentCompiler;
2148 this.bitDepth = bitDepth;
2149 usingValgrind = useValgrind;
2151 ChangeState(loaded);
2153 sentBreakInsert = false;
2154 breakpointError = false;
2155 ignoreBreakpoints = false;
2161 ide.outputView.ShowClearSelectTab(debug);
2162 ide.outputView.debugBox.Logf($"Starting debug mode\n");
2164 #ifdef GDB_DEBUG_OUTPUT
2165 ide.outputView.gdbBox.Logf("run: Starting GDB\n");
2168 strcpy(tempPath, ide.workspace.projectDir);
2169 PathCatSlash(tempPath, targetDirExp.dir);
2171 targetDir = CopyString(tempPath);
2172 project.CatTargetFileName(tempPath, compiler, config);
2174 targetFile = CopyString(tempPath);
2176 GetWorkingDir(oldDirectory, MAX_LOCATION);
2177 if(ide.workspace.debugDir && ide.workspace.debugDir[0])
2179 char temp[MAX_LOCATION];
2180 strcpy(temp, ide.workspace.projectDir);
2181 PathCatSlash(temp, ide.workspace.debugDir);
2182 ChangeWorkingDir(temp);
2185 ChangeWorkingDir(ide.workspace.projectDir);
2187 ide.SetPath(true, compiler, config, bitDepth);
2189 // TODO: This pollutes the environment, but at least it works
2190 // It shouldn't really affect the IDE as the PATH gets restored and other variables set for testing will unlikely cause problems
2191 // What is the proper solution for this? DualPipeOpenEnv?
2192 // gdb set environment commands don't seem to take effect
2193 for(e : ide.workspace.environmentVars)
2195 SetEnvironment(e.name, e.string);
2200 char * clArgs = ide.workspace.commandLineArgs;
2201 const char *valgrindCommand = "valgrind"; // TODO: valgrind command config option //TODO: valgrind options
2202 vgLogFile = CreateTemporaryFile(vgLogPath, "ecereidevglog");
2206 vgLogThread.Create();
2210 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't open temporary log file for Valgrind output\n");
2213 if(result && !CheckCommandAvailable(valgrindCommand))
2215 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for Valgrind is not available.\n", valgrindCommand);
2220 sprintf(command, "%s --vgdb=yes --vgdb-error=0 --log-file=%s%s %s%s%s",
2221 valgrindCommand, vgLogPath, vgFullLeakCheck ? " --leak-check=full" : "", targetFile, clArgs ? " " : "", clArgs ? clArgs : "");
2222 vgTargetHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
2225 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start Valgrind\n");
2231 incref vgTargetHandle;
2232 vgTargetThread.Create();
2234 targetProcessId = vgTargetHandle.GetProcessID();
2235 waitingForPID = false;
2236 if(!targetProcessId)
2238 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get Valgrind process ID\n");
2245 serialSemaphore.Wait();
2253 (compiler.targetPlatform == win32 && bitDepth == 64) ? "x86_64-w64-mingw32-gdb" :
2254 (compiler.targetPlatform == win32 && bitDepth == 32) ? "i686-w64-mingw32-gdb" :
2256 if(!CheckCommandAvailable(command))
2258 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for GDB is not available.\n", command);
2263 strcat(command, " -n -silent --interpreter=mi2"); //-async //\"%s\"
2265 gdbHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
2268 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start GDB\n");
2278 gdbProcessId = gdbHandle.GetProcessID();
2281 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get GDB process ID\n");
2288 serialSemaphore.Wait();
2291 GdbCommand(false, "-gdb-set verbose off");
2292 //GdbCommand(false, "-gdb-set exec-done-display on");
2293 GdbCommand(false, "-gdb-set step-mode off");
2294 GdbCommand(false, "-gdb-set unwindonsignal on");
2295 //GdbCommand(false, "-gdb-set shell on");
2296 GdbCommand(false, "set print elements 992");
2297 GdbCommand(false, "-gdb-set backtrace limit 100000");
2301 //ChangeState(terminated);
2307 #if defined(__unix__)
2309 CreateTemporaryDir(progFifoDir, "ecereide");
2310 strcpy(progFifoPath, progFifoDir);
2311 PathCat(progFifoPath, "ideprogfifo");
2312 if(!mkfifo(progFifoPath, 0600))
2314 //fileCreated = true;
2319 ide.outputView.debugBox.Logf(createFIFOMsg, progFifoPath);
2326 progThread.terminate = false;
2327 progThread.Create();
2331 #if defined(__WIN32__)
2332 GdbCommand(false, "-gdb-set new-console on");
2335 #if defined(__unix__)
2337 GdbCommand(false, "-inferior-tty-set %s", progFifoPath);
2341 GdbCommand(false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
2343 for(e : ide.workspace.environmentVars)
2345 GdbCommand(false, "set environment %s=%s", e.name, e.string);
2350 ChangeWorkingDir(oldDirectory);
2356 delete targetDirExp;
2362 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExit()");
2363 if(gdbHandle && gdbProcessId)
2365 GdbCommand(false, "-gdb-exit");
2380 ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
2386 for(bp : ide.workspace.breakpoints)
2388 bp.inserted = false;
2394 bp.inserted = false;
2399 bpRunToCursor.inserted = false;
2400 delete bpRunToCursor.bp;
2403 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2404 ClearBreakDisplay();
2407 #if defined(__unix__)
2408 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
2410 progThread.terminate = true;
2413 fifoFile.CloseInput();
2419 DeleteFile(progFifoPath);
2420 progFifoPath[0] = '\0';
2426 void WatchesCodeEditorLinkInit()
2428 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkInit()");
2430 char tempPath[MAX_LOCATION];
2431 char path[MAX_LOCATION];
2433 //void MakeFilePathProjectRelative(char * path, char * relativePath)
2434 if(!ide.projectView.project.GetRelativePath(activeFrame.file, tempPath))
2435 strcpy(tempPath, activeFrame.file);
2437 strcpy(path, ide.workspace.projectDir);
2438 PathCat(path, tempPath);
2439 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2442 for(srcDir : ide.workspace.sourceDirs)
2444 strcpy(path, srcDir);
2445 PathCat(path, tempPath);
2446 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2447 if(codeEditor) break;
2452 /*if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2453 activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);*/
2454 if(!activeFrame || !activeFrame.absoluteFile)
2457 codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal, false);
2460 codeEditor.inUseDebug = true;
2463 //watchesInit = true;
2466 void WatchesCodeEditorLinkRelease()
2468 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkRelease()");
2473 codeEditor.inUseDebug = false;
2474 if(!codeEditor.visible)
2475 codeEditor.Destroy(0);
2481 bool ResolveWatch(Watch wh)
2483 bool result = false;
2485 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ResolveWatch()");
2497 char watchmsg[MAX_F_STRING];
2498 if(state == stopped && !codeEditor)
2499 wh.value = CopyString($"No source file found for selected frame");
2500 //if(codeEditor && state == stopped || state != stopped)
2503 Module backupPrivateModule;
2504 Context backupContext;
2505 Class backupThisClass;
2509 backupPrivateModule = GetPrivateModule();
2510 backupContext = GetCurrentContext();
2511 backupThisClass = GetThisClass();
2514 SetPrivateModule(codeEditor.privateModule);
2515 SetCurrentContext(codeEditor.globalContext);
2516 SetTopContext(codeEditor.globalContext);
2517 SetGlobalContext(codeEditor.globalContext);
2518 SetGlobalData(&codeEditor.globalData);
2521 exp = ParseExpressionString(wh.expression);
2523 if(exp && !parseError)
2525 char expString[4096];
2527 PrintExpression(exp, expString);
2529 if(GetPrivateModule())
2532 DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2533 ProcessExpressionType(exp);
2535 wh.type = exp.expType;
2538 DebugComputeExpression(exp);
2539 if(ExpressionIsError(exp))
2541 GDBFallBack(exp, expString);
2544 /*if(exp.hasAddress)
2546 char temp[MAX_F_STRING];
2547 sprintf(temp, "0x%x", exp.address);
2548 wh.address = CopyString(temp);
2549 // wh.address = CopyStringf("0x%x", exp.address);
2554 Type dataType = exp.expType;
2557 char temp[MAX_F_STRING];
2558 switch(dataType.kind)
2561 sprintf(temp, "%i", exp.val.c);
2564 sprintf(temp, "%i", exp.val.s);
2569 sprintf(temp, "%i", exp.val.i);
2572 sprintf(temp, "%i", exp.val.i64);
2575 sprintf(temp, "%i", exp.val.p);
2580 long v = (long)exp.val.f;
2581 sprintf(temp, "%i", v);
2586 long v = (long)exp.val.d;
2587 sprintf(temp, "%i", v);
2592 wh.intVal = CopyString(temp);
2593 switch(dataType.kind)
2596 sprintf(temp, "0x%x", exp.val.c);
2599 sprintf(temp, "0x%x", exp.val.s);
2603 sprintf(temp, "0x%x", exp.val.i);
2606 sprintf(temp, "0x%x", exp.val.i64);
2609 sprintf(temp, "0x%x", exp.val.i64);
2612 sprintf(temp, "0x%x", exp.val.p);
2617 long v = (long)exp.val.f;
2618 sprintf(temp, "0x%x", v);
2623 long v = (long)exp.val.d;
2624 sprintf(temp, "0x%x", v);
2629 wh.hexVal = CopyString(temp);
2630 switch(dataType.kind)
2633 sprintf(temp, "0o%o", exp.val.c);
2636 sprintf(temp, "0o%o", exp.val.s);
2640 sprintf(temp, "0o%o", exp.val.i);
2643 sprintf(temp, "0o%o", exp.val.i64);
2646 sprintf(temp, "0o%o", exp.val.i64);
2649 sprintf(temp, "0o%o", exp.val.p);
2654 long v = (long)exp.val.f;
2655 sprintf(temp, "0o%o", v);
2660 long v = (long)exp.val.d;
2661 sprintf(temp, "0o%o", v);
2666 wh.octVal = CopyString(temp);
2669 // WHATS THIS HERE ?
2670 if(exp.type == constantExp && exp.constant)
2671 wh.constant = CopyString(exp.constant);
2677 case symbolErrorExp:
2678 snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2680 case structMemberSymbolErrorExp:
2681 // todo get info as in next case (ExpClassMemberSymbolError)
2682 snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2684 case classMemberSymbolErrorExp:
2687 Expression memberExp = exp.member.exp;
2688 Identifier memberID = exp.member.member;
2689 Type type = memberExp.expType;
2692 _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2695 char string[256] = "";
2697 PrintTypeNoConst(type, string, false, true);
2698 classSym = FindClass(string);
2699 _class = classSym ? classSym.registered : null;
2702 snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2704 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2707 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2710 case memoryErrorExp:
2711 // Need to ensure when set to memoryErrorExp, constant is set
2712 snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2714 case dereferenceErrorExp:
2715 snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2717 case unknownErrorExp:
2718 snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2720 case noDebuggerErrorExp:
2721 snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2723 case debugStateErrorExp:
2724 snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2727 snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2731 // Temporary Code for displaying Strings
2732 if((exp.expType && ((exp.expType.kind == pointerType ||
2733 exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) ||
2734 (wh.type && wh.type.kind == classType && wh.type._class &&
2735 wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2736 !strcmp(wh.type._class.registered.name, "String")))
2739 if(exp.expType.kind != arrayType || exp.hasAddress)
2745 //char temp[MAX_F_STRING * 32];
2747 ExpressionType evalError = dummyExp;
2748 /*if(exp.expType.kind == arrayType)
2749 sprintf(temp, "(char*)0x%x", exp.address);
2751 sprintf(temp, "(char*)%s", exp.constant);*/
2753 //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2754 // address = strtoul(exp.constant, null, 0);
2755 address = _strtoui64(exp.constant, null, 0);
2756 //_dpl(0, "0x", address);
2757 // snprintf(value, sizeof(value), "0x%08x ", address);
2759 if(address > 0xFFFFFFFFLL)
2760 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2762 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2763 value[sizeof(value)-1] = 0;
2766 strcat(value, $"Null string");
2770 len = strlen(value);
2772 while(!string && size > 2)
2774 string = GdbReadMemory(address, size);
2777 if(string && string[0])
2780 if(UTF8Validate(string))
2785 for(c = 0; (ch = string[c]) && c<4096; c++)
2788 value[len++] = '\0';
2793 ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2794 strcat(value, ") (ISO8859-1)");
2801 strcat(value, $"Empty string");
2805 strcat(value, $"Couldn't read memory");
2807 wh.value = CopyString(value);
2810 else if(wh.type && wh.type.kind == classType && wh.type._class &&
2811 wh.type._class.registered && wh.type._class.registered.type == enumClass)
2813 uint64 value = strtoul(exp.constant, null, 0);
2814 Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2815 EnumClassData enumeration = (EnumClassData)enumClass.data;
2817 for(item = enumeration.values.first; item; item = item.next)
2818 if((int)item.data == value)
2821 wh.value = CopyString(item.name);
2823 wh.value = CopyString($"Invalid Enum Value");
2826 else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class &&
2827 wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2834 if(exp.constant[0] == '\'')
2836 if((int)((byte *)exp.constant)[1] > 127)
2839 value = UTF8GetChar(exp.constant + 1, &nb);
2840 if(nb < 2) value = exp.constant[1];
2841 signedValue = value;
2845 signedValue = exp.constant[1];
2847 // Precomp Syntax error with boot strap here:
2848 byte b = (byte)(char)signedValue;
2849 value = (unichar) b;
2855 if(wh.type.kind == charType && wh.type.isSigned)
2857 signedValue = (int)(char)strtol(exp.constant, null, 0);
2859 // Precomp Syntax error with boot strap here:
2860 byte b = (byte)(char)signedValue;
2861 value = (unichar) b;
2866 value = (uint)strtoul(exp.constant, null, 0);
2867 signedValue = (int)value;
2871 UTF32toUTF8Len(&value, 1, charString, 5);
2873 snprintf(string, sizeof(string), "\'\\0' (0)");
2874 else if(value == '\t')
2875 snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2876 else if(value == '\n')
2877 snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2878 else if(value == '\r')
2879 snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2880 else if(wh.type.kind == charType && wh.type.isSigned)
2881 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2882 else if(value > 256 || wh.type.kind != charType)
2884 if(value > 0x10FFFF || !GetCharCategory(value))
2885 snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2887 snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2890 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2891 string[sizeof(string)-1] = 0;
2893 wh.value = CopyString(string);
2898 wh.value = CopyString(exp.constant);
2905 wh.value = PrintHexUInt64(exp.address);
2910 char tempString[256];
2911 if(exp.member.memberType == propertyMember)
2912 snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2914 snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression,
2915 exp.type.OnGetString(tempString, null, null));
2921 snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2922 if(exp) FreeExpression(exp);
2925 SetPrivateModule(backupPrivateModule);
2926 SetCurrentContext(backupContext);
2927 SetTopContext(backupContext);
2928 SetGlobalContext(backupContext);
2929 SetThisClass(backupThisClass);
2932 // wh.value = CopyString("No source file found for selected frame");
2934 watchmsg[sizeof(watchmsg)-1] = 0;
2936 wh.value = CopyString(watchmsg);
2938 ide.watchesView.UpdateWatch(wh);
2942 void EvaluateWatches()
2944 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateWatches()");
2945 for(wh : ide.workspace.watches)
2949 char * ::GdbEvaluateExpression(char * expression)
2951 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbEvaluateExpression(", expression, ")");
2954 GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2956 ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2960 // to be removed... use GdbReadMemory that returns a byte array instead
2961 char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
2963 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemoryString(", address, ")");
2968 _dpl(0, "GdbReadMemoryString called with size = 0!");
2970 // GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
2971 if(GetRuntimePlatform() == win32)
2972 GdbCommand(false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
2974 GdbCommand(false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
2976 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
2980 byte * ::GdbReadMemory(uint64 address, int bytes)
2982 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemory(", address, ")");
2985 //GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
2986 if(GetRuntimePlatform() == win32)
2987 GdbCommand(false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
2989 GdbCommand(false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
2992 _dpl(0, "GdbReadMemory called with bytes = 0!");
2995 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
2996 else if(eval.result && strcmp(eval.result, "N/A"))
2998 byte * result = new byte[bytes];
2999 byte * string = eval.result;
3003 result[c++] = (byte)strtol(string, &string, 10);
3019 void EventHit(GdbDataStop stopItem, Breakpoint bpInternal, Breakpoint bpUser)
3021 char * s1 = null; char * s2 = null;
3022 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EventHit(",
3023 "bpInternal(", bpInternal ? s1=bpInternal.CopyLocationString(false) : null, "), ",
3024 "bpUser(", bpUser ? s2=bpUser.CopyLocationString(false) : null, ")) -- ",
3025 "ignoreBreakpoints(", ignoreBreakpoints, "), ",
3026 "hitCursorBreakpoint(", bpUser && bpUser.type == runToCursor, ")");
3027 delete s1; delete s2;
3029 if(bpUser && stopItem.frame.line && bpUser.line != stopItem.frame.line)
3031 // updating user breakpoint on hit location difference
3032 // todo, print something?
3033 bpUser.line = stopItem.frame.line;
3034 ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3035 ide.workspace.Save();
3040 if(bpInternal.type == internalModulesLoaded)
3042 if(!bpUser && !userBreakOnInternalBreakpoint)
3043 GdbExecContinue(false);
3047 bool conditionMet = true;
3048 if(bpUser.condition)
3049 conditionMet = ResolveWatch(bpUser.condition);
3051 if(!ignoreBreakpoints && (bpUser.level == -1 || bpUser.level == frameCount-1) && conditionMet)
3058 GdbExecContinue(false);
3062 GdbExecContinue(false);
3063 ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3064 if(bpUser == bpRunToCursor)
3066 UnsetBreakpoint(bpUser);
3067 delete bpRunToCursor;
3071 if(!bpUser && !bpInternal)
3072 GdbExecContinue(false);
3075 void ValgrindTargetThreadExit()
3077 ide.outputView.debugBox.Logf($"ValgrindTargetThreadExit\n");
3080 vgTargetHandle.Wait();
3081 delete vgTargetHandle;
3083 HandleExit(null, null);
3086 void GdbThreadExit()
3088 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbThreadExit()");
3089 if(state != terminated)
3091 ChangeState(terminated);
3092 targetProcessId = 0;
3093 ClearBreakDisplay();
3099 serialSemaphore.Release();
3104 ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
3105 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3107 HideDebuggerViews();
3109 //ChangeState(terminated);
3113 void GdbThreadMain(char * output)
3116 Array<char *> outTokens { minAllocSize = 50 };
3117 Array<char *> subTokens { minAllocSize = 50 };
3118 DebugListItem item { };
3119 DebugListItem item2 { };
3120 bool setWaitingForPID = false;
3122 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
3123 #ifdef GDB_DEBUG_CONSOLE
3124 _dpl2(_dpct, dplchan::gdbOutput, 0, output);
3126 #ifdef GDB_DEBUG_OUTPUT
3128 int len = strlen(output);
3136 for(c = 0; c < len / 1024; c++)
3138 strncpy(tmp, start, 1024);
3139 ide.outputView.gdbBox.Logf("out: %s\n", tmp);
3142 ide.outputView.gdbBox.Logf("out: %s\n", start);
3146 ide.outputView.gdbBox.Logf("out: %s\n", output);
3150 #ifdef GDB_DEBUG_CONSOLE
3151 strcpy(lastGdbOutput, output);
3153 #ifdef GDB_DEBUG_GUI
3154 if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
3161 if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
3164 ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
3170 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
3172 //if(outTokens.count == 1)
3177 ChangeState(loaded);
3178 targetProcessId = 0;
3179 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3181 if(!strcmp(item.name, "reason"))
3183 char * reason = item.value;
3184 StripQuotes(reason, reason);
3185 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3188 if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
3190 StripQuotes(item2.value, item2.value);
3191 if(!strcmp(item2.name, "exit-code"))
3192 exitCode = item2.value;
3198 HandleExit(reason, exitCode);
3202 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "kill reply (", item.name, "=", item.value, ") is unheard of");
3205 HandleExit(null, null);
3208 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3210 if(!strcmp(item.name, "bkpt"))
3212 sentBreakInsert = false;
3217 bpItem = ParseBreakpoint(item.value, outTokens);
3218 //breakType = bpValidation;
3220 else if(!strcmp(item.name, "depth"))
3222 StripQuotes(item.value, item.value);
3223 frameCount = atoi(item.value);
3225 stackFrames.Free(Frame::Free);
3227 else if(!strcmp(item.name, "stack"))
3230 if(stackFrames.count)
3231 ide.callStackView.Logf("...\n");
3234 item.value = StripBrackets(item.value);
3235 TokenizeList(item.value, ',', subTokens);
3236 for(i = 0; i < subTokens.count; i++)
3238 if(TokenizeListItem(subTokens[i], item))
3240 if(!strcmp(item.name, "frame"))
3243 stackFrames.Add(frame);
3244 item.value = StripCurlies(item.value);
3245 ParseFrame(frame, item.value);
3246 if(frame.file && frame.from)
3247 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "unexpected frame file and from members present");
3251 if(activeFrameLevel == -1)
3253 if(ide.projectView.IsModuleInProject(frame.file));
3255 if(frame.level != 0)
3257 //stopItem.frame = frame;
3258 breakType = selectFrame;
3261 activeFrame = frame;
3262 activeFrameLevel = frame.level;
3265 ide.callStackView.Logf("%3d ", frame.level);
3266 if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
3267 ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
3268 else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
3269 ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
3270 else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
3271 ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
3272 else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
3273 ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
3275 ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
3280 ide.callStackView.Logf("%3d ", frame.level);
3285 ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
3289 ide.callStackView.Logf("%s\n", frame.func);
3291 ide.callStackView.Logf($"unknown source\n");
3295 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "stack content (", item.name, "=", item.value, ") is unheard of");
3298 if(activeFrameLevel == -1)
3300 activeFrameLevel = 0;
3301 activeFrame = stackFrames.first;
3303 ide.callStackView.Home();
3305 subTokens.RemoveAll();
3307 /*else if(!strcmp(item.name, "frame"))
3310 item.value = StripCurlies(item.value);
3311 ParseFrame(&frame, item.value);
3313 else if(!strcmp(item.name, "thread-ids"))
3315 ide.threadsView.Clear();
3316 item.value = StripCurlies(item.value);
3317 TokenizeList(item.value, ',', subTokens);
3318 for(i = subTokens.count - 1; ; i--)
3320 if(TokenizeListItem(subTokens[i], item))
3322 if(!strcmp(item.name, "thread-id"))
3325 StripQuotes(item.value, item.value);
3326 value = atoi(item.value);
3327 ide.threadsView.Logf("%3d \n", value);
3330 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "threads content (", item.name, "=", item.value, ") is unheard of");
3335 ide.threadsView.Home();
3337 subTokens.RemoveAll();
3338 //if(!strcmp(outTokens[2], "number-of-threads"))
3340 else if(!strcmp(item.name, "new-thread-id"))
3342 StripQuotes(item.value, item.value);
3343 activeThread = atoi(item.value);
3345 else if(!strcmp(item.name, "value"))
3347 StripQuotes(item.value, item.value);
3348 eval.result = CopyString(item.value);
3349 eval.active = false;
3351 else if(!strcmp(item.name, "addr"))
3353 for(i = 2; i < outTokens.count; i++)
3355 if(TokenizeListItem(outTokens[i], item))
3357 if(!strcmp(item.name, "total-bytes"))
3359 StripQuotes(item.value, item.value);
3360 eval.bytes = atoi(item.value);
3362 else if(!strcmp(item.name, "next-row"))
3364 StripQuotes(item.value, item.value);
3365 eval.nextBlockAddress = _strtoui64(item.value, null, 0);
3367 else if(!strcmp(item.name, "memory"))
3371 //StripQuotes(item.value, item.value);
3372 item.value = StripBrackets(item.value);
3373 // this should be treated as a list...
3374 item.value = StripCurlies(item.value);
3375 TokenizeList(item.value, ',', subTokens);
3376 for(j = 0; j < subTokens.count; j++)
3378 if(TokenizeListItem(subTokens[j], item))
3380 if(!strcmp(item.name, "data"))
3382 item.value = StripBrackets(item.value);
3383 StripQuotes2(item.value, item.value);
3384 eval.result = CopyString(item.value);
3385 eval.active = false;
3389 subTokens.RemoveAll();
3394 else if(!strcmp(item.name, "source-path") || !strcmp(item.name, "BreakpointTable"))
3395 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "command reply (", item.name, "=", item.value, ") is ignored");
3397 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "command reply (", item.name, "=", item.value, ") is unheard of");
3400 else if(!strcmp(outTokens[0], "^running"))
3402 waitingForPID = true;
3403 setWaitingForPID = true;
3405 else if(!strcmp(outTokens[0], "^exit"))
3407 ChangeState(terminated);
3408 // ide.outputView.debugBox.Logf("Exit\n");
3409 // ide.Update(null);
3411 serialSemaphore.Release();
3413 else if(!strcmp(outTokens[0], "^error"))
3417 sentBreakInsert = false;
3418 breakpointError = true;
3421 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3423 if(!strcmp(item.name, "msg"))
3425 StripQuotes(item.value, item.value);
3428 eval.active = false;
3430 if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3431 eval.error = symbolNotFound;
3432 else if(strstr(item.value, "Cannot access memory at address"))
3433 eval.error = memoryCantBeRead;
3435 eval.error = unknown;
3437 else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3440 else if(!strncmp(item.value, "Cannot access memory at address", 31))
3443 else if(!strcmp(item.value, "Cannot find bounds of current function"))
3445 ChangeState(stopped);
3446 gdbHandle.Printf("-exec-continue\n");
3448 else if(!strcmp(item.value, "ptrace: No such process."))
3450 ChangeState(loaded);
3451 targetProcessId = 0;
3453 else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3456 else if(!strcmp(item.value, "You can't do that without a process to debug."))
3458 ChangeState(loaded);
3459 targetProcessId = 0;
3461 else if(strstr(item.value, "No such file or directory."))
3463 ChangeState(loaded);
3464 targetProcessId = 0;
3466 else if(strstr(item.value, "During startup program exited with code "))
3468 ChangeState(loaded);
3469 targetProcessId = 0;
3474 if(strlen(item.value) < MAX_F_STRING)
3477 ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3481 ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3487 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "error content (", item.name, "=", item.value, ") is unheard of");
3490 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "result-record: ", outTokens[0]);
3491 outTokens.RemoveAll();
3494 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "status-async-output: ", outTokens[0]);
3497 if(TokenizeList(output, ',', outTokens))
3499 if(!strcmp(outTokens[0], "=library-loaded"))
3500 FGODetectLoadedLibraryForAddedProjectIssues(outTokens);
3501 else if(!strcmp(outTokens[0], "=thread-group-created") || !strcmp(outTokens[0], "=thread-group-added") ||
3502 !strcmp(outTokens[0], "=thread-group-started") || !strcmp(outTokens[0], "=thread-group-exited") ||
3503 !strcmp(outTokens[0], "=thread-created") || !strcmp(outTokens[0], "=thread-exited") ||
3504 !strcmp(outTokens[0], "=cmd-param-changed") || !strcmp(outTokens[0], "=library-unloaded") ||
3505 !strcmp(outTokens[0], "=breakpoint-modified"))
3506 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, outTokens[0], outTokens.count>1 ? outTokens[1] : "",
3507 outTokens.count>2 ? outTokens[2] : "", outTokens.count>3 ? outTokens[3] : "",
3508 outTokens.count>4 ? outTokens[4] : "", outTokens.count>5 ? outTokens[5] : "",
3509 outTokens.count>6 ? outTokens[6] : "", outTokens.count>7 ? outTokens[7] : "",
3510 outTokens.count>8 ? outTokens[8] : "", outTokens.count>9 ? outTokens[9] : "");
3512 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "notify-async-output: ", outTokens[0]);
3514 outTokens.RemoveAll();
3518 if(TokenizeList(output, ',', outTokens))
3520 if(!strcmp(outTokens[0],"*running"))
3522 waitingForPID = true;
3523 setWaitingForPID = true;
3525 else if(!strcmp(outTokens[0], "*stopped"))
3528 ChangeState(stopped);
3530 for(tk = 1; tk < outTokens.count; tk++)
3532 if(TokenizeListItem(outTokens[tk], item))
3534 if(!strcmp(item.name, "reason"))
3536 char * reason = item.value;
3537 StripQuotes(reason, reason);
3538 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3541 if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3544 StripQuotes(item2.value, item2.value);
3545 if(!strcmp(item2.name, "exit-code"))
3546 exitCode = item2.value;
3552 HandleExit(reason, exitCode);
3555 else if(!strcmp(reason, "breakpoint-hit"))
3561 stopItem = GdbDataStop { };
3563 for(i = tk+1; i < outTokens.count; i++)
3565 TokenizeListItem(outTokens[i], item);
3566 StripQuotes(item.value, item.value);
3567 if(!strcmp(item.name, "bkptno"))
3568 stopItem.bkptno = atoi(item.value);
3569 else if(!strcmp(item.name, "thread-id"))
3570 stopItem.threadid = atoi(item.value);
3571 else if(!strcmp(item.name, "frame"))
3573 item.value = StripCurlies(item.value);
3574 ParseFrame(stopItem.frame, item.value);
3576 else if(!strcmp(item.name, "disp") || !strcmp(item.name, "stopped-threads") || !strcmp(item.name, "core"))
3577 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3579 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown breakpoint hit item name (", item.name, "=", item.value, ")");
3584 else if(!strcmp(reason, "end-stepping-range"))
3590 stopItem = GdbDataStop { };
3592 for(i = tk+1; i < outTokens.count; i++)
3594 TokenizeListItem(outTokens[i], item);
3595 StripQuotes(item.value, item.value);
3596 if(!strcmp(item.name, "thread-id"))
3597 stopItem.threadid = atoi(item.value);
3598 else if(!strcmp(item.name, "frame"))
3600 item.value = StripCurlies(item.value);
3601 ParseFrame(stopItem.frame, item.value);
3603 else if(!strcmp(item.name, "reason") || !strcmp(item.name, "bkptno"))
3604 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3606 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown end of stepping range item name (", item.name, "=", item.value, ")");
3612 else if(!strcmp(reason, "function-finished"))
3618 stopItem = GdbDataStop { };
3619 stopItem.reason = CopyString(reason);
3621 for(i = tk+1; i < outTokens.count; i++)
3623 TokenizeListItem(outTokens[i], item);
3624 StripQuotes(item.value, item.value);
3625 if(!strcmp(item.name, "thread-id"))
3626 stopItem.threadid = atoi(item.value);
3627 else if(!strcmp(item.name, "frame"))
3629 item.value = StripCurlies(item.value);
3630 ParseFrame(stopItem.frame, item.value);
3632 else if(!strcmp(item.name, "gdb-result-var"))
3633 stopItem.gdbResultVar = CopyString(item.value);
3634 else if(!strcmp(item.name, "return-value"))
3635 stopItem.returnValue = CopyString(item.value);
3637 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown function finished item name (", item.name, "=", item.value, ")");
3640 event = functionEnd;
3643 else if(!strcmp(reason, "signal-received"))
3649 stopItem = GdbDataStop { };
3650 stopItem.reason = CopyString(reason);
3652 for(i = tk+1; i < outTokens.count; i++)
3654 TokenizeListItem(outTokens[i], item);
3655 StripQuotes(item.value, item.value);
3656 if(!strcmp(item.name, "signal-name"))
3657 stopItem.name = CopyString(item.value);
3658 else if(!strcmp(item.name, "signal-meaning"))
3659 stopItem.meaning = CopyString(item.value);
3660 else if(!strcmp(item.name, "thread-id"))
3661 stopItem.threadid = atoi(item.value);
3662 else if(!strcmp(item.name, "frame"))
3664 item.value = StripCurlies(item.value);
3665 ParseFrame(stopItem.frame, item.value);
3668 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown signal reveived item name (", item.name, "=", item.value, ")");
3670 if(!strcmp(stopItem.name, "SIGTRAP"))
3689 else if(!strcmp(reason, "watchpoint-trigger"))
3690 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint trigger not handled");
3691 else if(!strcmp(reason, "read-watchpoint-trigger"))
3692 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason read watchpoint trigger not handled");
3693 else if(!strcmp(reason, "access-watchpoint-trigger"))
3694 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason access watchpoint trigger not handled");
3695 else if(!strcmp(reason, "watchpoint-scope"))
3696 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint scope not handled");
3697 else if(!strcmp(reason, "location-reached"))
3698 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason location reached not handled");
3700 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown reason: ", reason);
3708 if(usingValgrind && event == none && !stopItem)
3709 event = valgrindStartPause;
3714 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown exec-async-output: ", outTokens[0]);
3715 outTokens.RemoveAll();
3718 if(!strcmpi(output, "(gdb) "))
3722 char exeFile[MAX_LOCATION];
3723 int oldProcessID = targetProcessId;
3724 GetLastDirectory(targetFile, exeFile);
3726 while(!targetProcessId/*true*/)
3728 targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3729 if(targetProcessId || gdbHandle.Peek()) break;
3734 ChangeState(running);
3735 else if(!oldProcessID)
3737 ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3738 // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3739 gdbHandle.Printf("-gdb-exit\n");
3741 ChangeState(terminated); //loaded;
3746 for(bp : ide.workspace.breakpoints)
3747 bp.inserted = false;
3750 bp.inserted = false;
3752 bpRunToCursor.inserted = false;
3754 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3755 ClearBreakDisplay();
3757 #if defined(__unix__)
3758 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
3760 progThread.terminate = true;
3763 fifoFile.CloseInput();
3770 DeleteFile(progFifoPath);
3771 progFifoPath[0] = '\0';
3778 serialSemaphore.Release();
3781 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown prompt", output);
3785 if(!strncmp(output, "&\"warning:", 10))
3788 content = strstr(output, "\"");
3789 StripQuotes(content, content);
3790 content = strstr(content, ":");
3796 ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3803 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown output: ", output);
3805 if(!setWaitingForPID)
3806 waitingForPID = false;
3807 setWaitingForPID = false;
3815 // From GDB Output functions
3816 void FGODetectLoadedLibraryForAddedProjectIssues(Array<char *> outTokens)
3818 char path[MAX_LOCATION] = "";
3819 char file[MAX_FILENAME] = "";
3821 DebugListItem item { };
3822 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGODetectLoadedLibraryForAddedProjectIssues()");
3823 for(token : outTokens)
3825 if(TokenizeListItem(token, item))
3827 if(!strcmp(item.name, "target-name"))
3829 StripQuotes(item.value, path);
3830 MakeSystemPath(path);
3831 GetLastDirectory(path, file);
3833 else if(!strcmp(item.name, "symbols-loaded"))
3835 symbolsLoaded = (atoi(item.value) == 1);
3840 if(path[0] && file[0])
3842 for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
3846 char prjTargetPath[MAX_LOCATION];
3847 char prjTargetFile[MAX_FILENAME];
3848 DirExpression targetDirExp = prj.GetTargetDir(currentCompiler, prj.config, bitDepth);
3849 strcpy(prjTargetPath, prj.topNode.path);
3850 PathCat(prjTargetPath, targetDirExp.dir);
3851 prjTargetFile[0] = '\0';
3852 prj.CatTargetFileName(prjTargetFile, currentCompiler, prj.config);
3853 PathCat(prjTargetPath, prjTargetFile);
3854 MakeSystemPath(prjTargetPath);
3856 match = !fstrcmp(prjTargetFile, file);
3857 if(!match && (dot = strstr(prjTargetFile, ".so.")))
3859 char * dot3 = strstr(dot+4, ".");
3863 match = !fstrcmp(prjTargetFile, file);
3868 match = !fstrcmp(prjTargetFile, file);
3873 // TODO: nice visual feedback to better warn user. use some ide notification system or other means.
3874 /* -- 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)
3876 ide.outputView.debugBox.Logf($"Attention! No symbols for loaded library %s matched to the %s added project.\n", path, prj.topNode.name);
3878 match = !fstrcmp(prjTargetPath, path);
3879 if(!match && (dot = strstr(prjTargetPath, ".so.")))
3881 char * dot3 = strstr(dot+4, ".");
3885 match = !fstrcmp(prjTargetPath, path);
3890 match = !fstrcmp(prjTargetPath, path);
3894 ide.outputView.debugBox.Logf($"Loaded library %s doesn't match the %s target of the %s added project.\n", path, prjTargetPath, prj.topNode.name);
3901 void FGOBreakpointModified(Array<char *> outTokens)
3903 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGOBreakpointModified() -- TODO only if needed: support breakpoint modified");
3905 DebugListItem item { };
3906 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3908 if(!strcmp(item.name, "bkpt"))
3910 GdbDataBreakpoint modBp = ParseBreakpoint(item.value, outTokens);
3918 ExpressionType ::DebugEvalExpTypeError(char * result)
3920 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::DebugEvalExpTypeError()");
3925 case symbolNotFound:
3926 return symbolErrorExp;
3927 case memoryCantBeRead:
3928 return memoryErrorExp;
3930 return unknownErrorExp;
3933 char * ::EvaluateExpression(char * expression, ExpressionType * error)
3936 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateExpression(", expression, ")");
3937 if(ide.projectView && ide.debugger.state == stopped)
3939 result = GdbEvaluateExpression(expression);
3940 *error = DebugEvalExpTypeError(result);
3945 *error = noDebuggerErrorExp;
3950 char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
3953 char * result = GdbReadMemoryString(address, size, format, 1, 1);
3954 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ReadMemory(", address, ")");
3955 if(!result || !strcmp(result, "N/A"))
3956 *error = memoryErrorExp;
3958 *error = DebugEvalExpTypeError(result);
3963 class ValgrindLogThread : Thread
3969 static char output[4096];
3970 Array<char> dynamicBuffer { minAllocSize = 4096 };
3971 File oldValgrindHandle = vgLogFile;
3972 incref oldValgrindHandle;
3975 while(debugger.state != terminated && vgLogFile)
3979 result = vgLogFile.Read(output, 1, sizeof(output));
3981 if(debugger.state == terminated || !vgLogFile/* || vgLogFile.Eof()*/)
3988 for(c = 0; c<result; c++)
3990 if(output[c] == '\n')
3992 int pos = dynamicBuffer.size;
3993 dynamicBuffer.size += c - start;
3994 memcpy(&dynamicBuffer[pos], output + start, c - start);
3995 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3996 // COMMENTED OUT DUE TO ISSUE #135, FIXED
3997 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
3998 dynamicBuffer.size++;
3999 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4001 // printf("%s\n", dynamicBuffer.array);
4003 if(strstr(&dynamicBuffer[0], "vgdb me"))
4004 debugger.serialSemaphore.Release();
4005 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4006 dynamicBuffer.size = 0;
4012 int pos = dynamicBuffer.size;
4013 dynamicBuffer.size += c - start;
4014 memcpy(&dynamicBuffer[pos], output + start, c - start);
4017 else if(debugger.state == stopped)
4020 printf("Got end of file from GDB!\n");
4027 delete dynamicBuffer;
4028 ide.outputView.debugBox.Logf($"ValgrindLogThreadExit\n");
4029 //if(oldValgrindHandle == vgLogFile)
4030 debugger.GdbThreadExit/*ValgrindLogThreadExit*/();
4031 delete oldValgrindHandle;
4037 class ValgrindTargetThread : Thread
4043 static char output[4096];
4044 Array<char> dynamicBuffer { minAllocSize = 4096 };
4045 DualPipe oldValgrindHandle = vgTargetHandle;
4046 incref oldValgrindHandle;
4049 while(debugger.state != terminated && vgTargetHandle && !vgTargetHandle.Eof())
4053 result = vgTargetHandle.Read(output, 1, sizeof(output));
4055 if(debugger.state == terminated || !vgTargetHandle || vgTargetHandle.Eof())
4062 for(c = 0; c<result; c++)
4064 if(output[c] == '\n')
4066 int pos = dynamicBuffer.size;
4067 dynamicBuffer.size += c - start;
4068 memcpy(&dynamicBuffer[pos], output + start, c - start);
4069 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4070 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4071 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4072 dynamicBuffer.size++;
4073 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4075 // printf("%s\n", dynamicBuffer.array);
4077 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4079 dynamicBuffer.size = 0;
4085 int pos = dynamicBuffer.size;
4086 dynamicBuffer.size += c - start;
4087 memcpy(&dynamicBuffer[pos], output + start, c - start);
4093 printf("Got end of file from GDB!\n");
4097 delete dynamicBuffer;
4098 //if(oldValgrindHandle == vgTargetHandle)
4099 debugger.ValgrindTargetThreadExit();
4100 delete oldValgrindHandle;
4106 class GdbThread : Thread
4112 static char output[4096];
4113 Array<char> dynamicBuffer { minAllocSize = 4096 };
4114 DualPipe oldGdbHandle = gdbHandle;
4115 incref oldGdbHandle;
4118 while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
4122 result = gdbHandle.Read(output, 1, sizeof(output));
4124 if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
4131 for(c = 0; c<result; c++)
4133 if(output[c] == '\n')
4135 int pos = dynamicBuffer.size;
4136 dynamicBuffer.size += c - start;
4137 memcpy(&dynamicBuffer[pos], output + start, c - start);
4138 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4139 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4140 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4141 dynamicBuffer.size++;
4142 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4144 // _dpl(0, dynamicBuffer.array);
4146 debugger.GdbThreadMain(&dynamicBuffer[0]);
4147 dynamicBuffer.size = 0;
4153 int pos = dynamicBuffer.size;
4154 dynamicBuffer.size += c - start;
4155 memcpy(&dynamicBuffer[pos], output + start, c - start);
4161 _dpl(0, "Got end of file from GDB!");
4165 delete dynamicBuffer;
4166 //if(oldGdbHandle == gdbHandle)
4167 debugger.GdbThreadExit();
4168 delete oldGdbHandle;
4174 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
4175 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
4177 #if defined(__unix__)
4182 #include <sys/types.h>
4187 class ProgramThread : Thread
4193 bool fileCreated = false;
4195 static char output[1000];
4198 /*if(!mkfifo(progFifoPath, mask))
4205 ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
4209 if(FileExists(progFifoPath)) //fileCreated)
4211 fifoFile = FileOpen(progFifoPath, read);
4215 ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
4220 fd = fileno((FILE *)fifoFile.input);
4221 //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
4225 while(!terminate && fifoFile && !fifoFile.Eof())
4228 struct timeval time;
4236 selectResult = select(fd + 1, &rs, null, null, &time);
4237 if(FD_ISSET(fd, &rs))
4239 int result = (int)read(fd, output, sizeof(output)-1);
4240 if(!result || (result < 0 && errno != EAGAIN))
4244 output[result] = '\0';
4245 if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
4248 ide.outputView.debugBox.Log(output);
4257 //fifoFile.CloseInput();
4260 ide.outputView.debugBox.Log("\n");
4264 if(FileExists(progFifoPath)) //fileCreated)
4266 DeleteFile(progFifoPath);
4267 progFifoPath[0] = '\0';
4275 class Argument : struct
4277 Argument prev, next;
4279 property char * name { set { delete name; if(value) name = CopyString(value); } }
4281 property char * val { set { delete val; if(value) val = CopyString(value); } }
4295 class Frame : struct
4300 property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4302 property char * func { set { delete func; if(value) func = CopyString(value); } }
4306 property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
4308 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4309 char * absoluteFile;
4310 property char * absoluteFile { set { delete absoluteFile; if(value) absoluteFile = CopyUnescapedUnixPath(value); } }
4319 delete absoluteFile;
4320 args.Free(Argument::Free);
4329 class GdbDataStop : struct
4346 char * gdbResultVar;
4356 if(!strcmp(reason, "signal-received"))
4361 else if(!strcmp(reason, "function-finished"))
4363 delete gdbResultVar;
4368 if(frame) frame.Free();
4377 class GdbDataBreakpoint : struct
4381 property char * number { set { delete number; if(value) number = CopyString(value); } }
4383 property char * type { set { delete type; if(value) type = CopyString(value); } }
4385 property char * disp { set { delete disp; if(value) disp = CopyString(value); } }
4388 property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4390 property char * func { set { delete func; if(value) func = CopyString(value); } }
4392 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4394 property char * fullname { set { delete fullname; if(value) fullname = CopyUnescapedUnixPath(value); } }
4397 property char * at { set { delete at; if(value) at = CopyString(value); } }
4400 Array<GdbDataBreakpoint> multipleBPs;
4405 PrintLn("{", "#", number, " T", type, " D", disp, " E", enabled, " H", times, " (", func, ") (", file, ":", line, ") (", fullname, ") (", addr, ") (", at, ")", "}");
4416 if(multipleBPs) multipleBPs.Free();
4420 ~GdbDataBreakpoint()
4426 class Breakpoint : struct
4431 property char * function { set { delete function; if(value) function = CopyString(value); } }
4432 char * relativeFilePath;
4433 property char * relativeFilePath { set { delete relativeFilePath; if(value) relativeFilePath = CopyString(value); } }
4434 char * absoluteFilePath;
4435 property char * absoluteFilePath { set { delete absoluteFilePath; if(value) absoluteFilePath = CopyString(value); } }
4444 BreakpointType type;
4446 GdbDataBreakpoint bp;
4448 char * CopyLocationString(bool removePath)
4451 char * file = relativeFilePath ? relativeFilePath : absoluteFilePath;
4452 bool removingPath = removePath && file;
4455 char * fileName = new char[MAX_FILENAME];
4456 GetLastDirectory(file, fileName);
4462 location = PrintString(file, ":", function);
4464 location = CopyString(function);
4467 location = PrintString(file, ":", line);
4473 char * CopyUserLocationString()
4476 char * loc = CopyLocationString(false);
4478 for(p : ide.workspace.projects)
4480 if(p.topNode.FindByFullPath(absoluteFilePath, false))
4488 location = PrintString("(", prj.name, ")", loc);
4498 if(relativeFilePath && relativeFilePath[0])
4500 f.Printf(" * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
4502 f.Printf(" ~ %s\n", condition.expression);
4512 delete relativeFilePath;
4513 delete absoluteFilePath;
4523 class Watch : struct
4534 f.Printf(" ~ %s\n", expression);
4558 class DebugListItem : struct
4564 struct DebugEvaluationData
4569 uint64 nextBlockAddress;
4571 DebuggerEvaluationError error;
4574 class CodeLocation : struct
4577 char * absoluteFile;
4580 CodeLocation ::ParseCodeLocation(char * location)
4584 char * colon = null;
4586 char loc[MAX_LOCATION];
4587 strcpy(loc, location);
4588 for(temp = loc; temp = strstr(temp, ":"); temp++)
4596 int line = atoi(colon);
4599 CodeLocation codloc { line = line };
4600 codloc.file = CopyString(loc);
4601 codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
4613 delete absoluteFile;
4622 void GDBFallBack(Expression exp, String expString)
4625 ExpressionType evalError = dummyExp;
4626 result = Debugger::EvaluateExpression(expString, &evalError);
4629 exp.constant = result;
4630 exp.type = constantExp;