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 && bpItem.multipleBPs && bpItem.multipleBPs.count)
1873 GdbDataBreakpoint first = null;
1874 for(n : bpItem.multipleBPs)
1876 if(!fstrcmp(n.fullname, bp.absoluteFilePath))
1886 GdbCommand(false, "-break-disable %s", n.number);
1890 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error breakpoint already disabled.");
1895 bpItem.addr = first.addr;
1896 bpItem.func = first.func;
1897 bpItem.file = first.file;
1898 bpItem.fullname = first.fullname;
1899 bpItem.line = first.line;
1900 //bpItem.thread-groups = first.thread-groups;
1901 bpItem.multipleBPs.Free();
1902 delete bpItem.multipleBPs;
1905 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error multiple breakpoints all disabled.");
1907 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error multiple breakpoints in exact same file not supported.");
1911 bp.inserted = (bp.bp && bp.bp.number && strcmp(bp.bp.number, "0"));
1913 ValidateBreakpoint(bp);
1914 /*if(bp == bpRunToCursor)
1915 runToCursorDebugStart = false;*/
1918 return !breakpointError;
1921 void GdbBreakpointsDelete(bool deleteRunToCursor, bool deleteInternalBreakpoints)
1923 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbBreakpointsDelete(deleteRunToCursor(", deleteRunToCursor, "))");
1926 if(deleteInternalBreakpoints)
1929 UnsetBreakpoint(bp);
1931 for(bp : ide.workspace.breakpoints)
1932 UnsetBreakpoint(bp);
1933 if(deleteRunToCursor && bpRunToCursor)
1934 UnsetBreakpoint(bpRunToCursor);
1940 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbGetStack()");
1942 stackFrames.Free(Frame::Free);
1943 GdbCommand(false, "-stack-info-depth");
1945 GdbCommand(false, "-stack-info-depth 192");
1946 if(frameCount && frameCount <= 192)
1947 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 191));
1950 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 95));
1951 GdbCommand(false, "-stack-list-frames %d %d", Max(frameCount - 96, 96), frameCount - 1);
1953 GdbCommand(false, "");
1958 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbTargetSet()");
1961 char escaped[MAX_LOCATION];
1962 strescpy(escaped, targetFile);
1963 GdbCommand(false, "file \"%s\"", escaped); //GDB/MI Missing Implementation -symbol-file, -target-attach
1970 const char *vgdbCommand = "/usr/bin/vgdb"; // TODO: vgdb command config option
1971 //GdbCommand(false, "-target-select remote | %s --pid=%d", "vgdb", targetProcessId);
1972 printf("target remote | %s --pid=%d\n", vgdbCommand, targetProcessId);
1973 GdbCommand(false, "target remote | %s --pid=%d", vgdbCommand, targetProcessId); // TODO: vgdb command config option
1976 /*for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
1977 GdbCommand(false, "-environment-directory \"%s\"", prj.topNode.path);*/
1979 for(dir : ide.workspace.sourceDirs; dir && dir[0])
1981 bool interference = false;
1982 for(prj : ide.workspace.projects)
1984 if(!fstrcmp(prj.topNode.path, dir))
1986 interference = true;
1990 if(!interference && dir[0])
1991 GdbCommand(false, "-environment-directory \"%s\"", dir);
1999 /*void GdbTargetRelease()
2003 GdbBreakpointsDelete(true, true);
2004 GdbCommand(false, "file"); //GDB/MI Missing Implementation -target-detach
2010 void GdbDebugBreak(bool internal)
2012 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbDebugBreak()");
2016 breakType = DebuggerAction::internal;
2018 if(ide) ide.Update(null);
2020 if(Process_Break(targetProcessId)) //GdbCommand(false, "-exec-interrupt");
2021 serialSemaphore.Wait();
2024 ChangeState(loaded);
2025 targetProcessId = 0;
2030 ide.outputView.debugBox.Logf("Debugger Error: GdbDebugBreak with not target id should never happen\n");
2035 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecRun()");
2038 ShowDebuggerViews();
2040 GdbCommand(true, "-exec-continue");
2042 GdbCommand(true, "-exec-run");
2045 void GdbExecContinue(bool focus)
2047 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecContinue()");
2049 GdbCommand(focus, "-exec-continue");
2054 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecNext()");
2056 GdbCommand(true, "-exec-next");
2061 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecStep()");
2063 GdbCommand(true, "-exec-step");
2066 void GdbExecFinish()
2068 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecFinish()");
2070 GdbCommand(true, "-exec-finish");
2073 void GdbExecCommon()
2075 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecCommon()");
2076 ClearBreakDisplay();
2077 GdbBreakpointsInsert();
2080 #ifdef GDB_DEBUG_GUI
2081 void SendGDBCommand(char * command)
2083 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SendGDBCommand()");
2084 DebuggerState oldState = state;
2089 GdbDebugBreak(true);
2092 GdbCommand(false, command);
2095 if(oldState == running)
2096 GdbExecContinue(false);
2100 void ClearBreakDisplay()
2102 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ClearBreakDisplay()");
2104 activeFrameLevel = -1;
2114 stackFrames.Free(Frame::Free);
2115 WatchesCodeEditorLinkRelease();
2116 ide.callStackView.Clear();
2117 ide.threadsView.Clear();
2123 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbAbortExec()");
2125 GdbCommand(false, "-interpreter-exec console \"kill\""); // should use -exec-abort -- GDB/MI implementation incomplete
2129 bool GdbInit(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
2132 char oldDirectory[MAX_LOCATION];
2133 char tempPath[MAX_LOCATION];
2134 char command[MAX_F_STRING*4];
2135 Project project = ide.project;
2136 DirExpression targetDirExp = project.GetTargetDir(compiler, config, bitDepth);
2137 PathBackup pathBackup { };
2139 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbInit()");
2140 if(currentCompiler != compiler)
2142 delete currentCompiler;
2143 currentCompiler = compiler;
2144 incref currentCompiler;
2147 this.bitDepth = bitDepth;
2148 usingValgrind = useValgrind;
2150 ChangeState(loaded);
2152 sentBreakInsert = false;
2153 breakpointError = false;
2154 ignoreBreakpoints = false;
2160 ide.outputView.ShowClearSelectTab(debug);
2161 ide.outputView.debugBox.Logf($"Starting debug mode\n");
2163 #ifdef GDB_DEBUG_OUTPUT
2164 ide.outputView.gdbBox.Logf("run: Starting GDB\n");
2167 strcpy(tempPath, ide.workspace.projectDir);
2168 PathCatSlash(tempPath, targetDirExp.dir);
2170 targetDir = CopyString(tempPath);
2171 project.CatTargetFileName(tempPath, compiler, config);
2173 targetFile = CopyString(tempPath);
2175 GetWorkingDir(oldDirectory, MAX_LOCATION);
2176 if(ide.workspace.debugDir && ide.workspace.debugDir[0])
2178 char temp[MAX_LOCATION];
2179 strcpy(temp, ide.workspace.projectDir);
2180 PathCatSlash(temp, ide.workspace.debugDir);
2181 ChangeWorkingDir(temp);
2184 ChangeWorkingDir(ide.workspace.projectDir);
2186 ide.SetPath(true, compiler, config, bitDepth);
2188 // TODO: This pollutes the environment, but at least it works
2189 // It shouldn't really affect the IDE as the PATH gets restored and other variables set for testing will unlikely cause problems
2190 // What is the proper solution for this? DualPipeOpenEnv?
2191 // gdb set environment commands don't seem to take effect
2192 for(e : ide.workspace.environmentVars)
2194 SetEnvironment(e.name, e.string);
2199 char * clArgs = ide.workspace.commandLineArgs;
2200 const char *valgrindCommand = "valgrind"; // TODO: valgrind command config option //TODO: valgrind options
2201 ValgrindLeakCheck vgLeakCheck = ide.workspace.vgLeakCheck;
2202 int vgRedzoneSize = ide.workspace.vgRedzoneSize;
2203 bool vgTrackOrigins = ide.workspace.vgTrackOrigins;
2204 vgLogFile = CreateTemporaryFile(vgLogPath, "ecereidevglog");
2208 vgLogThread.Create();
2212 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't open temporary log file for Valgrind output\n");
2215 if(result && !CheckCommandAvailable(valgrindCommand))
2217 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for Valgrind is not available.\n", valgrindCommand);
2222 char * vgRedzoneSizeFlag = vgRedzoneSize == -1 ? "" : PrintString(" --redzone-size=", vgRedzoneSize);
2223 sprintf(command, "%s --vgdb=yes --vgdb-error=0 --log-file=%s --leak-check=%s%s --track-origins=%s %s%s%s",
2224 valgrindCommand, vgLogPath, (char*)vgLeakCheck, vgRedzoneSizeFlag, vgTrackOrigins ? "yes" : "no", targetFile, clArgs ? " " : "", clArgs ? clArgs : "");
2225 if(vgRedzoneSize != -1)
2226 delete vgRedzoneSizeFlag;
2227 vgTargetHandle = DualPipeOpen(PipeOpenMode { output = true, /*error = true, */input = true }, command);
2230 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start Valgrind\n");
2236 incref vgTargetHandle;
2237 vgTargetThread.Create();
2239 targetProcessId = vgTargetHandle.GetProcessID();
2240 waitingForPID = false;
2241 if(!targetProcessId)
2243 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get Valgrind process ID\n");
2250 serialSemaphore.Wait();
2258 (compiler.targetPlatform == win32 && bitDepth == 64) ? "x86_64-w64-mingw32-gdb" :
2259 (compiler.targetPlatform == win32 && bitDepth == 32) ? "i686-w64-mingw32-gdb" :
2261 if(!CheckCommandAvailable(command))
2263 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for GDB is not available.\n", command);
2268 strcat(command, " -n -silent --interpreter=mi2"); //-async //\"%s\"
2270 gdbHandle = DualPipeOpen(PipeOpenMode { output = true, /*error = true, */input = true }, command);
2273 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start GDB\n");
2283 gdbProcessId = gdbHandle.GetProcessID();
2286 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get GDB process ID\n");
2293 serialSemaphore.Wait();
2296 GdbCommand(false, "-gdb-set verbose off");
2297 //GdbCommand(false, "-gdb-set exec-done-display on");
2298 GdbCommand(false, "-gdb-set step-mode off");
2299 GdbCommand(false, "-gdb-set unwindonsignal on");
2300 //GdbCommand(false, "-gdb-set shell on");
2301 GdbCommand(false, "set print elements 992");
2302 GdbCommand(false, "-gdb-set backtrace limit 100000");
2306 //ChangeState(terminated);
2312 #if defined(__unix__)
2314 CreateTemporaryDir(progFifoDir, "ecereide");
2315 strcpy(progFifoPath, progFifoDir);
2316 PathCat(progFifoPath, "ideprogfifo");
2317 if(!mkfifo(progFifoPath, 0600))
2319 //fileCreated = true;
2324 ide.outputView.debugBox.Logf(createFIFOMsg, progFifoPath);
2331 progThread.terminate = false;
2332 progThread.Create();
2336 #if defined(__WIN32__)
2337 GdbCommand(false, "-gdb-set new-console on");
2340 #if defined(__unix__)
2342 GdbCommand(false, "-inferior-tty-set %s", progFifoPath);
2346 GdbCommand(false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
2348 for(e : ide.workspace.environmentVars)
2350 GdbCommand(false, "set environment %s=%s", e.name, e.string);
2355 ChangeWorkingDir(oldDirectory);
2361 delete targetDirExp;
2367 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExit()");
2368 if(gdbHandle && gdbProcessId)
2370 GdbCommand(false, "-gdb-exit");
2385 ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
2391 for(bp : ide.workspace.breakpoints)
2393 bp.inserted = false;
2399 bp.inserted = false;
2404 bpRunToCursor.inserted = false;
2405 delete bpRunToCursor.bp;
2408 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2409 ClearBreakDisplay();
2412 #if defined(__unix__)
2413 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
2415 progThread.terminate = true;
2418 fifoFile.CloseInput();
2424 DeleteFile(progFifoPath);
2425 progFifoPath[0] = '\0';
2431 void WatchesCodeEditorLinkInit()
2433 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkInit()");
2435 char tempPath[MAX_LOCATION];
2436 char path[MAX_LOCATION];
2438 //void MakeFilePathProjectRelative(char * path, char * relativePath)
2439 if(!ide.projectView.project.GetRelativePath(activeFrame.file, tempPath))
2440 strcpy(tempPath, activeFrame.file);
2442 strcpy(path, ide.workspace.projectDir);
2443 PathCat(path, tempPath);
2444 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2447 for(srcDir : ide.workspace.sourceDirs)
2449 strcpy(path, srcDir);
2450 PathCat(path, tempPath);
2451 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2452 if(codeEditor) break;
2457 /*if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2458 activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);*/
2459 if(!activeFrame || !activeFrame.absoluteFile)
2462 codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal, false);
2465 codeEditor.inUseDebug = true;
2468 //watchesInit = true;
2471 void WatchesCodeEditorLinkRelease()
2473 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkRelease()");
2478 codeEditor.inUseDebug = false;
2479 if(!codeEditor.visible)
2480 codeEditor.Destroy(0);
2486 bool ResolveWatch(Watch wh)
2488 bool result = false;
2490 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ResolveWatch()");
2502 char watchmsg[MAX_F_STRING];
2503 if(state == stopped && !codeEditor)
2504 wh.value = CopyString($"No source file found for selected frame");
2505 //if(codeEditor && state == stopped || state != stopped)
2508 Module backupPrivateModule;
2509 Context backupContext;
2510 Class backupThisClass;
2514 backupPrivateModule = GetPrivateModule();
2515 backupContext = GetCurrentContext();
2516 backupThisClass = GetThisClass();
2519 SetPrivateModule(codeEditor.privateModule);
2520 SetCurrentContext(codeEditor.globalContext);
2521 SetTopContext(codeEditor.globalContext);
2522 SetGlobalContext(codeEditor.globalContext);
2523 SetGlobalData(&codeEditor.globalData);
2526 exp = ParseExpressionString(wh.expression);
2528 if(exp && !parseError)
2530 char expString[4096];
2532 PrintExpression(exp, expString);
2534 if(GetPrivateModule())
2537 DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2538 ProcessExpressionType(exp);
2540 wh.type = exp.expType;
2543 DebugComputeExpression(exp);
2544 if(ExpressionIsError(exp))
2546 GDBFallBack(exp, expString);
2549 /*if(exp.hasAddress)
2551 char temp[MAX_F_STRING];
2552 sprintf(temp, "0x%x", exp.address);
2553 wh.address = CopyString(temp);
2554 // wh.address = CopyStringf("0x%x", exp.address);
2559 Type dataType = exp.expType;
2562 char temp[MAX_F_STRING];
2563 switch(dataType.kind)
2566 sprintf(temp, "%i", exp.val.c);
2569 sprintf(temp, "%i", exp.val.s);
2574 sprintf(temp, "%i", exp.val.i);
2577 sprintf(temp, "%i", exp.val.i64);
2580 sprintf(temp, "%i", exp.val.p);
2585 long v = (long)exp.val.f;
2586 sprintf(temp, "%i", v);
2591 long v = (long)exp.val.d;
2592 sprintf(temp, "%i", v);
2597 wh.intVal = CopyString(temp);
2598 switch(dataType.kind)
2601 sprintf(temp, "0x%x", exp.val.c);
2604 sprintf(temp, "0x%x", exp.val.s);
2608 sprintf(temp, "0x%x", exp.val.i);
2611 sprintf(temp, "0x%x", exp.val.i64);
2614 sprintf(temp, "0x%x", exp.val.i64);
2617 sprintf(temp, "0x%x", exp.val.p);
2622 long v = (long)exp.val.f;
2623 sprintf(temp, "0x%x", v);
2628 long v = (long)exp.val.d;
2629 sprintf(temp, "0x%x", v);
2634 wh.hexVal = CopyString(temp);
2635 switch(dataType.kind)
2638 sprintf(temp, "0o%o", exp.val.c);
2641 sprintf(temp, "0o%o", exp.val.s);
2645 sprintf(temp, "0o%o", exp.val.i);
2648 sprintf(temp, "0o%o", exp.val.i64);
2651 sprintf(temp, "0o%o", exp.val.i64);
2654 sprintf(temp, "0o%o", exp.val.p);
2659 long v = (long)exp.val.f;
2660 sprintf(temp, "0o%o", v);
2665 long v = (long)exp.val.d;
2666 sprintf(temp, "0o%o", v);
2671 wh.octVal = CopyString(temp);
2674 // WHATS THIS HERE ?
2675 if(exp.type == constantExp && exp.constant)
2676 wh.constant = CopyString(exp.constant);
2682 case symbolErrorExp:
2683 snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2685 case structMemberSymbolErrorExp:
2686 // todo get info as in next case (ExpClassMemberSymbolError)
2687 snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2689 case classMemberSymbolErrorExp:
2692 Expression memberExp = exp.member.exp;
2693 Identifier memberID = exp.member.member;
2694 Type type = memberExp.expType;
2697 _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2700 char string[256] = "";
2702 PrintTypeNoConst(type, string, false, true);
2703 classSym = FindClass(string);
2704 _class = classSym ? classSym.registered : null;
2707 snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2709 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2712 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2715 case memoryErrorExp:
2716 // Need to ensure when set to memoryErrorExp, constant is set
2717 snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2719 case dereferenceErrorExp:
2720 snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2722 case unknownErrorExp:
2723 snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2725 case noDebuggerErrorExp:
2726 snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2728 case debugStateErrorExp:
2729 snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2732 snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2736 // Temporary Code for displaying Strings
2737 if((exp.expType && ((exp.expType.kind == pointerType ||
2738 exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) ||
2739 (wh.type && wh.type.kind == classType && wh.type._class &&
2740 wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2741 !strcmp(wh.type._class.registered.name, "String")))
2744 if(exp.expType.kind != arrayType || exp.hasAddress)
2750 //char temp[MAX_F_STRING * 32];
2752 ExpressionType evalError = dummyExp;
2753 /*if(exp.expType.kind == arrayType)
2754 sprintf(temp, "(char*)0x%x", exp.address);
2756 sprintf(temp, "(char*)%s", exp.constant);*/
2758 //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2759 // address = strtoul(exp.constant, null, 0);
2760 address = _strtoui64(exp.constant, null, 0);
2761 //_dpl(0, "0x", address);
2762 // snprintf(value, sizeof(value), "0x%08x ", address);
2764 if(address > 0xFFFFFFFFLL)
2765 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2767 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2768 value[sizeof(value)-1] = 0;
2771 strcat(value, $"Null string");
2775 len = strlen(value);
2777 while(!string && size > 2)
2779 string = GdbReadMemory(address, size);
2782 if(string && string[0])
2785 if(UTF8Validate(string))
2790 for(c = 0; (ch = string[c]) && c<4096; c++)
2793 value[len++] = '\0';
2798 ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2799 strcat(value, ") (ISO8859-1)");
2806 strcat(value, $"Empty string");
2810 strcat(value, $"Couldn't read memory");
2812 wh.value = CopyString(value);
2815 else if(wh.type && wh.type.kind == classType && wh.type._class &&
2816 wh.type._class.registered && wh.type._class.registered.type == enumClass)
2818 uint64 value = strtoul(exp.constant, null, 0);
2819 Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2820 EnumClassData enumeration = (EnumClassData)enumClass.data;
2822 for(item = enumeration.values.first; item; item = item.next)
2823 if((int)item.data == value)
2826 wh.value = CopyString(item.name);
2828 wh.value = CopyString($"Invalid Enum Value");
2831 else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class &&
2832 wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2839 if(exp.constant[0] == '\'')
2841 if((int)((byte *)exp.constant)[1] > 127)
2844 value = UTF8GetChar(exp.constant + 1, &nb);
2845 if(nb < 2) value = exp.constant[1];
2846 signedValue = value;
2850 signedValue = exp.constant[1];
2852 // Precomp Syntax error with boot strap here:
2853 byte b = (byte)(char)signedValue;
2854 value = (unichar) b;
2860 if(wh.type.kind == charType && wh.type.isSigned)
2862 signedValue = (int)(char)strtol(exp.constant, null, 0);
2864 // Precomp Syntax error with boot strap here:
2865 byte b = (byte)(char)signedValue;
2866 value = (unichar) b;
2871 value = (uint)strtoul(exp.constant, null, 0);
2872 signedValue = (int)value;
2876 UTF32toUTF8Len(&value, 1, charString, 5);
2878 snprintf(string, sizeof(string), "\'\\0' (0)");
2879 else if(value == '\t')
2880 snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2881 else if(value == '\n')
2882 snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2883 else if(value == '\r')
2884 snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2885 else if(wh.type.kind == charType && wh.type.isSigned)
2886 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2887 else if(value > 256 || wh.type.kind != charType)
2889 if(value > 0x10FFFF || !GetCharCategory(value))
2890 snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2892 snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2895 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2896 string[sizeof(string)-1] = 0;
2898 wh.value = CopyString(string);
2903 wh.value = CopyString(exp.constant);
2910 wh.value = PrintHexUInt64(exp.address);
2915 char tempString[256];
2916 if(exp.member.memberType == propertyMember)
2917 snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2919 snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression,
2920 exp.type.OnGetString(tempString, null, null));
2926 snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2927 if(exp) FreeExpression(exp);
2930 SetPrivateModule(backupPrivateModule);
2931 SetCurrentContext(backupContext);
2932 SetTopContext(backupContext);
2933 SetGlobalContext(backupContext);
2934 SetThisClass(backupThisClass);
2937 // wh.value = CopyString("No source file found for selected frame");
2939 watchmsg[sizeof(watchmsg)-1] = 0;
2941 wh.value = CopyString(watchmsg);
2943 ide.watchesView.UpdateWatch(wh);
2947 void EvaluateWatches()
2949 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateWatches()");
2950 for(wh : ide.workspace.watches)
2954 char * ::GdbEvaluateExpression(char * expression)
2956 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbEvaluateExpression(", expression, ")");
2959 GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2961 ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2965 // to be removed... use GdbReadMemory that returns a byte array instead
2966 char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
2968 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemoryString(", address, ")");
2973 _dpl(0, "GdbReadMemoryString called with size = 0!");
2975 // GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
2976 if(GetRuntimePlatform() == win32)
2977 GdbCommand(false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
2979 GdbCommand(false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
2981 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
2985 byte * ::GdbReadMemory(uint64 address, int bytes)
2987 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemory(", address, ")");
2990 //GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
2991 if(GetRuntimePlatform() == win32)
2992 GdbCommand(false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
2994 GdbCommand(false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
2997 _dpl(0, "GdbReadMemory called with bytes = 0!");
3000 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
3001 else if(eval.result && strcmp(eval.result, "N/A"))
3003 byte * result = new byte[bytes];
3004 byte * string = eval.result;
3008 result[c++] = (byte)strtol(string, &string, 10);
3024 void EventHit(GdbDataStop stopItem, Breakpoint bpInternal, Breakpoint bpUser)
3026 char * s1 = null; char * s2 = null;
3027 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EventHit(",
3028 "bpInternal(", bpInternal ? s1=bpInternal.CopyLocationString(false) : null, "), ",
3029 "bpUser(", bpUser ? s2=bpUser.CopyLocationString(false) : null, ")) -- ",
3030 "ignoreBreakpoints(", ignoreBreakpoints, "), ",
3031 "hitCursorBreakpoint(", bpUser && bpUser.type == runToCursor, ")");
3032 delete s1; delete s2;
3034 if(bpUser && stopItem.frame.line && bpUser.line != stopItem.frame.line)
3036 // updating user breakpoint on hit location difference
3037 // todo, print something?
3038 bpUser.line = stopItem.frame.line;
3039 ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3040 ide.workspace.Save();
3045 if(bpInternal.type == internalModulesLoaded)
3047 if(!bpUser && !userBreakOnInternalBreakpoint)
3048 GdbExecContinue(false);
3052 bool conditionMet = true;
3053 if(bpUser.condition)
3054 conditionMet = ResolveWatch(bpUser.condition);
3056 if(!ignoreBreakpoints && (bpUser.level == -1 || bpUser.level == frameCount-1) && conditionMet)
3063 GdbExecContinue(false);
3067 GdbExecContinue(false);
3068 ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3069 if(bpUser == bpRunToCursor)
3071 UnsetBreakpoint(bpUser);
3072 delete bpRunToCursor;
3076 if(!bpUser && !bpInternal)
3077 GdbExecContinue(false);
3080 void ValgrindTargetThreadExit()
3082 ide.outputView.debugBox.Logf($"ValgrindTargetThreadExit\n");
3085 vgTargetHandle.Wait();
3086 delete vgTargetHandle;
3088 HandleExit(null, null);
3091 void GdbThreadExit()
3093 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbThreadExit()");
3094 if(state != terminated)
3096 ChangeState(terminated);
3097 targetProcessId = 0;
3098 ClearBreakDisplay();
3104 serialSemaphore.Release();
3109 ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
3110 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3112 HideDebuggerViews();
3114 //ChangeState(terminated);
3118 void GdbThreadMain(char * output)
3121 Array<char *> outTokens { minAllocSize = 50 };
3122 Array<char *> subTokens { minAllocSize = 50 };
3123 DebugListItem item { };
3124 DebugListItem item2 { };
3125 bool setWaitingForPID = false;
3127 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
3128 #ifdef GDB_DEBUG_CONSOLE
3129 _dpl2(_dpct, dplchan::gdbOutput, 0, output);
3131 #ifdef GDB_DEBUG_OUTPUT
3133 int len = strlen(output);
3141 for(c = 0; c < len / 1024; c++)
3143 strncpy(tmp, start, 1024);
3144 ide.outputView.gdbBox.Logf("out: %s\n", tmp);
3147 ide.outputView.gdbBox.Logf("out: %s\n", start);
3151 ide.outputView.gdbBox.Logf("out: %s\n", output);
3155 #ifdef GDB_DEBUG_CONSOLE
3156 strcpy(lastGdbOutput, output);
3158 #ifdef GDB_DEBUG_GUI
3159 if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
3166 if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
3169 ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
3175 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
3177 //if(outTokens.count == 1)
3182 ChangeState(loaded);
3183 targetProcessId = 0;
3184 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3186 if(!strcmp(item.name, "reason"))
3188 char * reason = item.value;
3189 StripQuotes(reason, reason);
3190 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3193 if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
3195 StripQuotes(item2.value, item2.value);
3196 if(!strcmp(item2.name, "exit-code"))
3197 exitCode = item2.value;
3203 HandleExit(reason, exitCode);
3207 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "kill reply (", item.name, "=", item.value, ") is unheard of");
3210 HandleExit(null, null);
3213 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3215 if(!strcmp(item.name, "bkpt"))
3217 sentBreakInsert = false;
3222 bpItem = ParseBreakpoint(item.value, outTokens);
3223 //breakType = bpValidation;
3225 else if(!strcmp(item.name, "depth"))
3227 StripQuotes(item.value, item.value);
3228 frameCount = atoi(item.value);
3230 stackFrames.Free(Frame::Free);
3232 else if(!strcmp(item.name, "stack"))
3235 if(stackFrames.count)
3236 ide.callStackView.Logf("...\n");
3239 item.value = StripBrackets(item.value);
3240 TokenizeList(item.value, ',', subTokens);
3241 for(i = 0; i < subTokens.count; i++)
3243 if(TokenizeListItem(subTokens[i], item))
3245 if(!strcmp(item.name, "frame"))
3248 stackFrames.Add(frame);
3249 item.value = StripCurlies(item.value);
3250 ParseFrame(frame, item.value);
3251 if(frame.file && frame.from)
3252 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "unexpected frame file and from members present");
3256 if(activeFrameLevel == -1)
3258 if(ide.projectView.IsModuleInProject(frame.file));
3260 if(frame.level != 0)
3262 //stopItem.frame = frame;
3263 breakType = selectFrame;
3266 activeFrame = frame;
3267 activeFrameLevel = frame.level;
3270 ide.callStackView.Logf("%3d ", frame.level);
3271 if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
3272 ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
3273 else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
3274 ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
3275 else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
3276 ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
3277 else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
3278 ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
3280 ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
3285 ide.callStackView.Logf("%3d ", frame.level);
3290 ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
3294 ide.callStackView.Logf("%s\n", frame.func);
3296 ide.callStackView.Logf($"unknown source\n");
3300 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "stack content (", item.name, "=", item.value, ") is unheard of");
3303 if(activeFrameLevel == -1)
3305 activeFrameLevel = 0;
3306 activeFrame = stackFrames.first;
3308 ide.callStackView.Home();
3310 subTokens.RemoveAll();
3312 /*else if(!strcmp(item.name, "frame"))
3315 item.value = StripCurlies(item.value);
3316 ParseFrame(&frame, item.value);
3318 else if(!strcmp(item.name, "thread-ids"))
3320 ide.threadsView.Clear();
3321 item.value = StripCurlies(item.value);
3322 TokenizeList(item.value, ',', subTokens);
3323 for(i = subTokens.count - 1; ; i--)
3325 if(TokenizeListItem(subTokens[i], item))
3327 if(!strcmp(item.name, "thread-id"))
3330 StripQuotes(item.value, item.value);
3331 value = atoi(item.value);
3332 ide.threadsView.Logf("%3d \n", value);
3335 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "threads content (", item.name, "=", item.value, ") is unheard of");
3340 ide.threadsView.Home();
3342 subTokens.RemoveAll();
3343 //if(!strcmp(outTokens[2], "number-of-threads"))
3345 else if(!strcmp(item.name, "new-thread-id"))
3347 StripQuotes(item.value, item.value);
3348 activeThread = atoi(item.value);
3350 else if(!strcmp(item.name, "value"))
3352 StripQuotes(item.value, item.value);
3353 eval.result = CopyString(item.value);
3354 eval.active = false;
3356 else if(!strcmp(item.name, "addr"))
3358 for(i = 2; i < outTokens.count; i++)
3360 if(TokenizeListItem(outTokens[i], item))
3362 if(!strcmp(item.name, "total-bytes"))
3364 StripQuotes(item.value, item.value);
3365 eval.bytes = atoi(item.value);
3367 else if(!strcmp(item.name, "next-row"))
3369 StripQuotes(item.value, item.value);
3370 eval.nextBlockAddress = _strtoui64(item.value, null, 0);
3372 else if(!strcmp(item.name, "memory"))
3376 //StripQuotes(item.value, item.value);
3377 item.value = StripBrackets(item.value);
3378 // this should be treated as a list...
3379 item.value = StripCurlies(item.value);
3380 TokenizeList(item.value, ',', subTokens);
3381 for(j = 0; j < subTokens.count; j++)
3383 if(TokenizeListItem(subTokens[j], item))
3385 if(!strcmp(item.name, "data"))
3387 item.value = StripBrackets(item.value);
3388 StripQuotes2(item.value, item.value);
3389 eval.result = CopyString(item.value);
3390 eval.active = false;
3394 subTokens.RemoveAll();
3399 else if(!strcmp(item.name, "source-path") || !strcmp(item.name, "BreakpointTable"))
3400 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "command reply (", item.name, "=", item.value, ") is ignored");
3402 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "command reply (", item.name, "=", item.value, ") is unheard of");
3405 else if(!strcmp(outTokens[0], "^running"))
3407 waitingForPID = true;
3408 setWaitingForPID = true;
3410 else if(!strcmp(outTokens[0], "^exit"))
3412 ChangeState(terminated);
3413 // ide.outputView.debugBox.Logf("Exit\n");
3414 // ide.Update(null);
3416 serialSemaphore.Release();
3418 else if(!strcmp(outTokens[0], "^error"))
3422 sentBreakInsert = false;
3423 breakpointError = true;
3426 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3428 if(!strcmp(item.name, "msg"))
3430 StripQuotes(item.value, item.value);
3433 eval.active = false;
3435 if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3436 eval.error = symbolNotFound;
3437 else if(strstr(item.value, "Cannot access memory at address"))
3438 eval.error = memoryCantBeRead;
3440 eval.error = unknown;
3442 else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3445 else if(!strncmp(item.value, "Cannot access memory at address", 31))
3448 else if(!strcmp(item.value, "Cannot find bounds of current function"))
3450 ChangeState(stopped);
3451 gdbHandle.Printf("-exec-continue\n");
3453 else if(!strcmp(item.value, "ptrace: No such process."))
3455 ChangeState(loaded);
3456 targetProcessId = 0;
3458 else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3461 else if(!strcmp(item.value, "You can't do that without a process to debug."))
3463 ChangeState(loaded);
3464 targetProcessId = 0;
3466 else if(strstr(item.value, "No such file or directory."))
3468 ChangeState(loaded);
3469 targetProcessId = 0;
3471 else if(strstr(item.value, "During startup program exited with code "))
3473 ChangeState(loaded);
3474 targetProcessId = 0;
3479 if(strlen(item.value) < MAX_F_STRING)
3482 ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3486 ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3492 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "error content (", item.name, "=", item.value, ") is unheard of");
3495 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "result-record: ", outTokens[0]);
3496 outTokens.RemoveAll();
3499 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "status-async-output: ", outTokens[0]);
3502 if(TokenizeList(output, ',', outTokens))
3504 if(!strcmp(outTokens[0], "=library-loaded"))
3505 FGODetectLoadedLibraryForAddedProjectIssues(outTokens);
3506 else if(!strcmp(outTokens[0], "=thread-group-created") || !strcmp(outTokens[0], "=thread-group-added") ||
3507 !strcmp(outTokens[0], "=thread-group-started") || !strcmp(outTokens[0], "=thread-group-exited") ||
3508 !strcmp(outTokens[0], "=thread-created") || !strcmp(outTokens[0], "=thread-exited") ||
3509 !strcmp(outTokens[0], "=cmd-param-changed") || !strcmp(outTokens[0], "=library-unloaded") ||
3510 !strcmp(outTokens[0], "=breakpoint-modified"))
3511 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, outTokens[0], outTokens.count>1 ? outTokens[1] : "",
3512 outTokens.count>2 ? outTokens[2] : "", outTokens.count>3 ? outTokens[3] : "",
3513 outTokens.count>4 ? outTokens[4] : "", outTokens.count>5 ? outTokens[5] : "",
3514 outTokens.count>6 ? outTokens[6] : "", outTokens.count>7 ? outTokens[7] : "",
3515 outTokens.count>8 ? outTokens[8] : "", outTokens.count>9 ? outTokens[9] : "");
3517 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "notify-async-output: ", outTokens[0]);
3519 outTokens.RemoveAll();
3523 if(TokenizeList(output, ',', outTokens))
3525 if(!strcmp(outTokens[0],"*running"))
3527 waitingForPID = true;
3528 setWaitingForPID = true;
3530 else if(!strcmp(outTokens[0], "*stopped"))
3533 ChangeState(stopped);
3535 for(tk = 1; tk < outTokens.count; tk++)
3537 if(TokenizeListItem(outTokens[tk], item))
3539 if(!strcmp(item.name, "reason"))
3541 char * reason = item.value;
3542 StripQuotes(reason, reason);
3543 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3546 if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3549 StripQuotes(item2.value, item2.value);
3550 if(!strcmp(item2.name, "exit-code"))
3551 exitCode = item2.value;
3557 HandleExit(reason, exitCode);
3560 else if(!strcmp(reason, "breakpoint-hit"))
3566 stopItem = GdbDataStop { };
3568 for(i = tk+1; i < outTokens.count; i++)
3570 TokenizeListItem(outTokens[i], item);
3571 StripQuotes(item.value, item.value);
3572 if(!strcmp(item.name, "bkptno"))
3573 stopItem.bkptno = atoi(item.value);
3574 else if(!strcmp(item.name, "thread-id"))
3575 stopItem.threadid = atoi(item.value);
3576 else if(!strcmp(item.name, "frame"))
3578 item.value = StripCurlies(item.value);
3579 ParseFrame(stopItem.frame, item.value);
3581 else if(!strcmp(item.name, "disp") || !strcmp(item.name, "stopped-threads") || !strcmp(item.name, "core"))
3582 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3584 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown breakpoint hit item name (", item.name, "=", item.value, ")");
3589 else if(!strcmp(reason, "end-stepping-range"))
3595 stopItem = GdbDataStop { };
3597 for(i = tk+1; i < outTokens.count; i++)
3599 TokenizeListItem(outTokens[i], item);
3600 StripQuotes(item.value, item.value);
3601 if(!strcmp(item.name, "thread-id"))
3602 stopItem.threadid = atoi(item.value);
3603 else if(!strcmp(item.name, "frame"))
3605 item.value = StripCurlies(item.value);
3606 ParseFrame(stopItem.frame, item.value);
3608 else if(!strcmp(item.name, "reason") || !strcmp(item.name, "bkptno"))
3609 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3611 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown end of stepping range item name (", item.name, "=", item.value, ")");
3617 else if(!strcmp(reason, "function-finished"))
3623 stopItem = GdbDataStop { };
3624 stopItem.reason = CopyString(reason);
3626 for(i = tk+1; i < outTokens.count; i++)
3628 TokenizeListItem(outTokens[i], item);
3629 StripQuotes(item.value, item.value);
3630 if(!strcmp(item.name, "thread-id"))
3631 stopItem.threadid = atoi(item.value);
3632 else if(!strcmp(item.name, "frame"))
3634 item.value = StripCurlies(item.value);
3635 ParseFrame(stopItem.frame, item.value);
3637 else if(!strcmp(item.name, "gdb-result-var"))
3638 stopItem.gdbResultVar = CopyString(item.value);
3639 else if(!strcmp(item.name, "return-value"))
3640 stopItem.returnValue = CopyString(item.value);
3642 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown function finished item name (", item.name, "=", item.value, ")");
3645 event = functionEnd;
3648 else if(!strcmp(reason, "signal-received"))
3654 stopItem = GdbDataStop { };
3655 stopItem.reason = CopyString(reason);
3657 for(i = tk+1; i < outTokens.count; i++)
3659 TokenizeListItem(outTokens[i], item);
3660 StripQuotes(item.value, item.value);
3661 if(!strcmp(item.name, "signal-name"))
3662 stopItem.name = CopyString(item.value);
3663 else if(!strcmp(item.name, "signal-meaning"))
3664 stopItem.meaning = CopyString(item.value);
3665 else if(!strcmp(item.name, "thread-id"))
3666 stopItem.threadid = atoi(item.value);
3667 else if(!strcmp(item.name, "frame"))
3669 item.value = StripCurlies(item.value);
3670 ParseFrame(stopItem.frame, item.value);
3673 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown signal reveived item name (", item.name, "=", item.value, ")");
3675 if(!strcmp(stopItem.name, "SIGTRAP"))
3694 else if(!strcmp(reason, "watchpoint-trigger"))
3695 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint trigger not handled");
3696 else if(!strcmp(reason, "read-watchpoint-trigger"))
3697 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason read watchpoint trigger not handled");
3698 else if(!strcmp(reason, "access-watchpoint-trigger"))
3699 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason access watchpoint trigger not handled");
3700 else if(!strcmp(reason, "watchpoint-scope"))
3701 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint scope not handled");
3702 else if(!strcmp(reason, "location-reached"))
3703 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason location reached not handled");
3705 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown reason: ", reason);
3713 if(usingValgrind && event == none && !stopItem)
3714 event = valgrindStartPause;
3719 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown exec-async-output: ", outTokens[0]);
3720 outTokens.RemoveAll();
3723 if(!strcmpi(output, "(gdb) "))
3727 char exeFile[MAX_LOCATION];
3728 int oldProcessID = targetProcessId;
3729 GetLastDirectory(targetFile, exeFile);
3731 while(!targetProcessId/*true*/)
3733 targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3734 if(targetProcessId || gdbHandle.Peek()) break;
3739 ChangeState(running);
3740 else if(!oldProcessID)
3742 ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3743 // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3744 gdbHandle.Printf("-gdb-exit\n");
3746 ChangeState(terminated); //loaded;
3751 for(bp : ide.workspace.breakpoints)
3752 bp.inserted = false;
3755 bp.inserted = false;
3757 bpRunToCursor.inserted = false;
3759 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3760 ClearBreakDisplay();
3762 #if defined(__unix__)
3763 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
3765 progThread.terminate = true;
3768 fifoFile.CloseInput();
3775 DeleteFile(progFifoPath);
3776 progFifoPath[0] = '\0';
3783 serialSemaphore.Release();
3786 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown prompt", output);
3790 if(!strncmp(output, "&\"warning:", 10))
3793 content = strstr(output, "\"");
3794 StripQuotes(content, content);
3795 content = strstr(content, ":");
3801 ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3808 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown output: ", output);
3810 if(!setWaitingForPID)
3811 waitingForPID = false;
3812 setWaitingForPID = false;
3820 // From GDB Output functions
3821 void FGODetectLoadedLibraryForAddedProjectIssues(Array<char *> outTokens)
3823 char path[MAX_LOCATION] = "";
3824 char file[MAX_FILENAME] = "";
3826 DebugListItem item { };
3827 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGODetectLoadedLibraryForAddedProjectIssues()");
3828 for(token : outTokens)
3830 if(TokenizeListItem(token, item))
3832 if(!strcmp(item.name, "target-name"))
3834 StripQuotes(item.value, path);
3835 MakeSystemPath(path);
3836 GetLastDirectory(path, file);
3838 else if(!strcmp(item.name, "symbols-loaded"))
3840 symbolsLoaded = (atoi(item.value) == 1);
3845 if(path[0] && file[0])
3847 for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
3851 char prjTargetPath[MAX_LOCATION];
3852 char prjTargetFile[MAX_FILENAME];
3853 DirExpression targetDirExp = prj.GetTargetDir(currentCompiler, prj.config, bitDepth);
3854 strcpy(prjTargetPath, prj.topNode.path);
3855 PathCat(prjTargetPath, targetDirExp.dir);
3856 prjTargetFile[0] = '\0';
3857 prj.CatTargetFileName(prjTargetFile, currentCompiler, prj.config);
3858 PathCat(prjTargetPath, prjTargetFile);
3859 MakeSystemPath(prjTargetPath);
3861 match = !fstrcmp(prjTargetFile, file);
3862 if(!match && (dot = strstr(prjTargetFile, ".so.")))
3864 char * dot3 = strstr(dot+4, ".");
3868 match = !fstrcmp(prjTargetFile, file);
3873 match = !fstrcmp(prjTargetFile, file);
3878 // TODO: nice visual feedback to better warn user. use some ide notification system or other means.
3879 /* -- 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)
3881 ide.outputView.debugBox.Logf($"Attention! No symbols for loaded library %s matched to the %s added project.\n", path, prj.topNode.name);
3883 match = !fstrcmp(prjTargetPath, path);
3884 if(!match && (dot = strstr(prjTargetPath, ".so.")))
3886 char * dot3 = strstr(dot+4, ".");
3890 match = !fstrcmp(prjTargetPath, path);
3895 match = !fstrcmp(prjTargetPath, path);
3899 ide.outputView.debugBox.Logf($"Loaded library %s doesn't match the %s target of the %s added project.\n", path, prjTargetPath, prj.topNode.name);
3906 void FGOBreakpointModified(Array<char *> outTokens)
3908 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGOBreakpointModified() -- TODO only if needed: support breakpoint modified");
3910 DebugListItem item { };
3911 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3913 if(!strcmp(item.name, "bkpt"))
3915 GdbDataBreakpoint modBp = ParseBreakpoint(item.value, outTokens);
3923 ExpressionType ::DebugEvalExpTypeError(char * result)
3925 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::DebugEvalExpTypeError()");
3930 case symbolNotFound:
3931 return symbolErrorExp;
3932 case memoryCantBeRead:
3933 return memoryErrorExp;
3935 return unknownErrorExp;
3938 char * ::EvaluateExpression(char * expression, ExpressionType * error)
3941 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateExpression(", expression, ")");
3942 if(ide.projectView && ide.debugger.state == stopped)
3944 result = GdbEvaluateExpression(expression);
3945 *error = DebugEvalExpTypeError(result);
3950 *error = noDebuggerErrorExp;
3955 char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
3958 char * result = GdbReadMemoryString(address, size, format, 1, 1);
3959 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ReadMemory(", address, ")");
3960 if(!result || !strcmp(result, "N/A"))
3961 *error = memoryErrorExp;
3963 *error = DebugEvalExpTypeError(result);
3968 class ValgrindLogThread : Thread
3974 static char output[4096];
3975 Array<char> dynamicBuffer { minAllocSize = 4096 };
3976 File oldValgrindHandle = vgLogFile;
3977 incref oldValgrindHandle;
3980 while(debugger.state != terminated && vgLogFile)
3984 result = vgLogFile.Read(output, 1, sizeof(output));
3986 if(debugger.state == terminated || !vgLogFile/* || vgLogFile.Eof()*/)
3993 for(c = 0; c<result; c++)
3995 if(output[c] == '\n')
3997 int pos = dynamicBuffer.size;
3998 dynamicBuffer.size += c - start;
3999 memcpy(&dynamicBuffer[pos], output + start, c - start);
4000 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4001 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4002 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4003 dynamicBuffer.size++;
4004 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4006 // printf("%s\n", dynamicBuffer.array);
4008 if(strstr(&dynamicBuffer[0], "vgdb me"))
4009 debugger.serialSemaphore.Release();
4010 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4011 dynamicBuffer.size = 0;
4017 int pos = dynamicBuffer.size;
4018 dynamicBuffer.size += c - start;
4019 memcpy(&dynamicBuffer[pos], output + start, c - start);
4022 else if(debugger.state == stopped)
4025 printf("Got end of file from GDB!\n");
4032 delete dynamicBuffer;
4033 ide.outputView.debugBox.Logf($"ValgrindLogThreadExit\n");
4034 //if(oldValgrindHandle == vgLogFile)
4035 debugger.GdbThreadExit/*ValgrindLogThreadExit*/();
4036 delete oldValgrindHandle;
4042 class ValgrindTargetThread : Thread
4048 static char output[4096];
4049 Array<char> dynamicBuffer { minAllocSize = 4096 };
4050 DualPipe oldValgrindHandle = vgTargetHandle;
4051 incref oldValgrindHandle;
4054 while(debugger.state != terminated && vgTargetHandle && !vgTargetHandle.Eof())
4058 result = vgTargetHandle.Read(output, 1, sizeof(output));
4060 if(debugger.state == terminated || !vgTargetHandle || vgTargetHandle.Eof())
4067 for(c = 0; c<result; c++)
4069 if(output[c] == '\n')
4071 int pos = dynamicBuffer.size;
4072 dynamicBuffer.size += c - start;
4073 memcpy(&dynamicBuffer[pos], output + start, c - start);
4074 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4075 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4076 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4077 dynamicBuffer.size++;
4078 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4080 // printf("%s\n", dynamicBuffer.array);
4082 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4084 dynamicBuffer.size = 0;
4090 int pos = dynamicBuffer.size;
4091 dynamicBuffer.size += c - start;
4092 memcpy(&dynamicBuffer[pos], output + start, c - start);
4098 printf("Got end of file from GDB!\n");
4102 delete dynamicBuffer;
4103 //if(oldValgrindHandle == vgTargetHandle)
4104 debugger.ValgrindTargetThreadExit();
4105 delete oldValgrindHandle;
4111 class GdbThread : Thread
4117 static char output[4096];
4118 Array<char> dynamicBuffer { minAllocSize = 4096 };
4119 DualPipe oldGdbHandle = gdbHandle;
4120 incref oldGdbHandle;
4123 while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
4127 result = gdbHandle.Read(output, 1, sizeof(output));
4129 if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
4136 for(c = 0; c<result; c++)
4138 if(output[c] == '\n')
4140 int pos = dynamicBuffer.size;
4141 dynamicBuffer.size += c - start;
4142 memcpy(&dynamicBuffer[pos], output + start, c - start);
4143 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4144 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4145 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4146 dynamicBuffer.size++;
4147 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4149 // _dpl(0, dynamicBuffer.array);
4151 debugger.GdbThreadMain(&dynamicBuffer[0]);
4152 dynamicBuffer.size = 0;
4158 int pos = dynamicBuffer.size;
4159 dynamicBuffer.size += c - start;
4160 memcpy(&dynamicBuffer[pos], output + start, c - start);
4166 _dpl(0, "Got end of file from GDB!");
4170 delete dynamicBuffer;
4171 //if(oldGdbHandle == gdbHandle)
4172 debugger.GdbThreadExit();
4173 delete oldGdbHandle;
4179 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
4180 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
4182 #if defined(__unix__)
4187 #include <sys/types.h>
4192 class ProgramThread : Thread
4198 bool fileCreated = false;
4200 static char output[1000];
4203 /*if(!mkfifo(progFifoPath, mask))
4210 ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
4214 if(FileExists(progFifoPath)) //fileCreated)
4216 fifoFile = FileOpen(progFifoPath, read);
4220 ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
4225 fd = fileno((FILE *)fifoFile.input);
4226 //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
4230 while(!terminate && fifoFile && !fifoFile.Eof())
4233 struct timeval time;
4241 selectResult = select(fd + 1, &rs, null, null, &time);
4242 if(FD_ISSET(fd, &rs))
4244 int result = (int)read(fd, output, sizeof(output)-1);
4245 if(!result || (result < 0 && errno != EAGAIN))
4249 output[result] = '\0';
4250 if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
4253 ide.outputView.debugBox.Log(output);
4262 //fifoFile.CloseInput();
4265 ide.outputView.debugBox.Log("\n");
4269 if(FileExists(progFifoPath)) //fileCreated)
4271 DeleteFile(progFifoPath);
4272 progFifoPath[0] = '\0';
4280 class Argument : struct
4282 Argument prev, next;
4284 property char * name { set { delete name; if(value) name = CopyString(value); } }
4286 property char * val { set { delete val; if(value) val = CopyString(value); } }
4300 class Frame : struct
4305 property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4307 property char * func { set { delete func; if(value) func = CopyString(value); } }
4311 property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
4313 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4314 char * absoluteFile;
4315 property char * absoluteFile { set { delete absoluteFile; if(value) absoluteFile = CopyUnescapedUnixPath(value); } }
4324 delete absoluteFile;
4325 args.Free(Argument::Free);
4334 class GdbDataStop : struct
4351 char * gdbResultVar;
4361 if(!strcmp(reason, "signal-received"))
4366 else if(!strcmp(reason, "function-finished"))
4368 delete gdbResultVar;
4373 if(frame) frame.Free();
4382 class GdbDataBreakpoint : struct
4386 property char * number { set { delete number; if(value) number = CopyString(value); } }
4388 property char * type { set { delete type; if(value) type = CopyString(value); } }
4390 property char * disp { set { delete disp; if(value) disp = CopyString(value); } }
4393 property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4395 property char * func { set { delete func; if(value) func = CopyString(value); } }
4397 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4399 property char * fullname { set { delete fullname; if(value) fullname = CopyUnescapedUnixPath(value); } }
4402 property char * at { set { delete at; if(value) at = CopyString(value); } }
4405 Array<GdbDataBreakpoint> multipleBPs;
4410 PrintLn("{", "#", number, " T", type, " D", disp, " E", enabled, " H", times, " (", func, ") (", file, ":", line, ") (", fullname, ") (", addr, ") (", at, ")", "}");
4421 if(multipleBPs) multipleBPs.Free();
4425 ~GdbDataBreakpoint()
4431 class Breakpoint : struct
4436 property char * function { set { delete function; if(value) function = CopyString(value); } }
4437 char * relativeFilePath;
4438 property char * relativeFilePath { set { delete relativeFilePath; if(value) relativeFilePath = CopyString(value); } }
4439 char * absoluteFilePath;
4440 property char * absoluteFilePath { set { delete absoluteFilePath; if(value) absoluteFilePath = CopyString(value); } }
4449 BreakpointType type;
4451 GdbDataBreakpoint bp;
4453 char * CopyLocationString(bool removePath)
4456 char * file = relativeFilePath ? relativeFilePath : absoluteFilePath;
4457 bool removingPath = removePath && file;
4460 char * fileName = new char[MAX_FILENAME];
4461 GetLastDirectory(file, fileName);
4467 location = PrintString(file, ":", function);
4469 location = CopyString(function);
4472 location = PrintString(file, ":", line);
4478 char * CopyUserLocationString()
4481 char * loc = CopyLocationString(false);
4483 for(p : ide.workspace.projects)
4485 if(p.topNode.FindByFullPath(absoluteFilePath, false))
4493 location = PrintString("(", prj.name, ")", loc);
4503 if(relativeFilePath && relativeFilePath[0])
4505 f.Printf(" * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
4507 f.Printf(" ~ %s\n", condition.expression);
4517 delete relativeFilePath;
4518 delete absoluteFilePath;
4528 class Watch : struct
4539 f.Printf(" ~ %s\n", expression);
4563 class DebugListItem : struct
4569 struct DebugEvaluationData
4574 uint64 nextBlockAddress;
4576 DebuggerEvaluationError error;
4579 class CodeLocation : struct
4582 char * absoluteFile;
4585 CodeLocation ::ParseCodeLocation(char * location)
4589 char * colon = null;
4591 char loc[MAX_LOCATION];
4592 strcpy(loc, location);
4593 for(temp = loc; temp = strstr(temp, ":"); temp++)
4601 int line = atoi(colon);
4604 CodeLocation codloc { line = line };
4605 codloc.file = CopyString(loc);
4606 codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
4618 delete absoluteFile;
4627 void GDBFallBack(Expression exp, String expString)
4630 ExpressionType evalError = dummyExp;
4631 result = Debugger::EvaluateExpression(expString, &evalError);
4634 exp.constant = result;
4635 exp.type = constantExp;