2 public import static "ecere"
3 public import static "ec"
15 #define GDB_DEBUG_CONSOLE
18 extern char * strrchr(const char * s, int c);
21 #define strlen _strlen
31 #include <sys/time.h> // Required on Apple...
45 sprintf(s[0], "%04d", now.year);
46 sprintf(s[1], "%02d", now.month+1);
47 sprintf(s[2], "%02d", now.day);
48 sprintf(s[3], "%02d", now.hour);
49 sprintf(s[4], "%02d", now.minute);
50 sprintf(s[5], "%02d", now.second);
51 time = PrintString("*", s[0], s[1], s[2], "-", s[3], s[4], s[5], "*");
57 // use =0 to disable printing of specific channels
59 static enum dplchan { none, gdbProtoIgnored=0/*1*/, gdbProtoUnknown=2, gdbOutput=3/*3*/, gdbCommand=4/*4*/, debuggerCall=5, debuggerProblem=6, debuggerTemp=7 };
61 static enum dplchan { none, gdbProtoIgnored=0, gdbProtoUnknown=0, gdbOutput=0, gdbCommand=0, debuggerCall=0, debuggerProblem=0, debuggerTemp=0 };
63 static char * _dpct[] = {
65 "GDB Protocol Ignored",
66 "GDB Protocol ***Unknown***",
70 "Debugger ***Problem***",
71 "-----> Temporary Message",
75 // TODO if(strlen(item.value) < MAX_F_STRING)
77 #define _dpl2(...) __dpl2(__FILE__, __LINE__, ##__VA_ARGS__)
78 static void __dpl2(char * file, int line, char ** channels, int channel, int indent, typed_object object, ...)
80 bool chan = channel && channels && channels[channel];
83 char string[MAX_F_STRING];
85 char * time = PrintNow();
87 //ide.outputView.debugBox.Logf();
88 Logf("%s %s:% 5d: %s%s", time, file, line, chan ? channels[channel] : "", chan && channels[channel][0] ? ": " : "");
89 va_start(args, object);
90 len = PrintStdArgsToBuffer(string, sizeof(string), object, args);
98 #define _dpl(...) __dpl(__FILE__, __LINE__, ##__VA_ARGS__)
99 static void __dpl(char * file, int line, int indent, char * format, ...)
102 char string[MAX_F_STRING];
104 char * time = PrintNow();
105 //static File f = null;
106 va_start(args, format);
107 vsnprintf(string, sizeof(string), format, args);
108 string[sizeof(string)-1] = 0;
111 char * time = PrintNow();
113 logName = PrintString(time, ".log");
115 f = FileOpen(logName, write);
118 /*f.Printf("%s %s:% 5d: ", time, file, line);
119 for(c = 0; c<indent; c++)
121 f.Printf("%s\n", string);*/
122 Logf("%s %s:% 5d: ", time, file, line);
123 for(c = 0; c<indent; c++)
125 Logf("%s\n", string);
130 public char * StripQuotes2(char * string, char * output)
134 bool quoted = false, escaped = false;
136 for(c = 0; ch = string[c]; c++)
140 if(escaped || ch != '\"')
143 escaped = !escaped && ch == '\\';
157 // String Escape Copy
158 static void strescpy(char * d, char * s)
211 static char * CopyUnescapedSystemPath(char * p)
213 char * d = new char[strlen(p) + 1];
215 #if defined(__WIN32__)
216 ChangeCh(d, '/', '\\');
221 static char * CopyUnescapedUnixPath(char * p)
223 char * d = new char[strlen(p) + 1];
225 #if defined(__WIN32__)
226 ChangeCh(d, '\\', '/');
231 static char * CopyUnescapedString(char * s)
233 char * d = new char[strlen(s) + 1];
238 // String Unescape Copy
240 // TOFIX: THIS DOESN'T HANDLE NUMERIC ESCAPE CODES (OCTAL/HEXADECIMAL...)?
241 // Seems very similar to ReadString in pass15.ec (which also misses numeric escape codes :) )
243 static void struscpy(char * d, char * s)
295 static char * StripBrackets(char * string)
297 int length = strlen(string);
298 if(length > 1 && *string == '[' && string[length - 1] == ']')
301 string[length - 1] = '\0';
308 static char * StripCurlies(char * string)
310 int length = strlen(string);
311 if(length > 1 && *string == '{' && string[length - 1] == '}')
314 string[length - 1] = '\0';
321 static int StringGetInt(char * string, int start)
324 int i, len = strlen(string);
326 for(i = start; i < len && i < start + 8; i++)
328 if(string[i] == '0' || string[i] == '1' || string[i] == '2' || string[i] == '3' || string[i] == '4' || string[i] == '5' || string[i] == '6' || string[i] == '7' || string[i] == '8' || string[i] == '9')
329 strncat(number, &string[i], 1);
336 static int TokenizeList(char * string, const char seperator, Array<char *> tokens)
340 bool quoted = false, escaped = false;
341 char * start = string, ch;
343 for(; (ch = *string); string++)
350 if(escaped || ch != '\"')
351 escaped = !escaped && ch == '\\';
357 else if(ch == '{' || ch == '[' || ch == '(' || ch == '<')
359 else if(ch == '}' || ch == ']' || ch == ')' || ch == '>')
361 else if(ch == seperator && !level)
370 //tokens[count] = start;
371 //tokens[count++] = start;
378 static bool TokenizeListItem(char * string, DebugListItem item)
380 char * equal = strstr(string, "=");
394 static bool CheckCommandAvailable(const char * command)
396 bool available = false;
398 char * name = new char[MAX_FILENAME];
399 char * pathVar = new char[maxPathLen];
401 GetEnvironment("PATH", pathVar, maxPathLen);
402 count = TokenizeWith(pathVar, sizeof(paths) / sizeof(char *), paths, pathListSep, false);
403 strcpy(name, command);
407 const char * extensions[] = { "exe", "com", "bat", null };
408 for(e=0; extensions[e]; e++)
410 ChangeExtension(name, extensions[e], name);
412 for(c=0; c<count; c++)
414 FileListing fl { paths[c] };
417 if(fl.stats.attribs.isFile && !fstrcmp(fl.name, name))
435 // define GdbGetLineSize = 1638400;
436 define GdbGetLineSize = 5638400;
437 #if defined(__unix__)
438 char progFifoPath[MAX_LOCATION];
439 char progFifoDir[MAX_LOCATION];
442 enum DebuggerState { none, prompt, loaded, running, stopped, terminated, error };
445 none, hit, breakEvent, signal, stepEnd, functionEnd, exit;
447 property bool canBeMonitored { get { return (this == hit || this == breakEvent || this == signal || this == stepEnd || this == functionEnd); } };
449 enum DebuggerAction { none, internal, restart, stop, selectFrame }; //, bpValidation
452 none, internalMain, internalWinMain, internalModulesLoaded, user, runToCursor, internalModuleLoad;
454 property bool isInternal { get { return (this == internalMain || this == internalWinMain || this == internalModulesLoaded || this == internalModuleLoad); } };
455 property bool isUser { get { return (this == user || this == runToCursor); } };
457 enum DebuggerEvaluationError { none, symbolNotFound, memoryCantBeRead, unknown };
459 FileDialog debuggerFileDialog { type = selectDir };
461 static DualPipe vgTargetHandle;
462 static File vgLogFile;
463 static char vgLogPath[MAX_LOCATION];
464 static DualPipe gdbHandle;
465 static DebugEvaluationData eval { };
467 static int targetProcessId;
469 static bool gdbReady;
470 static bool breakpointError;
474 Semaphore serialSemaphore { };
480 bool sentBreakInsert;
481 bool ignoreBreakpoints;
482 bool userBreakOnInternalBreakpoint;
483 //bool runToCursorDebugStart;
492 int activeFrameLevel;
503 DebuggerAction breakType;
504 //DebuggerCommand lastCommand; // THE COMPILER COMPILES STUFF THAT DOES NOT EXIST???
506 GdbDataStop stopItem;
507 GdbDataBreakpoint bpItem;
510 List<Breakpoint> sysBPs { };
511 Breakpoint bpRunToCursor;
515 CompilerConfig currentCompiler;
516 ProjectConfig prjConfig;
519 CodeEditor codeEditor;
521 ValgrindLogThread vgLogThread { debugger = this };
522 ValgrindTargetThread vgTargetThread { debugger = this };
523 GdbThread gdbThread { debugger = this };
526 delay = 0.0, userData = this;
530 bool monitor = false;
531 DebuggerEvent curEvent = event;
532 GdbDataStop stopItem = this.stopItem;
533 Breakpoint bpUser = null;
534 Breakpoint bpInternal = null;
541 this.stopItem = null;
544 if(curEvent && curEvent != exit)
547 _dpl(0, "No stop item");
555 Restart(currentCompiler, prjConfig, bitDepth, usingValgrind);
564 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
565 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
566 if(activeFrame.level == activeFrameLevel)
572 // GdbCommand(false, "-break-info %s", bpItem.number);
582 activeThread = stopItem.threadid;
583 GdbCommand(false, "-thread-list-ids");
589 Breakpoint bp = GetBreakpointById(stopItem.bkptno, &isInternal);
590 if(bp && bp.inserted && bp.bp.addr)
592 if(bp.type.isInternal)
596 if(stopItem && stopItem.frame)
598 if(bpInternal && bpRunToCursor && bpRunToCursor.inserted && !strcmp(bpRunToCursor.bp.addr, bp.bp.addr))
599 bpUser = bpRunToCursor;
602 for(item : (bpInternal ? ide.workspace.breakpoints : sysBPs); item.inserted)
604 if(item.bp && item.bp.addr && !strcmp(item.bp.addr, bp.bp.addr))
616 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Invalid stopItem!");
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;
643 activeThread = stopItem.threadid;
644 GdbCommand(false, "-thread-list-ids");
646 if(activeFrameLevel > 0)
647 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
649 WatchesCodeEditorLinkInit();
653 if(curEvent == signal)
657 ide.outputView.debugBox.Logf($"Signal received: %s - %s\n", stopItem.name, stopItem.meaning);
658 ide.outputView.debugBox.Logf(" %s:%d\n", (s = CopySystemPath(stopItem.frame.file)), stopItem.frame.line);
659 ide.outputView.Show();
660 ide.callStackView.Show();
663 else if(curEvent == breakEvent)
665 ide.threadsView.Show();
666 ide.callStackView.Show();
667 ide.callStackView.Activate();
670 if(monitor && curEvent.canBeMonitored)
672 SelectFrame(activeFrameLevel);
673 GoToStackFrameLine(activeFrameLevel, true);
674 ide.ShowCodeEditor();
675 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
680 EventHit(stopItem, bpInternal, bpUser);
687 if(userBreakOnInternalBreakpoint)
688 userBreakOnInternalBreakpoint = false;
693 #ifdef GDB_DEBUG_CONSOLE
694 char lastGdbOutput[GdbGetLineSize];
696 #if defined(__unix__)
697 ProgramThread progThread { };
700 void ChangeState(DebuggerState value)
702 bool same = value == state;
703 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ChangeState (", state, same ? " *** == *** " : " -> ", value, ")");
705 if(!same && ide) ide.AdjustDebugMenus();
710 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::CleanUp");
712 stackFrames.Free(Frame::Free);
722 waitingForPID = false;
727 sentBreakInsert = false;
728 ignoreBreakpoints = false;
729 userBreakOnInternalBreakpoint = false;
730 //runToCursorDebugStart = false;
733 activeFrameLevel = 0;
750 bpRunToCursor = null;
752 delete currentCompiler;
756 /*GdbThread gdbThread
762 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::constructor");
763 ideProcessId = Process_GetCurrentProcessId();
765 sysBPs.Add(Breakpoint { type = internalMain, function = "main", enabled = true, level = -1 });
766 #if defined(__WIN32__)
767 sysBPs.Add(Breakpoint { type = internalWinMain, function = "WinMain", enabled = true, level = -1 });
769 sysBPs.Add(Breakpoint { type = internalModulesLoaded, enabled = true, level = -1 });
770 sysBPs.Add(Breakpoint { type = internalModuleLoad, function = "InternalModuleLoadBreakpoint", enabled = true, level = -1 });
775 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::destructor");
783 property bool isActive { get { return state == running || state == stopped; } }
784 property bool isPrepared { get { return state == loaded || state == running || state == stopped; } }
788 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Resume");
789 GdbExecContinue(true);
794 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Break");
798 GdbDebugBreak(false);
804 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Stop");
811 GdbDebugBreak(false);
825 void Restart(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
827 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Restart");
828 if(StartSession(compiler, config, bitDepth, useValgrind, true, false, false/*, false*/) == loaded)
832 bool GoToCodeLine(char * location)
835 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GoToCodeLine(", location, ")");
836 codloc = CodeLocation::ParseCodeLocation(location);
839 CodeEditor editor = (CodeEditor)ide.OpenFile(codloc.absoluteFile, normal, true, null, no, normal, false);
842 EditBox editBox = editor.editBox;
843 editBox.GoToLineNum(codloc.line - 1);
844 editBox.GoToPosition(editBox.line, codloc.line - 1, 0);
851 bool GoToStackFrameLine(int stackLevel, bool askForLocation)
853 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GoToStackFrameLine(", stackLevel, ", ", askForLocation, ")");
856 char filePath[MAX_LOCATION];
857 char sourceDir[MAX_LOCATION];
859 CodeEditor editor = null;
860 if(stackLevel == -1) // this (the two lines) is part of that fix that I would not put in for some time
862 for(frame = stackFrames.first; frame; frame = frame.next)
863 if(frame.level == stackLevel)
867 ide.callStackView.Show();
869 if(!frame.absoluteFile && frame.file)
870 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
871 if(!frame.absoluteFile && askForLocation && frame.file)
874 char title[MAX_LOCATION];
875 snprintf(title, sizeof(title), $"Provide source file location for %s", (s = CopySystemPath(frame.file)));
876 title[sizeof(title)-1] = 0;
878 if(SourceDirDialog(title, ide.workspace.projectDir, frame.file, sourceDir))
880 AddSourceDir(sourceDir);
881 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
884 if(frame.absoluteFile)
885 editor = (CodeEditor)ide.OpenFile(frame.absoluteFile, normal, true, null, no, normal, false);
887 if(editor && frame.line)
889 EditBox editBox = editor.editBox;
890 editBox.GoToLineNum(frame.line - 1);
891 editBox.GoToPosition(editBox.line, frame.line - 1, 0);
899 void SelectThread(int thread)
901 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SelectThread(", thread, ")");
904 if(thread != activeThread)
906 activeFrameLevel = -1;
907 ide.callStackView.Clear();
908 GdbCommand(false, "-thread-select %d", thread);
910 // Why was SelectFrame missing here?
911 SelectFrame(activeFrameLevel);
912 GoToStackFrameLine(activeFrameLevel, true);
913 WatchesCodeEditorLinkRelease();
914 WatchesCodeEditorLinkInit();
918 ide.callStackView.Show();
922 void SelectFrame(int frame)
924 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SelectFrame(", frame, ")");
927 if(frame != activeFrameLevel || !codeEditor || !codeEditor.visible)
929 activeFrameLevel = frame; // there is no active frame number in the gdb reply
930 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
931 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
932 if(activeFrame.level == activeFrameLevel)
935 WatchesCodeEditorLinkRelease();
936 WatchesCodeEditorLinkInit();
943 void HandleExit(char * reason, char * code)
945 bool returnedExitCode = false;
946 char verboseExitCode[128];
948 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::HandleExit(", reason, ", ", code, ")");
949 ChangeState(loaded); // this state change seems to be superfluous, might be in case of gdb crash
954 snprintf(verboseExitCode, sizeof(verboseExitCode), $" with exit code %s", code);
955 verboseExitCode[sizeof(verboseExitCode)-1] = 0;
958 verboseExitCode[0] = '\0';
962 // ClearBreakDisplay();
966 for(wh : ide.workspace.watches)
968 if(wh.type) FreeType(wh.type);
971 ide.watchesView.UpdateWatch(wh);
975 #if defined(__unix__)
978 progThread.terminate = true;
981 fifoFile.CloseInput();
991 char program[MAX_LOCATION];
992 GetSystemPathBuffer(program, targetFile);
994 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
995 else if(!strcmp(reason, "exited-normally"))
996 ide.outputView.debugBox.Logf($"The program %s has exited normally%s.\n", program, verboseExitCode);
997 else if(!strcmp(reason, "exited"))
998 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
999 else if(!strcmp(reason, "exited-signalled"))
1000 ide.outputView.debugBox.Logf($"The program %s has exited with a signal%s.\n", program, verboseExitCode);
1002 ide.outputView.debugBox.Logf($"The program %s has exited (gdb provided an unknown reason)%s.\n", program, verboseExitCode);
1007 DebuggerState StartSession(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool restart, bool userBreakOnInternalBreakpoint, bool ignoreBreakpoints/*, bool runToCursorDebugStart*/)
1009 DebuggerState result = none;
1010 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StartSession(restart(", restart, "), userBreakOnInternalBreakpoint(", userBreakOnInternalBreakpoint, "), ignoreBreakpoints(", ignoreBreakpoints, ")"/*, runToCursorDebugStart(", runToCursorDebugStart, ")"*/);
1011 if(restart && state == running && targetProcessId)
1013 breakType = DebuggerAction::restart;
1014 GdbDebugBreak(false);
1018 if(restart && state == stopped)
1020 if(needReset && state == loaded)
1021 GdbExit(); // this reset is to get a clean state with all the breakpoints until a better state can be maintained on program exit
1023 if(result == none || result == terminated)
1025 ide.outputView.ShowClearSelectTab(debug);
1026 ide.outputView.debugBox.Logf($"Starting debug mode\n");
1033 for(bp : ide.workspace.breakpoints)
1039 //this.runToCursorDebugStart = runToCursorDebugStart;
1041 if(GdbInit(compiler, config, bitDepth, useValgrind))
1046 this.ignoreBreakpoints = ignoreBreakpoints;
1047 this.userBreakOnInternalBreakpoint = userBreakOnInternalBreakpoint;
1048 if(ignoreBreakpoints && (result == loaded || result == stopped))
1049 GdbBreakpointsDelete(false, false);
1054 void Start(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
1056 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Start()");
1057 if(StartSession(compiler, config, bitDepth, useValgrind, true, false, false/*, false*/) == loaded)
1061 void StepInto(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
1063 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepInto()");
1064 switch(StartSession(compiler, config, bitDepth, useValgrind, false, true, false/*, false*/))
1066 case loaded: GdbExecRun(); break;
1067 case stopped: GdbExecStep(); break;
1071 void StepOver(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool ignoreBreakpoints)
1073 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepOver()");
1074 switch(StartSession(compiler, config, bitDepth, useValgrind, false, true, ignoreBreakpoints/*, false*/))
1076 case loaded: GdbExecRun(); break;
1077 case stopped: GdbExecNext(); break;
1081 void StepOut(bool ignoreBreakpoints)
1083 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepOut()");
1084 if(state == stopped)
1086 this.ignoreBreakpoints = ignoreBreakpoints;
1087 if(ignoreBreakpoints)
1088 GdbBreakpointsDelete(true, false);
1093 void RunToCursor(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, char * absoluteFilePath, int lineNumber, bool ignoreBreakpoints, bool atSameLevel)
1095 char relativeFilePath[MAX_LOCATION];
1096 DebuggerState st = state;
1097 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::RunToCursor()");
1100 // ide.outputView.ShowClearSelectTab(debug);
1101 // ide.outputView.debugBox.Logf($"Starting debug mode\n");
1103 if(!ide.projectView.project.GetRelativePath(absoluteFilePath, relativeFilePath))
1104 strcpy(relativeFilePath, absoluteFilePath);
1106 if(bpRunToCursor && bpRunToCursor.inserted && symbols)
1108 UnsetBreakpoint(bpRunToCursor);
1109 delete bpRunToCursor;
1112 bpRunToCursor = Breakpoint { };
1113 bpRunToCursor.absoluteFilePath = absoluteFilePath;
1114 bpRunToCursor.relativeFilePath = relativeFilePath;
1115 bpRunToCursor.line = lineNumber;
1116 bpRunToCursor.type = runToCursor;
1117 bpRunToCursor.enabled = true;
1118 bpRunToCursor.level = atSameLevel ? frameCount - activeFrameLevel -1 : -1;
1120 switch(StartSession(compiler, config, bitDepth, useValgrind, false, false, ignoreBreakpoints/*, true*/))
1126 GdbExecContinue(true);
1131 void GetCallStackCursorLine(bool * error, int * lineCursor, int * lineTopFrame)
1133 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetCallStackCursorLine()");
1134 if(activeFrameLevel == -1)
1142 *error = signalOn && activeThread == signalThread;
1143 *lineCursor = activeFrameLevel - ((frameCount > 192 && activeFrameLevel > 191) ? frameCount - 192 - 1 : 0) + 1;
1144 *lineTopFrame = activeFrameLevel ? 1 : 0;
1148 int GetMarginIconsLineNumbers(char * fileName, int lines[], bool enabled[], int max, bool * error, int * lineCursor, int * lineTopFrame)
1150 char winFilePath[MAX_LOCATION];
1151 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1153 Iterator<Breakpoint> it { ide.workspace.breakpoints };
1154 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetMarginIconsLineNumbers()");
1155 while(it.Next() && count < max)
1157 Breakpoint bp = it.data;
1160 if(bp.absoluteFilePath && bp.absoluteFilePath[0] && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1162 lines[count] = bp.line;
1163 enabled[count] = bp.enabled;
1168 if(activeFrameLevel == -1)
1176 *error = signalOn && activeThread == signalThread;
1177 if(activeFrame && activeFrame.absoluteFile && !fstrcmp(absoluteFilePath, activeFrame.absoluteFile))
1178 *lineCursor = activeFrame.line;
1181 if(activeFrame && stopItem && stopItem.frame && activeFrame.level == stopItem.frame.level)
1183 else if(stopItem && stopItem.frame && stopItem.frame.absoluteFile && !fstrcmp(absoluteFilePath, stopItem.frame.absoluteFile))
1184 *lineTopFrame = stopItem.frame.line;
1188 if(*lineTopFrame == *lineCursor && *lineTopFrame)
1194 void ChangeWatch(DataRow row, char * expression)
1196 Watch wh = (Watch)row.tag;
1197 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ChangeWatch(", expression, ")");
1200 delete wh.expression;
1202 wh.expression = CopyString(expression);
1205 Iterator<Watch> it { ide.workspace.watches };
1207 ide.workspace.watches.Delete(it.pointer);
1213 row.tag = (int64)wh;
1214 ide.workspace.watches.Add(wh);
1216 wh.expression = CopyString(expression);
1218 ide.workspace.Save();
1219 //if(expression && state == stopped)
1224 void MoveIcons(char * fileName, int lineNumber, int move, bool start)
1226 char winFilePath[MAX_LOCATION];
1227 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1230 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::MoveIcons()");
1231 for(bpLink = ide.workspace.breakpoints.first; bpLink; bpLink = next)
1233 Breakpoint bp = (Breakpoint)bpLink.data;
1236 if(bp.type == user && bp.absoluteFilePath && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1238 if(bp.line > lineNumber || (bp.line == lineNumber && start))
1240 if(move < 0 && (bp.line < lineNumber - move))
1241 ide.workspace.RemoveBreakpoint(bp);
1245 ide.breakpointsView.UpdateBreakpoint(bp.row);
1246 ide.workspace.Save();
1252 // moving code cursors is futile, on next step, stop, hit, cursors will be offset anyways
1255 bool SourceDirDialog(char * title, char * startDir, char * test, char * sourceDir)
1259 String srcDir = null;
1261 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SourceDirDialog()");
1262 debuggerFileDialog.text = title;
1263 debuggerFileDialog.currentDirectory = startDir;
1264 debuggerFileDialog.master = ide;
1266 while(debuggerFileDialog.Modal())
1268 strcpy(sourceDir, debuggerFileDialog.filePath);
1269 if(!fstrcmp(ide.workspace.projectDir, sourceDir) &&
1270 MessageBox { type = yesNo, master = ide,
1271 contents = $"This is the project directory.\nWould you like to try again?",
1272 text = $"Invalid Source Directory" }.Modal() == no)
1276 for(dir : ide.workspace.sourceDirs)
1278 if(!fstrcmp(dir, sourceDir))
1286 MessageBox { type = yesNo, master = ide,
1287 contents = $"This source directory is already specified.\nWould you like to try again?",
1288 text = $"Invalid Source Directory" }.Modal() == no)
1294 char file[MAX_LOCATION];
1295 strcpy(file, sourceDir);
1296 PathCat(file, test);
1297 result = FileExists(file);
1299 MessageBox { type = yesNo, master = ide,
1300 contents = $"Unable to locate source file.\nWould you like to try again?",
1301 text = $"Invalid Source Directory" }.Modal() == no)
1315 void AddSourceDir(char * sourceDir)
1317 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::AddSourceDir(", sourceDir, ")");
1318 ide.workspace.sourceDirs.Add(CopyString(sourceDir));
1319 ide.workspace.Save();
1323 DebuggerState oldState = state;
1328 GdbDebugBreak(true);
1331 GdbCommand(false, "-environment-directory \"%s\"", sourceDir);
1334 if(oldState == running)
1335 GdbExecContinue(false);
1339 void ToggleBreakpoint(char * fileName, int lineNumber, Project prj)
1341 char winFilePath[MAX_LOCATION];
1342 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1343 char absolutePath[MAX_LOCATION];
1344 char relativePath[MAX_LOCATION];
1345 char sourceDir[MAX_LOCATION];
1346 Breakpoint bp = null;
1348 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ToggleBreakpoint(", fileName, ":", lineNumber, ")");
1349 strcpy(absolutePath, absoluteFilePath);
1350 for(i : ide.workspace.breakpoints; i.type == user && i.absoluteFilePath && !fstrcmp(i.absoluteFilePath, absolutePath) && i.line == lineNumber)
1359 ide.workspace.RemoveBreakpoint(bp);
1367 // FIXED: This is how it should have been... Source locations are only for files not in project
1368 // if(IsPathInsideOf(absolutePath, ide.workspace.projectDir))
1369 // MakePathRelative(absolutePath, ide.workspace.projectDir, relativePath);
1370 bool result = false;
1372 result = prj.GetRelativePath(absolutePath, relativePath);
1374 result = ide.projectView.project.GetRelativePath(absolutePath, relativePath);
1375 //if(ide.projectView.project.GetRelativePath(absolutePath, relativePath));
1379 char title[MAX_LOCATION];
1380 char directory[MAX_LOCATION];
1381 StripLastDirectory(absolutePath, directory);
1382 snprintf(title, sizeof(title), $"Provide source files location directory for %s", absolutePath);
1383 title[sizeof(title)-1] = 0;
1386 String srcDir = null;
1387 for(dir : ide.workspace.sourceDirs)
1389 if(IsPathInsideOf(absolutePath, dir))
1391 MakePathRelative(absoluteFilePath, dir, relativePath);
1399 if(SourceDirDialog(title, directory, null, sourceDir))
1401 if(IsPathInsideOf(absolutePath, sourceDir))
1403 AddSourceDir(sourceDir);
1404 MakePathRelative(absoluteFilePath, sourceDir, relativePath);
1407 else if(MessageBox { type = yesNo, master = ide,
1408 contents = $"You must provide a valid source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1409 text = $"Invalid Source Directory" }.Modal() == no)
1412 else if(MessageBox { type = yesNo, master = ide,
1413 contents = $"You must provide a source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1414 text = $"No Source Directory Provided" }.Modal() == no)
1418 ide.workspace.bpCount++;
1419 bp = { line = lineNumber, type = user, enabled = true, level = -1 };
1420 ide.workspace.breakpoints.Add(bp);
1421 bp.absoluteFilePath = absolutePath;
1422 bp.relativeFilePath = relativePath;
1423 ide.breakpointsView.AddBreakpoint(bp);
1428 DebuggerState oldState = state;
1433 GdbDebugBreak(true);
1436 SetBreakpoint(bp, false);
1439 if(oldState == running)
1440 GdbExecContinue(false);
1443 ide.workspace.Save();
1446 void UpdateRemovedBreakpoint(Breakpoint bp)
1448 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::UpdateRemovedBreakpoint()");
1449 if(targeted && bp.inserted)
1451 DebuggerState oldState = state;
1456 GdbDebugBreak(true);
1459 UnsetBreakpoint(bp);
1462 if(oldState == running)
1463 GdbExecContinue(false);
1469 void ParseFrame(Frame frame, char * string)
1472 Array<char *> frameTokens { minAllocSize = 50 };
1473 Array<char *> argsTokens { minAllocSize = 50 };
1474 Array<char *> argumentTokens { minAllocSize = 50 };
1475 DebugListItem item { };
1478 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ParseFrame()");
1479 TokenizeList(string, ',', frameTokens);
1480 for(i = 0; i < frameTokens.count; i++)
1482 if(TokenizeListItem(frameTokens[i], item))
1484 StripQuotes(item.value, item.value);
1485 if(!strcmp(item.name, "level"))
1486 frame.level = atoi(item.value);
1487 else if(!strcmp(item.name, "addr"))
1488 frame.addr = item.value;
1489 else if(!strcmp(item.name, "func"))
1490 frame.func = item.value;
1491 else if(!strcmp(item.name, "args"))
1493 if(!strcmp(item.value, "[]"))
1494 frame.argsCount = 0;
1497 item.value = StripBrackets(item.value);
1498 TokenizeList(item.value, ',', argsTokens);
1499 for(j = 0; j < argsTokens.count; j++)
1501 argsTokens[j] = StripCurlies(argsTokens[j]);
1502 TokenizeList(argsTokens[j], ',', argumentTokens);
1503 for(k = 0; k < argumentTokens.count; k++)
1506 frame.args.Add(arg);
1507 if(TokenizeListItem(argumentTokens[k], item))
1509 if(!strcmp(item.name, "name"))
1511 StripQuotes(item.value, item.value);
1512 arg.name = item.value;
1514 else if(!strcmp(item.name, "value"))
1516 StripQuotes(item.value, item.value);
1517 arg.val = item.value;
1520 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "frame args item (", item.name, "=", item.value, ") is unheard of");
1523 _dpl(0, "Bad frame args item");
1525 argumentTokens.RemoveAll();
1527 frame.argsCount = argsTokens.count;
1528 argsTokens.RemoveAll();
1531 else if(!strcmp(item.name, "from"))
1532 frame.from = item.value;
1533 else if(!strcmp(item.name, "file"))
1534 frame.file = item.value;
1535 else if(!strcmp(item.name, "line"))
1536 frame.line = atoi(item.value);
1537 else if(!strcmp(item.name, "fullname"))
1538 frame.absoluteFile = item.value;
1540 // GDB 6.3 on OS X is giving "fullname" and "dir", all in absolute, but file name only in 'file'
1541 String path = ide.workspace.GetPathWorkspaceRelativeOrAbsolute(item.value);
1542 if(strcmp(frame.file, path))
1545 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1550 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "frame member (", item.name, "=", item.value, ") is unheard of");
1553 _dpl(0, "Bad frame");
1558 delete argumentTokens;
1562 Breakpoint GetBreakpointById(int id, bool * isInternal)
1564 Breakpoint bp = null;
1565 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetBreakpointById(", id, ")");
1567 *isInternal = false;
1570 for(i : sysBPs; i.bp && i.bp.id == id)
1577 if(!bp && bpRunToCursor && bpRunToCursor.bp && bpRunToCursor.bp.id == id)
1581 for(i : ide.workspace.breakpoints; i.bp && i.bp.id == id)
1591 GdbDataBreakpoint ParseBreakpoint(char * string, Array<char *> outTokens)
1594 GdbDataBreakpoint bp { };
1595 DebugListItem item { };
1596 Array<char *> bpTokens { minAllocSize = 16 };
1597 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ParseBreakpoint()");
1598 string = StripCurlies(string);
1599 TokenizeList(string, ',', bpTokens);
1600 for(i = 0; i < bpTokens.count; i++)
1602 if(TokenizeListItem(bpTokens[i], item))
1604 StripQuotes(item.value, item.value);
1605 if(!strcmp(item.name, "number"))
1607 if(!strchr(item.value, '.'))
1608 bp.id = atoi(item.value);
1609 bp.number = item.value;
1611 else if(!strcmp(item.name, "type"))
1612 bp.type = item.value;
1613 else if(!strcmp(item.name, "disp"))
1614 bp.disp = item.value;
1615 else if(!strcmp(item.name, "enabled"))
1616 bp.enabled = (!strcmpi(item.value, "y"));
1617 else if(!strcmp(item.name, "addr"))
1619 if(outTokens && !strcmp(item.value, "<MULTIPLE>"))
1622 Array<GdbDataBreakpoint> bpArray = bp.multipleBPs = { };
1623 while(outTokens.count > ++c)
1625 GdbDataBreakpoint multBp = ParseBreakpoint(outTokens[c], null);
1626 bpArray.Add(multBp);
1630 bp.addr = item.value;
1632 else if(!strcmp(item.name, "func"))
1633 bp.func = item.value;
1634 else if(!strcmp(item.name, "file"))
1635 bp.file = item.value;
1636 else if(!strcmp(item.name, "fullname"))
1637 bp.fullname = item.value;
1638 else if(!strcmp(item.name, "line"))
1639 bp.line = atoi(item.value);
1640 else if(!strcmp(item.name, "at"))
1642 else if(!strcmp(item.name, "times"))
1643 bp.times = atoi(item.value);
1644 else if(!strcmp(item.name, "original-location") || !strcmp(item.name, "thread-groups"))
1645 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "breakpoint member (", item.name, "=", item.value, ") is ignored");
1647 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "breakpoint member (", item.name, "=", item.value, ") is unheard of");
1653 void ShowDebuggerViews()
1655 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ShowDebuggerViews()");
1656 ide.outputView.Show();
1657 ide.outputView.SelectTab(debug);
1658 ide.threadsView.Show();
1659 ide.callStackView.Show();
1660 ide.watchesView.Show();
1664 void HideDebuggerViews()
1666 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::HideDebuggerViews()");
1667 ide.RepositionWindows(true);
1670 void ::GdbCommand(bool focus, char * format, ...)
1674 // TODO: Improve this limit
1675 static char string[MAX_F_STRING*4];
1677 va_start(args, format);
1678 vsnprintf(string, sizeof(string), format, args);
1679 string[sizeof(string)-1] = 0;
1683 ide.debugger.serialSemaphore.TryWait();
1685 #ifdef GDB_DEBUG_CONSOLE
1686 _dpl2(_dpct, dplchan::gdbCommand, 0, string);
1688 #ifdef GDB_DEBUG_OUTPUT
1689 ide.outputView.gdbBox.Logf("cmd: %s\n", string);
1691 #ifdef GDB_DEBUG_GUI
1693 ide.gdbDialog.AddCommand(string);
1696 strcat(string,"\n");
1697 gdbHandle.Puts(string);
1700 Process_ShowWindows(targetProcessId);
1703 ide.debugger.serialSemaphore.Wait();
1708 bool ValidateBreakpoint(Breakpoint bp)
1710 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ValidateBreakpoint()");
1711 if(modules && bp.bp)
1713 if(bp.bp.line != bp.line)
1719 ide.outputView.debugBox.Logf("WOULD HAVE -- Invalid breakpoint disabled: %s:%d\n", bp.relativeFilePath, bp.line);
1721 //UnsetBreakpoint(bp);
1722 //bp.enabled = false;
1728 ide.outputView.debugBox.Logf("Debugger Error: ValidateBreakpoint error\n");
1729 bp.line = bp.bp.line;
1736 void GdbBreakpointsInsert()
1738 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbBreakpointsInsert()");
1741 DirExpression objDir = ide.project.GetObjDir(currentCompiler, prjConfig, bitDepth);
1742 for(bp : sysBPs; !bp.inserted)
1744 bool insert = false;
1745 if(bp.type == internalModulesLoaded)
1747 char path[MAX_LOCATION];
1748 char name[MAX_LOCATION];
1749 char fixedModuleName[MAX_FILENAME];
1752 bool moduleLoadBlock = false;
1754 ReplaceSpaces(fixedModuleName, ide.project.moduleName);
1755 snprintf(name, sizeof(name),"%s.main.ec", fixedModuleName);
1756 name[sizeof(name)-1] = 0;
1757 strcpy(path, ide.workspace.projectDir);
1758 PathCatSlash(path, objDir.dir);
1759 PathCatSlash(path, name);
1760 f = FileOpen(path, read);
1763 for(lineNumber = 1; !f.Eof(); lineNumber++)
1765 if(f.GetLine(line, sizeof(line) - 1))
1767 bool moduleLoadLine;
1768 TrimLSpaces(line, line);
1769 moduleLoadLine = !strncmp(line, "eModule_Load", strlen("eModule_Load"));
1770 if(!moduleLoadBlock && moduleLoadLine)
1771 moduleLoadBlock = true;
1772 else if(moduleLoadBlock && !moduleLoadLine && strlen(line) > 0)
1778 char relative[MAX_LOCATION];
1779 bp.absoluteFilePath = path;
1780 MakePathRelative(path, ide.workspace.projectDir, relative);
1781 bp.relativeFilePath = relative;
1782 bp.line = lineNumber;
1788 else if(bp.type == internalModuleLoad)
1792 for(prj : ide.workspace.projects)
1794 if(!strcmp(prj.moduleName, "ecere"))
1796 ProjectNode node = prj.topNode.Find("instance.c", false);
1799 char path[MAX_LOCATION];
1800 char relative[MAX_LOCATION];
1801 node.GetFullFilePath(path);
1802 bp.absoluteFilePath = path;
1803 MakePathRelative(path, prj.topNode.path, relative);
1804 bp.relativeFilePath = relative;
1815 SetBreakpoint(bp, false);
1819 if(bpRunToCursor && !bpRunToCursor.inserted)
1820 SetBreakpoint(bpRunToCursor, false);
1822 if(!ignoreBreakpoints)
1824 for(bp : ide.workspace.breakpoints; !bp.inserted && bp.type == user)
1828 if(!SetBreakpoint(bp, false))
1829 SetBreakpoint(bp, true);
1837 bp.bp = GdbDataBreakpoint { };
1844 void UnsetBreakpoint(Breakpoint bp)
1846 char * s; _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::UnsetBreakpoint(", s=bp.CopyLocationString(false), ")"); delete s;
1847 if(symbols && bp.inserted)
1849 GdbCommand(false, "-break-delete %s", bp.bp.number);
1850 bp.inserted = false;
1855 bool SetBreakpoint(Breakpoint bp, bool removePath)
1857 char * s; _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SetBreakpoint(", s=bp.CopyLocationString(false), ", ", removePath ? "**** removePath(true) ****" : "", ")"); delete s;
1858 breakpointError = false;
1861 char * location = bp.CopyLocationString(removePath);
1862 sentBreakInsert = true;
1863 GdbCommand(false, "-break-insert %s", location);
1865 if(!breakpointError)
1867 if(bpItem.multipleBPs && bpItem.multipleBPs.count)
1870 GdbDataBreakpoint first = null;
1871 for(n : bpItem.multipleBPs)
1873 if(!fstrcmp(n.fullname, bp.absoluteFilePath))
1883 GdbCommand(false, "-break-disable %s", n.number);
1887 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error breakpoint already disabled.");
1892 bpItem.addr = first.addr;
1893 bpItem.func = first.func;
1894 bpItem.file = first.file;
1895 bpItem.fullname = first.fullname;
1896 bpItem.line = first.line;
1897 //bpItem.thread-groups = first.thread-groups;
1898 bpItem.multipleBPs.Free();
1899 delete bpItem.multipleBPs;
1902 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error multiple breakpoints all disabled.");
1904 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error multiple breakpoints in exact same file not supported.");
1908 bp.inserted = (bp.bp && bp.bp.number && strcmp(bp.bp.number, "0"));
1910 ValidateBreakpoint(bp);
1911 /*if(bp == bpRunToCursor)
1912 runToCursorDebugStart = false;*/
1915 return !breakpointError;
1918 void GdbBreakpointsDelete(bool deleteRunToCursor, bool deleteInternalBreakpoints)
1920 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbBreakpointsDelete(deleteRunToCursor(", deleteRunToCursor, "))");
1923 if(deleteInternalBreakpoints)
1926 UnsetBreakpoint(bp);
1928 for(bp : ide.workspace.breakpoints)
1929 UnsetBreakpoint(bp);
1930 if(deleteRunToCursor && bpRunToCursor)
1931 UnsetBreakpoint(bpRunToCursor);
1937 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbGetStack()");
1939 stackFrames.Free(Frame::Free);
1940 GdbCommand(false, "-stack-info-depth");
1942 GdbCommand(false, "-stack-info-depth 192");
1943 if(frameCount && frameCount <= 192)
1944 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 191));
1947 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 95));
1948 GdbCommand(false, "-stack-list-frames %d %d", Max(frameCount - 96, 96), frameCount - 1);
1950 GdbCommand(false, "");
1955 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbTargetSet()");
1958 char escaped[MAX_LOCATION];
1959 strescpy(escaped, targetFile);
1960 GdbCommand(false, "file \"%s\"", escaped); //GDB/MI Missing Implementation -symbol-file, -target-attach
1967 const char *vgdbCommand = "/usr/bin/vgdb"; // TODO: vgdb command config option
1968 //GdbCommand(false, "-target-select remote | %s --pid=%d", "vgdb", targetProcessId);
1969 printf("target remote | %s --pid=%d\n", vgdbCommand, targetProcessId);
1970 GdbCommand(false, "target remote | %s --pid=%d", vgdbCommand, targetProcessId); // TODO: vgdb command config option
1973 /*for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
1974 GdbCommand(false, "-environment-directory \"%s\"", prj.topNode.path);*/
1976 for(dir : ide.workspace.sourceDirs; dir && dir[0])
1978 bool interference = false;
1979 for(prj : ide.workspace.projects)
1981 if(!fstrcmp(prj.topNode.path, dir))
1983 interference = true;
1987 if(!interference && dir[0])
1988 GdbCommand(false, "-environment-directory \"%s\"", dir);
1996 /*void GdbTargetRelease()
2000 GdbBreakpointsDelete(true, true);
2001 GdbCommand(false, "file"); //GDB/MI Missing Implementation -target-detach
2007 void GdbDebugBreak(bool internal)
2009 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbDebugBreak()");
2013 breakType = DebuggerAction::internal;
2015 if(ide) ide.Update(null);
2017 if(Process_Break(targetProcessId)) //GdbCommand(false, "-exec-interrupt");
2018 serialSemaphore.Wait();
2021 ChangeState(loaded);
2022 targetProcessId = 0;
2027 ide.outputView.debugBox.Logf("Debugger Error: GdbDebugBreak with not target id should never happen\n");
2032 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecRun()");
2035 ShowDebuggerViews();
2037 GdbCommand(true, "-exec-continue");
2039 GdbCommand(true, "-exec-run");
2042 void GdbExecContinue(bool focus)
2044 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecContinue()");
2046 GdbCommand(focus, "-exec-continue");
2051 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecNext()");
2053 GdbCommand(true, "-exec-next");
2058 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecStep()");
2060 GdbCommand(true, "-exec-step");
2063 void GdbExecFinish()
2065 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecFinish()");
2067 GdbCommand(true, "-exec-finish");
2070 void GdbExecCommon()
2072 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecCommon()");
2073 ClearBreakDisplay();
2074 GdbBreakpointsInsert();
2077 #ifdef GDB_DEBUG_GUI
2078 void SendGDBCommand(char * command)
2080 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SendGDBCommand()");
2081 DebuggerState oldState = state;
2086 GdbDebugBreak(true);
2089 GdbCommand(false, command);
2092 if(oldState == running)
2093 GdbExecContinue(false);
2097 void ClearBreakDisplay()
2099 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ClearBreakDisplay()");
2101 activeFrameLevel = -1;
2111 stackFrames.Free(Frame::Free);
2112 WatchesCodeEditorLinkRelease();
2113 ide.callStackView.Clear();
2114 ide.threadsView.Clear();
2120 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbAbortExec()");
2122 GdbCommand(false, "-interpreter-exec console \"kill\""); // should use -exec-abort -- GDB/MI implementation incomplete
2126 bool GdbInit(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
2129 char oldDirectory[MAX_LOCATION];
2130 char tempPath[MAX_LOCATION];
2131 char command[MAX_F_STRING*4];
2132 bool vgFullLeakCheck = ide.workspace.vgFullLeakCheck;
2133 Project project = ide.project;
2134 DirExpression targetDirExp = project.GetTargetDir(compiler, config, bitDepth);
2135 PathBackup pathBackup { };
2137 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbInit()");
2138 if(currentCompiler != compiler)
2140 delete currentCompiler;
2141 currentCompiler = compiler;
2142 incref currentCompiler;
2145 this.bitDepth = bitDepth;
2146 usingValgrind = useValgrind;
2148 ChangeState(loaded);
2150 sentBreakInsert = false;
2151 breakpointError = false;
2152 ignoreBreakpoints = false;
2158 ide.outputView.ShowClearSelectTab(debug);
2159 ide.outputView.debugBox.Logf($"Starting debug mode\n");
2161 #ifdef GDB_DEBUG_OUTPUT
2162 ide.outputView.gdbBox.Logf("run: Starting GDB\n");
2165 strcpy(tempPath, ide.workspace.projectDir);
2166 PathCatSlash(tempPath, targetDirExp.dir);
2168 targetDir = CopyString(tempPath);
2169 project.CatTargetFileName(tempPath, compiler, config);
2171 targetFile = CopyString(tempPath);
2173 GetWorkingDir(oldDirectory, MAX_LOCATION);
2174 if(ide.workspace.debugDir && ide.workspace.debugDir[0])
2176 char temp[MAX_LOCATION];
2177 strcpy(temp, ide.workspace.projectDir);
2178 PathCatSlash(temp, ide.workspace.debugDir);
2179 ChangeWorkingDir(temp);
2182 ChangeWorkingDir(ide.workspace.projectDir);
2184 ide.SetPath(true, compiler, config, bitDepth);
2186 // TODO: This pollutes the environment, but at least it works
2187 // It shouldn't really affect the IDE as the PATH gets restored and other variables set for testing will unlikely cause problems
2188 // What is the proper solution for this? DualPipeOpenEnv?
2189 // gdb set environment commands don't seem to take effect
2190 for(e : ide.workspace.environmentVars)
2192 SetEnvironment(e.name, e.string);
2197 char * clArgs = ide.workspace.commandLineArgs;
2198 const char *valgrindCommand = "valgrind"; // TODO: valgrind command config option //TODO: valgrind options
2199 vgLogFile = CreateTemporaryFile(vgLogPath, "ecereidevglog");
2203 vgLogThread.Create();
2207 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't open temporary log file for Valgrind output\n");
2210 if(result && !CheckCommandAvailable(valgrindCommand))
2212 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for Valgrind is not available.\n", valgrindCommand);
2217 sprintf(command, "%s --vgdb=yes --vgdb-error=0 --log-file=%s%s %s%s%s",
2218 valgrindCommand, vgLogPath, vgFullLeakCheck ? " --leak-check=full" : "", targetFile, clArgs ? " " : "", clArgs ? clArgs : "");
2219 vgTargetHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
2222 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start Valgrind\n");
2228 incref vgTargetHandle;
2229 vgTargetThread.Create();
2231 targetProcessId = vgTargetHandle.GetProcessID();
2232 waitingForPID = false;
2233 if(!targetProcessId)
2235 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get Valgrind process ID\n");
2242 serialSemaphore.Wait();
2250 (compiler.targetPlatform == win32 && bitDepth == 64) ? "x86_64-w64-mingw32-gdb" :
2251 (compiler.targetPlatform == win32 && bitDepth == 32) ? "i686-w64-mingw32-gdb" :
2253 if(!CheckCommandAvailable(command))
2255 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for GDB is not available.\n", command);
2260 strcat(command, " -n -silent --interpreter=mi2"); //-async //\"%s\"
2262 gdbHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
2265 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start GDB\n");
2275 gdbProcessId = gdbHandle.GetProcessID();
2278 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get GDB process ID\n");
2285 serialSemaphore.Wait();
2288 GdbCommand(false, "-gdb-set verbose off");
2289 //GdbCommand(false, "-gdb-set exec-done-display on");
2290 GdbCommand(false, "-gdb-set step-mode off");
2291 GdbCommand(false, "-gdb-set unwindonsignal on");
2292 //GdbCommand(false, "-gdb-set shell on");
2293 GdbCommand(false, "set print elements 992");
2294 GdbCommand(false, "-gdb-set backtrace limit 100000");
2298 //ChangeState(terminated);
2304 #if defined(__unix__)
2306 CreateTemporaryDir(progFifoDir, "ecereide");
2307 strcpy(progFifoPath, progFifoDir);
2308 PathCat(progFifoPath, "ideprogfifo");
2309 if(!mkfifo(progFifoPath, 0600))
2311 //fileCreated = true;
2316 ide.outputView.debugBox.Logf(createFIFOMsg, progFifoPath);
2323 progThread.terminate = false;
2324 progThread.Create();
2328 #if defined(__WIN32__)
2329 GdbCommand(false, "-gdb-set new-console on");
2332 #if defined(__unix__)
2334 GdbCommand(false, "-inferior-tty-set %s", progFifoPath);
2338 GdbCommand(false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
2340 for(e : ide.workspace.environmentVars)
2342 GdbCommand(false, "set environment %s=%s", e.name, e.string);
2347 ChangeWorkingDir(oldDirectory);
2353 delete targetDirExp;
2359 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExit()");
2360 if(gdbHandle && gdbProcessId)
2362 GdbCommand(false, "-gdb-exit");
2377 ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
2383 for(bp : ide.workspace.breakpoints)
2385 bp.inserted = false;
2391 bp.inserted = false;
2396 bpRunToCursor.inserted = false;
2397 delete bpRunToCursor.bp;
2400 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2401 ClearBreakDisplay();
2404 #if defined(__unix__)
2405 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
2407 progThread.terminate = true;
2410 fifoFile.CloseInput();
2416 DeleteFile(progFifoPath);
2417 progFifoPath[0] = '\0';
2423 void WatchesCodeEditorLinkInit()
2425 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkInit()");
2427 char tempPath[MAX_LOCATION];
2428 char path[MAX_LOCATION];
2430 //void MakeFilePathProjectRelative(char * path, char * relativePath)
2431 if(!ide.projectView.project.GetRelativePath(activeFrame.file, tempPath))
2432 strcpy(tempPath, activeFrame.file);
2434 strcpy(path, ide.workspace.projectDir);
2435 PathCat(path, tempPath);
2436 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2439 for(srcDir : ide.workspace.sourceDirs)
2441 strcpy(path, srcDir);
2442 PathCat(path, tempPath);
2443 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2444 if(codeEditor) break;
2449 /*if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2450 activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);*/
2451 if(!activeFrame || !activeFrame.absoluteFile)
2454 codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal, false);
2457 codeEditor.inUseDebug = true;
2460 //watchesInit = true;
2463 void WatchesCodeEditorLinkRelease()
2465 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkRelease()");
2470 codeEditor.inUseDebug = false;
2471 if(!codeEditor.visible)
2472 codeEditor.Destroy(0);
2478 bool ResolveWatch(Watch wh)
2480 bool result = false;
2482 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ResolveWatch()");
2494 char watchmsg[MAX_F_STRING];
2495 if(state == stopped && !codeEditor)
2496 wh.value = CopyString($"No source file found for selected frame");
2497 //if(codeEditor && state == stopped || state != stopped)
2500 Module backupPrivateModule;
2501 Context backupContext;
2502 Class backupThisClass;
2506 backupPrivateModule = GetPrivateModule();
2507 backupContext = GetCurrentContext();
2508 backupThisClass = GetThisClass();
2511 SetPrivateModule(codeEditor.privateModule);
2512 SetCurrentContext(codeEditor.globalContext);
2513 SetTopContext(codeEditor.globalContext);
2514 SetGlobalContext(codeEditor.globalContext);
2515 SetGlobalData(&codeEditor.globalData);
2518 exp = ParseExpressionString(wh.expression);
2520 if(exp && !parseError)
2522 char expString[4096];
2524 PrintExpression(exp, expString);
2526 if(GetPrivateModule())
2529 DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2530 ProcessExpressionType(exp);
2532 wh.type = exp.expType;
2535 DebugComputeExpression(exp);
2536 if(ExpressionIsError(exp))
2538 GDBFallBack(exp, expString);
2541 /*if(exp.hasAddress)
2543 char temp[MAX_F_STRING];
2544 sprintf(temp, "0x%x", exp.address);
2545 wh.address = CopyString(temp);
2546 // wh.address = CopyStringf("0x%x", exp.address);
2551 Type dataType = exp.expType;
2554 char temp[MAX_F_STRING];
2555 switch(dataType.kind)
2558 sprintf(temp, "%i", exp.val.c);
2561 sprintf(temp, "%i", exp.val.s);
2566 sprintf(temp, "%i", exp.val.i);
2569 sprintf(temp, "%i", exp.val.i64);
2572 sprintf(temp, "%i", exp.val.p);
2577 long v = (long)exp.val.f;
2578 sprintf(temp, "%i", v);
2583 long v = (long)exp.val.d;
2584 sprintf(temp, "%i", v);
2589 wh.intVal = CopyString(temp);
2590 switch(dataType.kind)
2593 sprintf(temp, "0x%x", exp.val.c);
2596 sprintf(temp, "0x%x", exp.val.s);
2600 sprintf(temp, "0x%x", exp.val.i);
2603 sprintf(temp, "0x%x", exp.val.i64);
2606 sprintf(temp, "0x%x", exp.val.i64);
2609 sprintf(temp, "0x%x", exp.val.p);
2614 long v = (long)exp.val.f;
2615 sprintf(temp, "0x%x", v);
2620 long v = (long)exp.val.d;
2621 sprintf(temp, "0x%x", v);
2626 wh.hexVal = CopyString(temp);
2627 switch(dataType.kind)
2630 sprintf(temp, "0o%o", exp.val.c);
2633 sprintf(temp, "0o%o", exp.val.s);
2637 sprintf(temp, "0o%o", exp.val.i);
2640 sprintf(temp, "0o%o", exp.val.i64);
2643 sprintf(temp, "0o%o", exp.val.i64);
2646 sprintf(temp, "0o%o", exp.val.p);
2651 long v = (long)exp.val.f;
2652 sprintf(temp, "0o%o", v);
2657 long v = (long)exp.val.d;
2658 sprintf(temp, "0o%o", v);
2663 wh.octVal = CopyString(temp);
2666 // WHATS THIS HERE ?
2667 if(exp.type == constantExp && exp.constant)
2668 wh.constant = CopyString(exp.constant);
2674 case symbolErrorExp:
2675 snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2677 case structMemberSymbolErrorExp:
2678 // todo get info as in next case (ExpClassMemberSymbolError)
2679 snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2681 case classMemberSymbolErrorExp:
2684 Expression memberExp = exp.member.exp;
2685 Identifier memberID = exp.member.member;
2686 Type type = memberExp.expType;
2689 _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2692 char string[256] = "";
2694 PrintTypeNoConst(type, string, false, true);
2695 classSym = FindClass(string);
2696 _class = classSym ? classSym.registered : null;
2699 snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2701 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2704 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2707 case memoryErrorExp:
2708 // Need to ensure when set to memoryErrorExp, constant is set
2709 snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2711 case dereferenceErrorExp:
2712 snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2714 case unknownErrorExp:
2715 snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2717 case noDebuggerErrorExp:
2718 snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2720 case debugStateErrorExp:
2721 snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2724 snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2728 // Temporary Code for displaying Strings
2729 if((exp.expType && ((exp.expType.kind == pointerType ||
2730 exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) ||
2731 (wh.type && wh.type.kind == classType && wh.type._class &&
2732 wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2733 !strcmp(wh.type._class.registered.name, "String")))
2736 if(exp.expType.kind != arrayType || exp.hasAddress)
2742 //char temp[MAX_F_STRING * 32];
2744 ExpressionType evalError = dummyExp;
2745 /*if(exp.expType.kind == arrayType)
2746 sprintf(temp, "(char*)0x%x", exp.address);
2748 sprintf(temp, "(char*)%s", exp.constant);*/
2750 //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2751 // address = strtoul(exp.constant, null, 0);
2752 address = _strtoui64(exp.constant, null, 0);
2753 //_dpl(0, "0x", address);
2754 // snprintf(value, sizeof(value), "0x%08x ", address);
2756 if(address > 0xFFFFFFFFLL)
2757 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2759 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2760 value[sizeof(value)-1] = 0;
2763 strcat(value, $"Null string");
2767 len = strlen(value);
2769 while(!string && size > 2)
2771 string = GdbReadMemory(address, size);
2774 if(string && string[0])
2777 if(UTF8Validate(string))
2782 for(c = 0; (ch = string[c]) && c<4096; c++)
2785 value[len++] = '\0';
2790 ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2791 strcat(value, ") (ISO8859-1)");
2798 strcat(value, $"Empty string");
2802 strcat(value, $"Couldn't read memory");
2804 wh.value = CopyString(value);
2807 else if(wh.type && wh.type.kind == classType && wh.type._class &&
2808 wh.type._class.registered && wh.type._class.registered.type == enumClass)
2810 uint64 value = strtoul(exp.constant, null, 0);
2811 Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2812 EnumClassData enumeration = (EnumClassData)enumClass.data;
2814 for(item = enumeration.values.first; item; item = item.next)
2815 if((int)item.data == value)
2818 wh.value = CopyString(item.name);
2820 wh.value = CopyString($"Invalid Enum Value");
2823 else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class &&
2824 wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2831 if(exp.constant[0] == '\'')
2833 if((int)((byte *)exp.constant)[1] > 127)
2836 value = UTF8GetChar(exp.constant + 1, &nb);
2837 if(nb < 2) value = exp.constant[1];
2838 signedValue = value;
2842 signedValue = exp.constant[1];
2844 // Precomp Syntax error with boot strap here:
2845 byte b = (byte)(char)signedValue;
2846 value = (unichar) b;
2852 if(wh.type.kind == charType && wh.type.isSigned)
2854 signedValue = (int)(char)strtol(exp.constant, null, 0);
2856 // Precomp Syntax error with boot strap here:
2857 byte b = (byte)(char)signedValue;
2858 value = (unichar) b;
2863 value = (uint)strtoul(exp.constant, null, 0);
2864 signedValue = (int)value;
2868 UTF32toUTF8Len(&value, 1, charString, 5);
2870 snprintf(string, sizeof(string), "\'\\0' (0)");
2871 else if(value == '\t')
2872 snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2873 else if(value == '\n')
2874 snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2875 else if(value == '\r')
2876 snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2877 else if(wh.type.kind == charType && wh.type.isSigned)
2878 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2879 else if(value > 256 || wh.type.kind != charType)
2881 if(value > 0x10FFFF || !GetCharCategory(value))
2882 snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2884 snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2887 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2888 string[sizeof(string)-1] = 0;
2890 wh.value = CopyString(string);
2895 wh.value = CopyString(exp.constant);
2902 wh.value = PrintHexUInt64(exp.address);
2907 char tempString[256];
2908 if(exp.member.memberType == propertyMember)
2909 snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2911 snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression,
2912 exp.type.OnGetString(tempString, null, null));
2918 snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2919 if(exp) FreeExpression(exp);
2922 SetPrivateModule(backupPrivateModule);
2923 SetCurrentContext(backupContext);
2924 SetTopContext(backupContext);
2925 SetGlobalContext(backupContext);
2926 SetThisClass(backupThisClass);
2929 // wh.value = CopyString("No source file found for selected frame");
2931 watchmsg[sizeof(watchmsg)-1] = 0;
2933 wh.value = CopyString(watchmsg);
2935 ide.watchesView.UpdateWatch(wh);
2939 void EvaluateWatches()
2941 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateWatches()");
2942 for(wh : ide.workspace.watches)
2946 char * ::GdbEvaluateExpression(char * expression)
2948 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbEvaluateExpression(", expression, ")");
2951 GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2953 ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2957 // to be removed... use GdbReadMemory that returns a byte array instead
2958 char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
2960 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemoryString(", address, ")");
2965 _dpl(0, "GdbReadMemoryString called with size = 0!");
2967 // GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
2968 if(GetRuntimePlatform() == win32)
2969 GdbCommand(false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
2971 GdbCommand(false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
2973 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
2977 byte * ::GdbReadMemory(uint64 address, int bytes)
2979 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemory(", address, ")");
2982 //GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
2983 if(GetRuntimePlatform() == win32)
2984 GdbCommand(false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
2986 GdbCommand(false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
2989 _dpl(0, "GdbReadMemory called with bytes = 0!");
2992 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
2993 else if(eval.result && strcmp(eval.result, "N/A"))
2995 byte * result = new byte[bytes];
2996 byte * string = eval.result;
3000 result[c++] = (byte)strtol(string, &string, 10);
3016 void EventHit(GdbDataStop stopItem, Breakpoint bpInternal, Breakpoint bpUser)
3018 char * s1 = null; char * s2 = null;
3019 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EventHit(",
3020 "bpInternal(", bpInternal ? s1=bpInternal.CopyLocationString(false) : null, "), ",
3021 "bpUser(", bpUser ? s2=bpUser.CopyLocationString(false) : null, ")) -- ",
3022 "ignoreBreakpoints(", ignoreBreakpoints, "), ",
3023 "hitCursorBreakpoint(", bpUser && bpUser.type == runToCursor, ")");
3024 delete s1; delete s2;
3026 if(bpUser && stopItem.frame.line && bpUser.line != stopItem.frame.line)
3028 // updating user breakpoint on hit location difference
3029 // todo, print something?
3030 bpUser.line = stopItem.frame.line;
3031 ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3032 ide.workspace.Save();
3037 if(bpInternal.type == internalModulesLoaded)
3039 if(!bpUser && !userBreakOnInternalBreakpoint)
3040 GdbExecContinue(false);
3044 bool conditionMet = true;
3045 if(bpUser.condition)
3046 conditionMet = ResolveWatch(bpUser.condition);
3048 if(!ignoreBreakpoints && (bpUser.level == -1 || bpUser.level == frameCount-1) && conditionMet)
3055 GdbExecContinue(false);
3059 GdbExecContinue(false);
3060 ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3061 if(bpUser == bpRunToCursor)
3063 UnsetBreakpoint(bpUser);
3064 delete bpRunToCursor;
3068 if(!bpUser && !bpInternal)
3069 GdbExecContinue(false);
3072 void ValgrindTargetThreadExit()
3074 ide.outputView.debugBox.Logf($"ValgrindTargetThreadExit\n");
3077 vgTargetHandle.Wait();
3078 delete vgTargetHandle;
3080 HandleExit(null, null);
3083 void GdbThreadExit()
3085 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbThreadExit()");
3086 if(state != terminated)
3088 ChangeState(terminated);
3089 targetProcessId = 0;
3090 ClearBreakDisplay();
3096 serialSemaphore.Release();
3101 ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
3102 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3104 HideDebuggerViews();
3106 //ChangeState(terminated);
3110 void GdbThreadMain(char * output)
3113 Array<char *> outTokens { minAllocSize = 50 };
3114 Array<char *> subTokens { minAllocSize = 50 };
3115 DebugListItem item { };
3116 DebugListItem item2 { };
3117 bool setWaitingForPID = false;
3119 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
3120 #ifdef GDB_DEBUG_CONSOLE
3121 _dpl2(_dpct, dplchan::gdbOutput, 0, output);
3123 #ifdef GDB_DEBUG_OUTPUT
3125 int len = strlen(output);
3133 for(c = 0; c < len / 1024; c++)
3135 strncpy(tmp, start, 1024);
3136 ide.outputView.gdbBox.Logf("out: %s\n", tmp);
3139 ide.outputView.gdbBox.Logf("out: %s\n", start);
3143 ide.outputView.gdbBox.Logf("out: %s\n", output);
3147 #ifdef GDB_DEBUG_CONSOLE
3148 strcpy(lastGdbOutput, output);
3150 #ifdef GDB_DEBUG_GUI
3151 if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
3158 if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
3161 ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
3167 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
3169 //if(outTokens.count == 1)
3174 ChangeState(loaded);
3175 targetProcessId = 0;
3176 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3178 if(!strcmp(item.name, "reason"))
3180 char * reason = item.value;
3181 StripQuotes(reason, reason);
3182 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3185 if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
3187 StripQuotes(item2.value, item2.value);
3188 if(!strcmp(item2.name, "exit-code"))
3189 exitCode = item2.value;
3195 HandleExit(reason, exitCode);
3199 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "kill reply (", item.name, "=", item.value, ") is unheard of");
3202 HandleExit(null, null);
3205 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3207 if(!strcmp(item.name, "bkpt"))
3209 sentBreakInsert = false;
3214 bpItem = ParseBreakpoint(item.value, outTokens);
3215 //breakType = bpValidation;
3217 else if(!strcmp(item.name, "depth"))
3219 StripQuotes(item.value, item.value);
3220 frameCount = atoi(item.value);
3222 stackFrames.Free(Frame::Free);
3224 else if(!strcmp(item.name, "stack"))
3227 if(stackFrames.count)
3228 ide.callStackView.Logf("...\n");
3231 item.value = StripBrackets(item.value);
3232 TokenizeList(item.value, ',', subTokens);
3233 for(i = 0; i < subTokens.count; i++)
3235 if(TokenizeListItem(subTokens[i], item))
3237 if(!strcmp(item.name, "frame"))
3240 stackFrames.Add(frame);
3241 item.value = StripCurlies(item.value);
3242 ParseFrame(frame, item.value);
3243 if(frame.file && frame.from)
3244 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "unexpected frame file and from members present");
3248 if(activeFrameLevel == -1)
3250 if(ide.projectView.IsModuleInProject(frame.file));
3252 if(frame.level != 0)
3254 //stopItem.frame = frame;
3255 breakType = selectFrame;
3258 activeFrame = frame;
3259 activeFrameLevel = frame.level;
3262 ide.callStackView.Logf("%3d ", frame.level);
3263 if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
3264 ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
3265 else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
3266 ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
3267 else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
3268 ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
3269 else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
3270 ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
3272 ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
3277 ide.callStackView.Logf("%3d ", frame.level);
3282 ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
3286 ide.callStackView.Logf("%s\n", frame.func);
3288 ide.callStackView.Logf($"unknown source\n");
3292 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "stack content (", item.name, "=", item.value, ") is unheard of");
3295 if(activeFrameLevel == -1)
3297 activeFrameLevel = 0;
3298 activeFrame = stackFrames.first;
3300 ide.callStackView.Home();
3302 subTokens.RemoveAll();
3304 /*else if(!strcmp(item.name, "frame"))
3307 item.value = StripCurlies(item.value);
3308 ParseFrame(&frame, item.value);
3310 else if(!strcmp(item.name, "thread-ids"))
3312 ide.threadsView.Clear();
3313 item.value = StripCurlies(item.value);
3314 TokenizeList(item.value, ',', subTokens);
3315 for(i = subTokens.count - 1; ; i--)
3317 if(TokenizeListItem(subTokens[i], item))
3319 if(!strcmp(item.name, "thread-id"))
3322 StripQuotes(item.value, item.value);
3323 value = atoi(item.value);
3324 ide.threadsView.Logf("%3d \n", value);
3327 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "threads content (", item.name, "=", item.value, ") is unheard of");
3332 ide.threadsView.Home();
3334 subTokens.RemoveAll();
3335 //if(!strcmp(outTokens[2], "number-of-threads"))
3337 else if(!strcmp(item.name, "new-thread-id"))
3339 StripQuotes(item.value, item.value);
3340 activeThread = atoi(item.value);
3342 else if(!strcmp(item.name, "value"))
3344 StripQuotes(item.value, item.value);
3345 eval.result = CopyString(item.value);
3346 eval.active = false;
3348 else if(!strcmp(item.name, "addr"))
3350 for(i = 2; i < outTokens.count; i++)
3352 if(TokenizeListItem(outTokens[i], item))
3354 if(!strcmp(item.name, "total-bytes"))
3356 StripQuotes(item.value, item.value);
3357 eval.bytes = atoi(item.value);
3359 else if(!strcmp(item.name, "next-row"))
3361 StripQuotes(item.value, item.value);
3362 eval.nextBlockAddress = _strtoui64(item.value, null, 0);
3364 else if(!strcmp(item.name, "memory"))
3368 //StripQuotes(item.value, item.value);
3369 item.value = StripBrackets(item.value);
3370 // this should be treated as a list...
3371 item.value = StripCurlies(item.value);
3372 TokenizeList(item.value, ',', subTokens);
3373 for(j = 0; j < subTokens.count; j++)
3375 if(TokenizeListItem(subTokens[j], item))
3377 if(!strcmp(item.name, "data"))
3379 item.value = StripBrackets(item.value);
3380 StripQuotes2(item.value, item.value);
3381 eval.result = CopyString(item.value);
3382 eval.active = false;
3386 subTokens.RemoveAll();
3391 else if(!strcmp(item.name, "source-path") || !strcmp(item.name, "BreakpointTable"))
3392 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "command reply (", item.name, "=", item.value, ") is ignored");
3394 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "command reply (", item.name, "=", item.value, ") is unheard of");
3397 else if(!strcmp(outTokens[0], "^running"))
3399 waitingForPID = true;
3400 setWaitingForPID = true;
3402 else if(!strcmp(outTokens[0], "^exit"))
3404 ChangeState(terminated);
3405 // ide.outputView.debugBox.Logf("Exit\n");
3406 // ide.Update(null);
3408 serialSemaphore.Release();
3410 else if(!strcmp(outTokens[0], "^error"))
3414 sentBreakInsert = false;
3415 breakpointError = true;
3418 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3420 if(!strcmp(item.name, "msg"))
3422 StripQuotes(item.value, item.value);
3425 eval.active = false;
3427 if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3428 eval.error = symbolNotFound;
3429 else if(strstr(item.value, "Cannot access memory at address"))
3430 eval.error = memoryCantBeRead;
3432 eval.error = unknown;
3434 else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3437 else if(!strncmp(item.value, "Cannot access memory at address", 31))
3440 else if(!strcmp(item.value, "Cannot find bounds of current function"))
3442 ChangeState(stopped);
3443 gdbHandle.Printf("-exec-continue\n");
3445 else if(!strcmp(item.value, "ptrace: No such process."))
3447 ChangeState(loaded);
3448 targetProcessId = 0;
3450 else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3453 else if(!strcmp(item.value, "You can't do that without a process to debug."))
3455 ChangeState(loaded);
3456 targetProcessId = 0;
3458 else if(strstr(item.value, "No such file or directory."))
3460 ChangeState(loaded);
3461 targetProcessId = 0;
3463 else if(strstr(item.value, "During startup program exited with code "))
3465 ChangeState(loaded);
3466 targetProcessId = 0;
3471 if(strlen(item.value) < MAX_F_STRING)
3474 ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3478 ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3484 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "error content (", item.name, "=", item.value, ") is unheard of");
3487 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "result-record: ", outTokens[0]);
3488 outTokens.RemoveAll();
3491 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "status-async-output: ", outTokens[0]);
3494 if(TokenizeList(output, ',', outTokens))
3496 if(!strcmp(outTokens[0], "=library-loaded"))
3497 FGODetectLoadedLibraryForAddedProjectIssues(outTokens);
3498 else if(!strcmp(outTokens[0], "=thread-group-created") || !strcmp(outTokens[0], "=thread-group-added") ||
3499 !strcmp(outTokens[0], "=thread-group-started") || !strcmp(outTokens[0], "=thread-group-exited") ||
3500 !strcmp(outTokens[0], "=thread-created") || !strcmp(outTokens[0], "=thread-exited") ||
3501 !strcmp(outTokens[0], "=cmd-param-changed") || !strcmp(outTokens[0], "=library-unloaded") ||
3502 !strcmp(outTokens[0], "=breakpoint-modified"))
3503 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, outTokens[0], outTokens.count>1 ? outTokens[1] : "",
3504 outTokens.count>2 ? outTokens[2] : "", outTokens.count>3 ? outTokens[3] : "",
3505 outTokens.count>4 ? outTokens[4] : "", outTokens.count>5 ? outTokens[5] : "",
3506 outTokens.count>6 ? outTokens[6] : "", outTokens.count>7 ? outTokens[7] : "",
3507 outTokens.count>8 ? outTokens[8] : "", outTokens.count>9 ? outTokens[9] : "");
3509 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "notify-async-output: ", outTokens[0]);
3511 outTokens.RemoveAll();
3515 if(TokenizeList(output, ',', outTokens))
3517 if(!strcmp(outTokens[0],"*running"))
3519 waitingForPID = true;
3520 setWaitingForPID = true;
3522 else if(!strcmp(outTokens[0], "*stopped"))
3525 ChangeState(stopped);
3527 for(tk = 1; tk < outTokens.count; tk++)
3529 if(TokenizeListItem(outTokens[tk], item))
3531 if(!strcmp(item.name, "reason"))
3533 char * reason = item.value;
3534 StripQuotes(reason, reason);
3535 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3538 if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3541 StripQuotes(item2.value, item2.value);
3542 if(!strcmp(item2.name, "exit-code"))
3543 exitCode = item2.value;
3549 HandleExit(reason, exitCode);
3552 else if(!strcmp(reason, "breakpoint-hit"))
3558 stopItem = GdbDataStop { };
3560 for(i = tk+1; i < outTokens.count; i++)
3562 TokenizeListItem(outTokens[i], item);
3563 StripQuotes(item.value, item.value);
3564 if(!strcmp(item.name, "bkptno"))
3565 stopItem.bkptno = atoi(item.value);
3566 else if(!strcmp(item.name, "thread-id"))
3567 stopItem.threadid = atoi(item.value);
3568 else if(!strcmp(item.name, "frame"))
3570 item.value = StripCurlies(item.value);
3571 ParseFrame(stopItem.frame, item.value);
3573 else if(!strcmp(item.name, "disp") || !strcmp(item.name, "stopped-threads") || !strcmp(item.name, "core"))
3574 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3576 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown breakpoint hit item name (", item.name, "=", item.value, ")");
3581 else if(!strcmp(reason, "end-stepping-range"))
3587 stopItem = GdbDataStop { };
3589 for(i = tk+1; i < outTokens.count; i++)
3591 TokenizeListItem(outTokens[i], item);
3592 StripQuotes(item.value, item.value);
3593 if(!strcmp(item.name, "thread-id"))
3594 stopItem.threadid = atoi(item.value);
3595 else if(!strcmp(item.name, "frame"))
3597 item.value = StripCurlies(item.value);
3598 ParseFrame(stopItem.frame, item.value);
3600 else if(!strcmp(item.name, "reason") || !strcmp(item.name, "bkptno"))
3601 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3603 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown end of stepping range item name (", item.name, "=", item.value, ")");
3609 else if(!strcmp(reason, "function-finished"))
3615 stopItem = GdbDataStop { };
3616 stopItem.reason = CopyString(reason);
3618 for(i = tk+1; i < outTokens.count; i++)
3620 TokenizeListItem(outTokens[i], item);
3621 StripQuotes(item.value, item.value);
3622 if(!strcmp(item.name, "thread-id"))
3623 stopItem.threadid = atoi(item.value);
3624 else if(!strcmp(item.name, "frame"))
3626 item.value = StripCurlies(item.value);
3627 ParseFrame(stopItem.frame, item.value);
3629 else if(!strcmp(item.name, "gdb-result-var"))
3630 stopItem.gdbResultVar = CopyString(item.value);
3631 else if(!strcmp(item.name, "return-value"))
3632 stopItem.returnValue = CopyString(item.value);
3634 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown function finished item name (", item.name, "=", item.value, ")");
3637 event = functionEnd;
3640 else if(!strcmp(reason, "signal-received"))
3646 stopItem = GdbDataStop { };
3647 stopItem.reason = CopyString(reason);
3649 for(i = tk+1; i < outTokens.count; i++)
3651 TokenizeListItem(outTokens[i], item);
3652 StripQuotes(item.value, item.value);
3653 if(!strcmp(item.name, "signal-name"))
3654 stopItem.name = CopyString(item.value);
3655 else if(!strcmp(item.name, "signal-meaning"))
3656 stopItem.meaning = CopyString(item.value);
3657 else if(!strcmp(item.name, "thread-id"))
3658 stopItem.threadid = atoi(item.value);
3659 else if(!strcmp(item.name, "frame"))
3661 item.value = StripCurlies(item.value);
3662 ParseFrame(stopItem.frame, item.value);
3665 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown signal reveived item name (", item.name, "=", item.value, ")");
3667 if(!strcmp(stopItem.name, "SIGTRAP"))
3686 else if(!strcmp(reason, "watchpoint-trigger"))
3687 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint trigger not handled");
3688 else if(!strcmp(reason, "read-watchpoint-trigger"))
3689 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason read watchpoint trigger not handled");
3690 else if(!strcmp(reason, "access-watchpoint-trigger"))
3691 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason access watchpoint trigger not handled");
3692 else if(!strcmp(reason, "watchpoint-scope"))
3693 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint scope not handled");
3694 else if(!strcmp(reason, "location-reached"))
3695 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason location reached not handled");
3697 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown reason: ", reason);
3709 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown exec-async-output: ", outTokens[0]);
3710 outTokens.RemoveAll();
3713 if(!strcmpi(output, "(gdb) "))
3717 char exeFile[MAX_LOCATION];
3718 int oldProcessID = targetProcessId;
3719 GetLastDirectory(targetFile, exeFile);
3721 while(!targetProcessId/*true*/)
3723 targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3724 if(targetProcessId || gdbHandle.Peek()) break;
3729 ChangeState(running);
3730 else if(!oldProcessID)
3732 ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3733 // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3734 gdbHandle.Printf("-gdb-exit\n");
3736 ChangeState(terminated); //loaded;
3741 for(bp : ide.workspace.breakpoints)
3742 bp.inserted = false;
3745 bp.inserted = false;
3747 bpRunToCursor.inserted = false;
3749 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3750 ClearBreakDisplay();
3752 #if defined(__unix__)
3753 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
3755 progThread.terminate = true;
3758 fifoFile.CloseInput();
3765 DeleteFile(progFifoPath);
3766 progFifoPath[0] = '\0';
3773 serialSemaphore.Release();
3776 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown prompt", output);
3780 if(!strncmp(output, "&\"warning:", 10))
3783 content = strstr(output, "\"");
3784 StripQuotes(content, content);
3785 content = strstr(content, ":");
3791 ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3798 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown output: ", output);
3800 if(!setWaitingForPID)
3801 waitingForPID = false;
3802 setWaitingForPID = false;
3810 // From GDB Output functions
3811 void FGODetectLoadedLibraryForAddedProjectIssues(Array<char *> outTokens)
3813 char path[MAX_LOCATION] = "";
3814 char file[MAX_FILENAME] = "";
3816 DebugListItem item { };
3817 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGODetectLoadedLibraryForAddedProjectIssues()");
3818 for(token : outTokens)
3820 if(TokenizeListItem(token, item))
3822 if(!strcmp(item.name, "target-name"))
3824 StripQuotes(item.value, path);
3825 MakeSystemPath(path);
3826 GetLastDirectory(path, file);
3828 else if(!strcmp(item.name, "symbols-loaded"))
3830 symbolsLoaded = (atoi(item.value) == 1);
3835 if(path[0] && file[0])
3837 for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
3841 char prjTargetPath[MAX_LOCATION];
3842 char prjTargetFile[MAX_FILENAME];
3843 DirExpression targetDirExp = prj.GetTargetDir(currentCompiler, prj.config, bitDepth);
3844 strcpy(prjTargetPath, prj.topNode.path);
3845 PathCat(prjTargetPath, targetDirExp.dir);
3846 prjTargetFile[0] = '\0';
3847 prj.CatTargetFileName(prjTargetFile, currentCompiler, prj.config);
3848 PathCat(prjTargetPath, prjTargetFile);
3849 MakeSystemPath(prjTargetPath);
3851 match = !fstrcmp(prjTargetFile, file);
3852 if(!match && (dot = strstr(prjTargetFile, ".so.")))
3854 char * dot3 = strstr(dot+4, ".");
3858 match = !fstrcmp(prjTargetFile, file);
3863 match = !fstrcmp(prjTargetFile, file);
3868 // TODO: nice visual feedback to better warn user. use some ide notification system or other means.
3869 /* -- 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)
3871 ide.outputView.debugBox.Logf($"Attention! No symbols for loaded library %s matched to the %s added project.\n", path, prj.topNode.name);
3873 match = !fstrcmp(prjTargetPath, path);
3874 if(!match && (dot = strstr(prjTargetPath, ".so.")))
3876 char * dot3 = strstr(dot+4, ".");
3880 match = !fstrcmp(prjTargetPath, path);
3885 match = !fstrcmp(prjTargetPath, path);
3889 ide.outputView.debugBox.Logf($"Loaded library %s doesn't match the %s target of the %s added project.\n", path, prjTargetPath, prj.topNode.name);
3896 void FGOBreakpointModified(Array<char *> outTokens)
3898 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGOBreakpointModified() -- TODO only if needed: support breakpoint modified");
3900 DebugListItem item { };
3901 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3903 if(!strcmp(item.name, "bkpt"))
3905 GdbDataBreakpoint modBp = ParseBreakpoint(item.value, outTokens);
3913 ExpressionType ::DebugEvalExpTypeError(char * result)
3915 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::DebugEvalExpTypeError()");
3920 case symbolNotFound:
3921 return symbolErrorExp;
3922 case memoryCantBeRead:
3923 return memoryErrorExp;
3925 return unknownErrorExp;
3928 char * ::EvaluateExpression(char * expression, ExpressionType * error)
3931 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateExpression(", expression, ")");
3932 if(ide.projectView && ide.debugger.state == stopped)
3934 result = GdbEvaluateExpression(expression);
3935 *error = DebugEvalExpTypeError(result);
3940 *error = noDebuggerErrorExp;
3945 char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
3948 char * result = GdbReadMemoryString(address, size, format, 1, 1);
3949 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ReadMemory(", address, ")");
3950 if(!result || !strcmp(result, "N/A"))
3951 *error = memoryErrorExp;
3953 *error = DebugEvalExpTypeError(result);
3958 class ValgrindLogThread : Thread
3964 static char output[4096];
3965 Array<char> dynamicBuffer { minAllocSize = 4096 };
3966 File oldValgrindHandle = vgLogFile;
3967 incref oldValgrindHandle;
3970 while(debugger.state != terminated && vgLogFile)
3974 result = vgLogFile.Read(output, 1, sizeof(output));
3976 if(debugger.state == terminated || !vgLogFile/* || vgLogFile.Eof()*/)
3983 for(c = 0; c<result; c++)
3985 if(output[c] == '\n')
3987 int pos = dynamicBuffer.size;
3988 dynamicBuffer.size += c - start;
3989 memcpy(&dynamicBuffer[pos], output + start, c - start);
3990 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3991 // COMMENTED OUT DUE TO ISSUE #135, FIXED
3992 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
3993 dynamicBuffer.size++;
3994 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
3996 // printf("%s\n", dynamicBuffer.array);
3998 if(strstr(&dynamicBuffer[0], "vgdb me"))
3999 debugger.serialSemaphore.Release();
4000 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4001 dynamicBuffer.size = 0;
4007 int pos = dynamicBuffer.size;
4008 dynamicBuffer.size += c - start;
4009 memcpy(&dynamicBuffer[pos], output + start, c - start);
4012 else if(debugger.state == stopped)
4015 printf("Got end of file from GDB!\n");
4022 delete dynamicBuffer;
4023 ide.outputView.debugBox.Logf($"ValgrindLogThreadExit\n");
4024 //if(oldValgrindHandle == vgLogFile)
4025 debugger.GdbThreadExit/*ValgrindLogThreadExit*/();
4026 delete oldValgrindHandle;
4032 class ValgrindTargetThread : Thread
4038 static char output[4096];
4039 Array<char> dynamicBuffer { minAllocSize = 4096 };
4040 DualPipe oldValgrindHandle = vgTargetHandle;
4041 incref oldValgrindHandle;
4044 while(debugger.state != terminated && vgTargetHandle && !vgTargetHandle.Eof())
4048 result = vgTargetHandle.Read(output, 1, sizeof(output));
4050 if(debugger.state == terminated || !vgTargetHandle || vgTargetHandle.Eof())
4057 for(c = 0; c<result; c++)
4059 if(output[c] == '\n')
4061 int pos = dynamicBuffer.size;
4062 dynamicBuffer.size += c - start;
4063 memcpy(&dynamicBuffer[pos], output + start, c - start);
4064 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4065 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4066 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4067 dynamicBuffer.size++;
4068 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4070 // printf("%s\n", dynamicBuffer.array);
4072 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4074 dynamicBuffer.size = 0;
4080 int pos = dynamicBuffer.size;
4081 dynamicBuffer.size += c - start;
4082 memcpy(&dynamicBuffer[pos], output + start, c - start);
4088 printf("Got end of file from GDB!\n");
4092 delete dynamicBuffer;
4093 //if(oldValgrindHandle == vgTargetHandle)
4094 debugger.ValgrindTargetThreadExit();
4095 delete oldValgrindHandle;
4101 class GdbThread : Thread
4107 static char output[4096];
4108 Array<char> dynamicBuffer { minAllocSize = 4096 };
4109 DualPipe oldGdbHandle = gdbHandle;
4110 incref oldGdbHandle;
4113 while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
4117 result = gdbHandle.Read(output, 1, sizeof(output));
4119 if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
4126 for(c = 0; c<result; c++)
4128 if(output[c] == '\n')
4130 int pos = dynamicBuffer.size;
4131 dynamicBuffer.size += c - start;
4132 memcpy(&dynamicBuffer[pos], output + start, c - start);
4133 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4134 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4135 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4136 dynamicBuffer.size++;
4137 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4139 // _dpl(0, dynamicBuffer.array);
4141 debugger.GdbThreadMain(&dynamicBuffer[0]);
4142 dynamicBuffer.size = 0;
4148 int pos = dynamicBuffer.size;
4149 dynamicBuffer.size += c - start;
4150 memcpy(&dynamicBuffer[pos], output + start, c - start);
4156 _dpl(0, "Got end of file from GDB!");
4160 delete dynamicBuffer;
4161 //if(oldGdbHandle == gdbHandle)
4162 debugger.GdbThreadExit();
4163 delete oldGdbHandle;
4169 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
4170 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
4172 #if defined(__unix__)
4177 #include <sys/types.h>
4182 class ProgramThread : Thread
4188 bool fileCreated = false;
4190 static char output[1000];
4193 /*if(!mkfifo(progFifoPath, mask))
4200 ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
4204 if(FileExists(progFifoPath)) //fileCreated)
4206 fifoFile = FileOpen(progFifoPath, read);
4210 ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
4215 fd = fileno((FILE *)fifoFile.input);
4216 //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
4220 while(!terminate && fifoFile && !fifoFile.Eof())
4223 struct timeval time;
4231 selectResult = select(fd + 1, &rs, null, null, &time);
4232 if(FD_ISSET(fd, &rs))
4234 int result = (int)read(fd, output, sizeof(output)-1);
4235 if(!result || (result < 0 && errno != EAGAIN))
4239 output[result] = '\0';
4240 if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
4243 ide.outputView.debugBox.Log(output);
4252 //fifoFile.CloseInput();
4255 ide.outputView.debugBox.Log("\n");
4259 if(FileExists(progFifoPath)) //fileCreated)
4261 DeleteFile(progFifoPath);
4262 progFifoPath[0] = '\0';
4270 class Argument : struct
4272 Argument prev, next;
4274 property char * name { set { delete name; if(value) name = CopyString(value); } }
4276 property char * val { set { delete val; if(value) val = CopyString(value); } }
4290 class Frame : struct
4295 property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4297 property char * func { set { delete func; if(value) func = CopyString(value); } }
4301 property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
4303 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4304 char * absoluteFile;
4305 property char * absoluteFile { set { delete absoluteFile; if(value) absoluteFile = CopyUnescapedUnixPath(value); } }
4314 delete absoluteFile;
4315 args.Free(Argument::Free);
4324 class GdbDataStop : struct
4341 char * gdbResultVar;
4351 if(!strcmp(reason, "signal-received"))
4356 else if(!strcmp(reason, "function-finished"))
4358 delete gdbResultVar;
4363 if(frame) frame.Free();
4372 class GdbDataBreakpoint : struct
4376 property char * number { set { delete number; if(value) number = CopyString(value); } }
4378 property char * type { set { delete type; if(value) type = CopyString(value); } }
4380 property char * disp { set { delete disp; if(value) disp = CopyString(value); } }
4383 property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4385 property char * func { set { delete func; if(value) func = CopyString(value); } }
4387 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4389 property char * fullname { set { delete fullname; if(value) fullname = CopyUnescapedUnixPath(value); } }
4392 property char * at { set { delete at; if(value) at = CopyString(value); } }
4395 Array<GdbDataBreakpoint> multipleBPs;
4400 PrintLn("{", "#", number, " T", type, " D", disp, " E", enabled, " H", times, " (", func, ") (", file, ":", line, ") (", fullname, ") (", addr, ") (", at, ")", "}");
4411 if(multipleBPs) multipleBPs.Free();
4415 ~GdbDataBreakpoint()
4421 class Breakpoint : struct
4426 property char * function { set { delete function; if(value) function = CopyString(value); } }
4427 char * relativeFilePath;
4428 property char * relativeFilePath { set { delete relativeFilePath; if(value) relativeFilePath = CopyString(value); } }
4429 char * absoluteFilePath;
4430 property char * absoluteFilePath { set { delete absoluteFilePath; if(value) absoluteFilePath = CopyString(value); } }
4439 BreakpointType type;
4441 GdbDataBreakpoint bp;
4443 char * CopyLocationString(bool removePath)
4446 char * file = relativeFilePath ? relativeFilePath : absoluteFilePath;
4447 bool removingPath = removePath && file;
4450 char * fileName = new char[MAX_FILENAME];
4451 GetLastDirectory(file, fileName);
4457 location = PrintString(file, ":", function);
4459 location = CopyString(function);
4462 location = PrintString(file, ":", line);
4470 if(relativeFilePath && relativeFilePath[0])
4472 f.Printf(" * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
4474 f.Printf(" ~ %s\n", condition.expression);
4484 delete relativeFilePath;
4485 delete absoluteFilePath;
4495 class Watch : struct
4506 f.Printf(" ~ %s\n", expression);
4530 class DebugListItem : struct
4536 struct DebugEvaluationData
4541 uint64 nextBlockAddress;
4543 DebuggerEvaluationError error;
4546 class CodeLocation : struct
4549 char * absoluteFile;
4552 CodeLocation ::ParseCodeLocation(char * location)
4556 char * colon = null;
4558 char loc[MAX_LOCATION];
4559 strcpy(loc, location);
4560 for(temp = loc; temp = strstr(temp, ":"); temp++)
4568 int line = atoi(colon);
4571 CodeLocation codloc { line = line };
4572 codloc.file = CopyString(loc);
4573 codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
4585 delete absoluteFile;
4594 void GDBFallBack(Expression exp, String expString)
4597 ExpressionType evalError = dummyExp;
4598 result = Debugger::EvaluateExpression(expString, &evalError);
4601 exp.constant = result;
4602 exp.type = constantExp;
4606 static int String_OnCompare(char * string1, char * string2)
4610 if(string1 && string2)
4611 result = strcmp(string1, string2); //strcmpi
4612 else if(!string1 && string2)
4614 else if(string1 && !string2)