2 public import static "ecere"
3 public import static "ec"
15 #define GDB_DEBUG_CONSOLE
18 extern char * strrchr(const char * s, int c);
21 #define strlen _strlen
31 #include <sys/time.h> // Required on Apple...
45 sprintf(s[0], "%04d", now.year);
46 sprintf(s[1], "%02d", now.month+1);
47 sprintf(s[2], "%02d", now.day);
48 sprintf(s[3], "%02d", now.hour);
49 sprintf(s[4], "%02d", now.minute);
50 sprintf(s[5], "%02d", now.second);
51 time = PrintString("*", s[0], s[1], s[2], "-", s[3], s[4], s[5], "*");
57 // use =0 to disable printing of specific channels
59 static enum dplchan { none, gdbProtoIgnored=0/*1*/, gdbProtoUnknown=2, gdbOutput=3/*3*/, gdbCommand=4/*4*/, debuggerCall=5, debuggerProblem=6, debuggerTemp=7 };
61 static enum dplchan { none, gdbProtoIgnored=0, gdbProtoUnknown=0, gdbOutput=0, gdbCommand=0, debuggerCall=0, debuggerProblem=0, debuggerTemp=0 };
63 static char * _dpct[] = {
65 "GDB Protocol Ignored",
66 "GDB Protocol ***Unknown***",
70 "Debugger ***Problem***",
71 "-----> Temporary Message",
75 // TODO if(strlen(item.value) < MAX_F_STRING)
77 #define _dpl2(...) __dpl2(__FILE__, __LINE__, ##__VA_ARGS__)
78 static void __dpl2(char * file, int line, char ** channels, int channel, int indent, typed_object object, ...)
80 bool chan = channel && channels && channels[channel];
83 char string[MAX_F_STRING];
85 char * time = PrintNow();
87 //ide.outputView.debugBox.Logf();
88 Logf("%s %s:% 5d: %s%s", time, file, line, chan ? channels[channel] : "", chan && channels[channel][0] ? ": " : "");
89 va_start(args, object);
90 len = PrintStdArgsToBuffer(string, sizeof(string), object, args);
98 #define _dpl(...) __dpl(__FILE__, __LINE__, ##__VA_ARGS__)
99 static void __dpl(char * file, int line, int indent, char * format, ...)
102 char string[MAX_F_STRING];
104 char * time = PrintNow();
105 //static File f = null;
106 va_start(args, format);
107 vsnprintf(string, sizeof(string), format, args);
108 string[sizeof(string)-1] = 0;
111 char * time = PrintNow();
113 logName = PrintString(time, ".log");
115 f = FileOpen(logName, write);
118 /*f.Printf("%s %s:% 5d: ", time, file, line);
119 for(c = 0; c<indent; c++)
121 f.Printf("%s\n", string);*/
122 Logf("%s %s:% 5d: ", time, file, line);
123 for(c = 0; c<indent; c++)
125 Logf("%s\n", string);
130 public char * StripQuotes2(char * string, char * output)
134 bool quoted = false, escaped = false;
136 for(c = 0; ch = string[c]; c++)
140 if(escaped || ch != '\"')
143 escaped = !escaped && ch == '\\';
157 // String Escape Copy
158 static void strescpy(char * d, char * s)
211 static char * CopyUnescapedSystemPath(char * p)
213 char * d = new char[strlen(p) + 1];
215 #if defined(__WIN32__)
216 ChangeCh(d, '/', '\\');
221 static char * CopyUnescapedUnixPath(char * p)
223 char * d = new char[strlen(p) + 1];
225 #if defined(__WIN32__)
226 ChangeCh(d, '\\', '/');
231 static char * CopyUnescapedString(char * s)
233 char * d = new char[strlen(s) + 1];
238 // String Unescape Copy
240 // TOFIX: THIS DOESN'T HANDLE NUMERIC ESCAPE CODES (OCTAL/HEXADECIMAL...)?
241 // Seems very similar to ReadString in pass15.ec (which also misses numeric escape codes :) )
243 static void struscpy(char * d, char * s)
295 static char * StripBrackets(char * string)
297 int length = strlen(string);
298 if(length > 1 && *string == '[' && string[length - 1] == ']')
301 string[length - 1] = '\0';
308 static char * StripCurlies(char * string)
310 int length = strlen(string);
311 if(length > 1 && *string == '{' && string[length - 1] == '}')
314 string[length - 1] = '\0';
321 static int StringGetInt(char * string, int start)
324 int i, len = strlen(string);
326 for(i = start; i < len && i < start + 8; i++)
328 if(string[i] == '0' || string[i] == '1' || string[i] == '2' || string[i] == '3' || string[i] == '4' || string[i] == '5' || string[i] == '6' || string[i] == '7' || string[i] == '8' || string[i] == '9')
329 strncat(number, &string[i], 1);
336 static int TokenizeList(char * string, const char seperator, Array<char *> tokens)
340 bool quoted = false, escaped = false;
341 char * start = string, ch;
343 for(; (ch = *string); string++)
350 if(escaped || ch != '\"')
351 escaped = !escaped && ch == '\\';
357 else if(ch == '{' || ch == '[' || ch == '(' || ch == '<')
359 else if(ch == '}' || ch == ']' || ch == ')' || ch == '>')
361 else if(ch == seperator && !level)
370 //tokens[count] = start;
371 //tokens[count++] = start;
378 static bool TokenizeListItem(char * string, DebugListItem item)
380 char * equal = strstr(string, "=");
394 static bool CheckCommandAvailable(const char * command)
396 bool available = false;
398 char * name = new char[MAX_FILENAME];
399 char * pathVar = new char[maxPathLen];
401 GetEnvironment("PATH", pathVar, maxPathLen);
402 count = TokenizeWith(pathVar, sizeof(paths) / sizeof(char *), paths, pathListSep, false);
403 strcpy(name, command);
407 const char * extensions[] = { "exe", "com", "bat", null };
408 for(e=0; extensions[e]; e++)
410 ChangeExtension(name, extensions[e], name);
412 for(c=0; c<count; c++)
414 FileListing fl { paths[c] };
417 if(fl.stats.attribs.isFile && !fstrcmp(fl.name, name))
435 // define GdbGetLineSize = 1638400;
436 define GdbGetLineSize = 5638400;
437 #if defined(__unix__)
438 char progFifoPath[MAX_LOCATION];
439 char progFifoDir[MAX_LOCATION];
442 enum DebuggerState { none, prompt, loaded, running, stopped, terminated, error };
445 none, hit, breakEvent, signal, stepEnd, functionEnd, exit, valgrindStartPause;
447 property bool canBeMonitored { get { return (this == hit || this == breakEvent || this == signal || this == stepEnd || this == functionEnd); } };
449 enum DebuggerAction { none, internal, restart, stop, selectFrame }; //, bpValidation
452 unknown, endSteppingRange, functionFinished, signalReceived, breakpointHit
453 //watchpointTrigger, readWatchpointTrigger, accessWatchpointTrigger, watchpointScope, locationReached,
454 //exited, exitedNormally, exitedSignalled;
458 none, internalMain, internalWinMain, internalModulesLoaded, user, runToCursor, internalModuleLoad;
460 property bool isInternal { get { return (this == internalMain || this == internalWinMain || this == internalModulesLoaded || this == internalModuleLoad); } };
461 property bool isUser { get { return (this == user || this == runToCursor); } };
463 enum DebuggerEvaluationError { none, symbolNotFound, memoryCantBeRead, unknown };
464 enum DebuggerUserAction { none, start, resume, _break, stop, restart, selectThread, selectFrame, stepInto, stepOver, stepOut, runToCursor };
466 FileDialog debuggerFileDialog { type = selectDir };
468 static DualPipe vgTargetHandle;
469 static File vgLogFile;
470 static char vgLogPath[MAX_LOCATION];
471 static DualPipe gdbHandle;
472 static DebugEvaluationData eval { };
474 static int targetProcessId;
476 static bool gdbReady;
477 static bool breakpointError;
481 Semaphore serialSemaphore { };
487 bool sentBreakInsert;
488 bool ignoreBreakpoints;
489 bool userBreakOnInternalBreakpoint;
490 //bool runToCursorDebugStart;
499 int activeFrameLevel;
508 DebuggerUserAction userAction;
511 DebuggerAction breakType;
512 //DebuggerCommand lastCommand; // THE COMPILER COMPILES STUFF THAT DOES NOT EXIST???
514 GdbDataStop stopItem;
515 GdbDataBreakpoint bpItem;
518 List<Breakpoint> sysBPs { };
519 Breakpoint bpRunToCursor;
523 CompilerConfig currentCompiler;
524 ProjectConfig prjConfig;
527 CodeEditor codeEditor;
529 ValgrindLogThread vgLogThread { debugger = this };
530 ValgrindTargetThread vgTargetThread { debugger = this };
531 GdbThread gdbThread { debugger = this };
534 delay = 0.0, userData = this;
538 bool monitor = false;
539 DebuggerEvent curEvent = event;
540 GdbDataStop stopItem = this.stopItem;
541 Breakpoint bpUser = null;
542 Breakpoint bpInternal = null;
549 this.stopItem = null;
552 if(curEvent && curEvent != exit)
555 _dpl(0, "No stop item");
563 Restart(currentCompiler, prjConfig, bitDepth, usingValgrind);
572 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
573 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
574 if(activeFrame.level == activeFrameLevel)
580 // GdbCommand(false, "-break-info %s", bpItem.number);
590 activeThread = stopItem.threadid;
591 GdbCommand(false, "-thread-list-ids");
597 Breakpoint bp = stopItem ? GetBreakpointById(stopItem.bkptno, &isInternal) : null;
598 if(bp && bp.inserted && bp.bp.addr)
600 if(bp.type.isInternal)
604 if(stopItem && stopItem.frame)
606 if(bpInternal && bpRunToCursor && bpRunToCursor.inserted && !strcmp(bpRunToCursor.bp.addr, bp.bp.addr))
607 bpUser = bpRunToCursor;
610 for(item : (bpInternal ? ide.workspace.breakpoints : sysBPs); item.inserted)
612 if(item.bp && item.bp.addr && !strcmp(item.bp.addr, bp.bp.addr))
624 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Invalid stopItem!");
625 if(bpUser && strcmp(stopItem.frame.addr, bpUser.bp.addr))
629 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Breakpoint bkptno(", stopItem.bkptno, ") invalid or not found!");
630 if(bpUser && bpUser.type == runToCursor)
631 ignoreBreakpoints = false;
632 if((bpUser && !ignoreBreakpoints) || (bpInternal && userBreakOnInternalBreakpoint))
634 hitThread = stopItem.threadid;
638 signalThread = stopItem.threadid;
642 ignoreBreakpoints = false;
644 case valgrindStartPause:
645 GdbExecContinue(true);
654 activeThread = stopItem.threadid;
655 GdbCommand(false, "-thread-list-ids");
657 if(activeFrameLevel > 0)
658 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
660 WatchesCodeEditorLinkInit();
664 if(curEvent == signal)
668 ide.outputView.debugBox.Logf($"Signal received: %s - %s\n", stopItem.name, stopItem.meaning);
669 ide.outputView.debugBox.Logf(" %s:%d\n", (s = CopySystemPath(stopItem.frame.file)), stopItem.frame.line);
670 ide.outputView.Show();
671 ide.callStackView.Show();
674 else if(curEvent == breakEvent)
676 ide.threadsView.Show();
677 ide.callStackView.Show();
678 ide.callStackView.Activate();
681 if(monitor && curEvent.canBeMonitored)
683 SelectFrame(activeFrameLevel);
684 GoToStackFrameLine(activeFrameLevel, true, false);
685 ide.ShowCodeEditor();
686 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
691 EventHit(stopItem, bpInternal, bpUser);
698 if(userBreakOnInternalBreakpoint)
699 userBreakOnInternalBreakpoint = false;
704 #ifdef GDB_DEBUG_CONSOLE
705 char lastGdbOutput[GdbGetLineSize];
707 #if defined(__unix__)
708 ProgramThread progThread { };
711 void ChangeState(DebuggerState value)
713 bool same = value == state;
714 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ChangeState (", state, same ? " *** == *** " : " -> ", value, ")");
716 if(!same && ide) ide.AdjustDebugMenus();
721 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::CleanUp");
723 stackFrames.Free(Frame::Free);
733 waitingForPID = false;
738 sentBreakInsert = false;
739 ignoreBreakpoints = false;
740 userBreakOnInternalBreakpoint = false;
741 //runToCursorDebugStart = false;
744 activeFrameLevel = 0;
761 bpRunToCursor = null;
763 delete currentCompiler;
767 /*GdbThread gdbThread
773 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::constructor");
774 ideProcessId = Process_GetCurrentProcessId();
776 sysBPs.Add(Breakpoint { type = internalMain, function = "main", enabled = true, level = -1 });
777 #if defined(__WIN32__)
778 sysBPs.Add(Breakpoint { type = internalWinMain, function = "WinMain", enabled = true, level = -1 });
780 sysBPs.Add(Breakpoint { type = internalModulesLoaded, enabled = true, level = -1 });
781 sysBPs.Add(Breakpoint { type = internalModuleLoad, function = "InternalModuleLoadBreakpoint", enabled = true, level = -1 });
786 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::destructor");
794 property bool isActive { get { return state == running || state == stopped; } }
795 property bool isPrepared { get { return state == loaded || state == running || state == stopped; } }
799 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Resume");
801 GdbExecContinue(true);
806 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Break");
811 GdbDebugBreak(false);
817 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Stop");
825 GdbDebugBreak(false);
839 void Restart(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
841 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Restart");
842 userAction = restart;
843 if(StartSession(compiler, config, bitDepth, useValgrind, true, false, false/*, false*/) == loaded)
847 bool GoToCodeLine(char * location)
850 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GoToCodeLine(", location, ")");
851 codloc = CodeLocation::ParseCodeLocation(location);
854 CodeEditor editor = (CodeEditor)ide.OpenFile(codloc.absoluteFile, normal, true, null, no, normal, false);
857 EditBox editBox = editor.editBox;
858 editBox.GoToLineNum(codloc.line - 1);
859 editBox.GoToPosition(editBox.line, codloc.line - 1, 0);
866 bool GoToStackFrameLine(int stackLevel, bool askForLocation, bool fromCallStack)
868 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GoToStackFrameLine(", stackLevel, ", ", askForLocation, ")");
871 char filePath[MAX_LOCATION];
872 char sourceDir[MAX_LOCATION];
874 CodeEditor editor = null;
875 if(stackLevel == -1) // this (the two lines) is part of that fix that I would not put in for some time
877 for(frame = stackFrames.first; frame; frame = frame.next)
878 if(frame.level == stackLevel)
883 ide.callStackView.Show();
885 if(frame.absoluteFile)
886 editor = (CodeEditor)ide.OpenFile(frame.absoluteFile, normal, true, null, no, normal, false);
887 if(!editor && frame.file)
888 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
889 if(!frame.absoluteFile && askForLocation && frame.file)
892 char title[MAX_LOCATION];
893 snprintf(title, sizeof(title), $"Provide source file location for %s", (s = CopySystemPath(frame.file)));
894 title[sizeof(title)-1] = 0;
896 if(SourceDirDialog(title, ide.workspace.projectDir, frame.file, sourceDir))
898 AddSourceDir(sourceDir);
899 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
902 if(!editor && frame.absoluteFile)
903 editor = (CodeEditor)ide.OpenFile(frame.absoluteFile, normal, true, null, no, normal, false);
905 ide.RepositionWindows(false);
907 if(editor && frame.line)
909 EditBox editBox = editor.editBox;
910 editBox.GoToLineNum(frame.line - 1);
911 editBox.GoToPosition(editBox.line, frame.line - 1, 0);
919 void SelectThread(int thread)
921 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SelectThread(", thread, ")");
922 userAction = selectThread;
925 if(thread != activeThread)
927 activeFrameLevel = -1;
928 ide.callStackView.Clear();
929 GdbCommand(false, "-thread-select %d", thread);
931 // Why was SelectFrame missing here?
932 SelectFrame(activeFrameLevel);
933 GoToStackFrameLine(activeFrameLevel, true, false);
934 WatchesCodeEditorLinkRelease();
935 WatchesCodeEditorLinkInit();
939 ide.callStackView.Show();
943 void SelectFrame(int frame)
945 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SelectFrame(", frame, ")");
946 userAction = selectFrame; // not always user action, right? doesn't matter for now.
949 if(frame != activeFrameLevel || !codeEditor || !codeEditor.visible)
951 activeFrameLevel = frame; // there is no active frame number in the gdb reply
952 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
953 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
954 if(activeFrame.level == activeFrameLevel)
957 WatchesCodeEditorLinkRelease();
958 WatchesCodeEditorLinkInit();
965 void HandleExit(char * reason, char * code)
967 bool returnedExitCode = false;
968 char verboseExitCode[128];
970 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::HandleExit(", reason, ", ", code, ")");
971 ChangeState(loaded); // this state change seems to be superfluous, might be in case of gdb crash
976 snprintf(verboseExitCode, sizeof(verboseExitCode), $" with exit code %s", code);
977 verboseExitCode[sizeof(verboseExitCode)-1] = 0;
980 verboseExitCode[0] = '\0';
984 // ClearBreakDisplay();
988 for(wh : ide.workspace.watches)
990 if(wh.type) FreeType(wh.type);
993 ide.watchesView.UpdateWatch(wh);
997 #if defined(__unix__)
1000 progThread.terminate = true;
1003 fifoFile.CloseInput();
1013 char program[MAX_LOCATION];
1014 GetSystemPathBuffer(program, targetFile);
1016 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
1017 else if(!strcmp(reason, "exited-normally"))
1018 ide.outputView.debugBox.Logf($"The program %s has exited normally%s.\n", program, verboseExitCode);
1019 else if(!strcmp(reason, "exited"))
1020 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
1021 else if(!strcmp(reason, "exited-signalled"))
1022 ide.outputView.debugBox.Logf($"The program %s has exited with a signal%s.\n", program, verboseExitCode);
1024 ide.outputView.debugBox.Logf($"The program %s has exited (gdb provided an unknown reason)%s.\n", program, verboseExitCode);
1029 DebuggerState StartSession(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool restart, bool userBreakOnInternalBreakpoint, bool ignoreBreakpoints/*, bool runToCursorDebugStart*/)
1031 DebuggerState result = none;
1032 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StartSession(restart(", restart, "), userBreakOnInternalBreakpoint(", userBreakOnInternalBreakpoint, "), ignoreBreakpoints(", ignoreBreakpoints, ")"/*, runToCursorDebugStart(", runToCursorDebugStart, ")"*/);
1033 if(restart && state == running && targetProcessId)
1035 breakType = DebuggerAction::restart;
1036 GdbDebugBreak(false);
1040 if(restart && state == stopped)
1042 if(needReset && state == loaded)
1043 GdbExit(); // this reset is to get a clean state with all the breakpoints until a better state can be maintained on program exit
1045 if(result == none || result == terminated)
1047 ide.outputView.ShowClearSelectTab(debug);
1048 ide.outputView.debugBox.Logf($"Starting debug mode\n");
1055 for(bp : ide.workspace.breakpoints)
1061 //this.runToCursorDebugStart = runToCursorDebugStart;
1063 if(GdbInit(compiler, config, bitDepth, useValgrind))
1068 this.ignoreBreakpoints = ignoreBreakpoints;
1069 this.userBreakOnInternalBreakpoint = userBreakOnInternalBreakpoint;
1070 if(result == loaded || result == stopped)
1071 GdbBreakpointsDelete(false, (userAction == stepOver || userAction == stepOut), ignoreBreakpoints);
1076 void Start(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
1078 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Start()");
1080 if(StartSession(compiler, config, bitDepth, useValgrind, true, false, false/*, false*/) == loaded)
1084 void StepInto(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
1086 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepInto()");
1087 userAction = stepInto;
1088 switch(StartSession(compiler, config, bitDepth, useValgrind, false, true, false/*, false*/))
1090 case loaded: GdbExecRun(); break;
1091 case stopped: GdbExecStep(); break;
1095 void StepOver(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool ignoreBreakpoints)
1097 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepOver()");
1098 userAction = stepOver;
1099 switch(StartSession(compiler, config, bitDepth, useValgrind, false, true, ignoreBreakpoints/*, false*/))
1101 case loaded: GdbExecRun(); break;
1102 case stopped: GdbExecNext(); break;
1106 void StepOut(bool ignoreBreakpoints)
1108 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepOut()");
1109 userAction = stepOut;
1110 if(state == stopped)
1112 this.ignoreBreakpoints = ignoreBreakpoints;
1113 GdbBreakpointsDelete(true, true, ignoreBreakpoints);
1117 GdbExecContinue(true);
1121 void RunToCursor(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, char * absoluteFilePath, int lineNumber, bool ignoreBreakpoints, bool atSameLevel)
1123 char relativeFilePath[MAX_LOCATION];
1124 DebuggerState st = state;
1125 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::RunToCursor()");
1126 userAction = runToCursor;
1129 // ide.outputView.ShowClearSelectTab(debug);
1130 // ide.outputView.debugBox.Logf($"Starting debug mode\n");
1132 if(!ide.projectView.project.GetRelativePath(absoluteFilePath, relativeFilePath))
1133 strcpy(relativeFilePath, absoluteFilePath);
1135 if(bpRunToCursor && bpRunToCursor.inserted && symbols)
1137 UnsetBreakpoint(bpRunToCursor);
1138 delete bpRunToCursor;
1141 bpRunToCursor = Breakpoint { };
1142 bpRunToCursor.absoluteFilePath = absoluteFilePath;
1143 bpRunToCursor.relativeFilePath = relativeFilePath;
1144 bpRunToCursor.line = lineNumber;
1145 bpRunToCursor.type = runToCursor;
1146 bpRunToCursor.enabled = true;
1147 bpRunToCursor.level = atSameLevel ? frameCount - activeFrameLevel -1 : -1;
1149 switch(StartSession(compiler, config, bitDepth, useValgrind, false, false, ignoreBreakpoints/*, true*/))
1155 GdbExecContinue(true);
1160 void GetCallStackCursorLine(bool * error, int * lineCursor, int * lineTopFrame)
1162 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetCallStackCursorLine()");
1163 if(activeFrameLevel == -1)
1171 *error = signalOn && activeThread == signalThread;
1172 *lineCursor = activeFrameLevel - ((frameCount > 192 && activeFrameLevel > 191) ? frameCount - 192 - 1 : 0) + 1;
1173 *lineTopFrame = activeFrameLevel ? 1 : 0;
1177 int GetMarginIconsLineNumbers(char * fileName, int lines[], bool enabled[], int max, bool * error, int * lineCursor, int * lineTopFrame)
1179 char winFilePath[MAX_LOCATION];
1180 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1182 Iterator<Breakpoint> it { ide.workspace.breakpoints };
1183 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetMarginIconsLineNumbers()");
1184 while(it.Next() && count < max)
1186 Breakpoint bp = it.data;
1189 if(bp.absoluteFilePath && bp.absoluteFilePath[0] && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1191 lines[count] = bp.line;
1192 enabled[count] = bp.enabled;
1197 if(activeFrameLevel == -1)
1205 *error = signalOn && activeThread == signalThread;
1206 if(activeFrame && activeFrame.absoluteFile && !fstrcmp(absoluteFilePath, activeFrame.absoluteFile))
1207 *lineCursor = activeFrame.line;
1210 if(activeFrame && stopItem && stopItem.frame && activeFrame.level == stopItem.frame.level)
1212 else if(stopItem && stopItem.frame && stopItem.frame.absoluteFile && !fstrcmp(absoluteFilePath, stopItem.frame.absoluteFile))
1213 *lineTopFrame = stopItem.frame.line;
1217 if(*lineTopFrame == *lineCursor && *lineTopFrame)
1223 void ChangeWatch(DataRow row, char * expression)
1225 Watch wh = (Watch)row.tag;
1226 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ChangeWatch(", expression, ")");
1229 delete wh.expression;
1231 wh.expression = CopyString(expression);
1234 Iterator<Watch> it { ide.workspace.watches };
1236 ide.workspace.watches.Delete(it.pointer);
1242 row.tag = (int64)wh;
1243 ide.workspace.watches.Add(wh);
1245 wh.expression = CopyString(expression);
1247 ide.workspace.Save();
1248 //if(expression && state == stopped)
1253 void MoveIcons(char * fileName, int lineNumber, int move, bool start)
1255 char winFilePath[MAX_LOCATION];
1256 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1259 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::MoveIcons()");
1260 for(bpLink = ide.workspace.breakpoints.first; bpLink; bpLink = next)
1262 Breakpoint bp = (Breakpoint)bpLink.data;
1265 if(bp.type == user && bp.absoluteFilePath && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1267 if(bp.line > lineNumber || (bp.line == lineNumber && start))
1269 if(move < 0 && (bp.line < lineNumber - move))
1270 ide.workspace.RemoveBreakpoint(bp);
1274 ide.breakpointsView.UpdateBreakpoint(bp.row);
1275 ide.workspace.Save();
1281 // moving code cursors is futile, on next step, stop, hit, cursors will be offset anyways
1284 bool SourceDirDialog(char * title, char * startDir, char * test, char * sourceDir)
1288 String srcDir = null;
1290 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SourceDirDialog()");
1291 debuggerFileDialog.text = title;
1292 debuggerFileDialog.currentDirectory = startDir;
1293 debuggerFileDialog.master = ide;
1295 while(debuggerFileDialog.Modal())
1297 strcpy(sourceDir, debuggerFileDialog.filePath);
1298 if(!fstrcmp(ide.workspace.projectDir, sourceDir) &&
1299 MessageBox { type = yesNo, master = ide,
1300 contents = $"This is the project directory.\nWould you like to try again?",
1301 text = $"Invalid Source Directory" }.Modal() == no)
1305 for(dir : ide.workspace.sourceDirs)
1307 if(!fstrcmp(dir, sourceDir))
1315 MessageBox { type = yesNo, master = ide,
1316 contents = $"This source directory is already specified.\nWould you like to try again?",
1317 text = $"Invalid Source Directory" }.Modal() == no)
1323 char file[MAX_LOCATION];
1324 strcpy(file, sourceDir);
1325 PathCat(file, test);
1326 result = FileExists(file);
1328 MessageBox { type = yesNo, master = ide,
1329 contents = $"Unable to locate source file.\nWould you like to try again?",
1330 text = $"Invalid Source Directory" }.Modal() == no)
1344 void AddSourceDir(char * sourceDir)
1346 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::AddSourceDir(", sourceDir, ")");
1347 ide.workspace.sourceDirs.Add(CopyString(sourceDir));
1348 ide.workspace.Save();
1352 DebuggerState oldState = state;
1357 GdbDebugBreak(true);
1360 GdbCommand(false, "-environment-directory \"%s\"", sourceDir);
1363 if(oldState == running)
1364 GdbExecContinue(false);
1368 void ToggleBreakpoint(char * fileName, int lineNumber, Project prj)
1370 char winFilePath[MAX_LOCATION];
1371 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1372 char absolutePath[MAX_LOCATION];
1373 char relativePath[MAX_LOCATION];
1374 char sourceDir[MAX_LOCATION];
1375 Breakpoint bp = null;
1377 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ToggleBreakpoint(", fileName, ":", lineNumber, ")");
1378 strcpy(absolutePath, absoluteFilePath);
1379 for(i : ide.workspace.breakpoints; i.type == user && i.absoluteFilePath && !fstrcmp(i.absoluteFilePath, absolutePath) && i.line == lineNumber)
1388 ide.workspace.RemoveBreakpoint(bp);
1396 // FIXED: This is how it should have been... Source locations are only for files not in project
1397 // if(IsPathInsideOf(absolutePath, ide.workspace.projectDir))
1398 // MakePathRelative(absolutePath, ide.workspace.projectDir, relativePath);
1399 bool result = false;
1401 result = prj.GetRelativePath(absolutePath, relativePath);
1403 result = ide.projectView.project.GetRelativePath(absolutePath, relativePath);
1404 //if(ide.projectView.project.GetRelativePath(absolutePath, relativePath));
1408 char title[MAX_LOCATION];
1409 char directory[MAX_LOCATION];
1410 StripLastDirectory(absolutePath, directory);
1411 snprintf(title, sizeof(title), $"Provide source files location directory for %s", absolutePath);
1412 title[sizeof(title)-1] = 0;
1415 String srcDir = null;
1416 for(dir : ide.workspace.sourceDirs)
1418 if(IsPathInsideOf(absolutePath, dir))
1420 MakePathRelative(absoluteFilePath, dir, relativePath);
1428 if(SourceDirDialog(title, directory, null, sourceDir))
1430 if(IsPathInsideOf(absolutePath, sourceDir))
1432 AddSourceDir(sourceDir);
1433 MakePathRelative(absoluteFilePath, sourceDir, relativePath);
1436 else if(MessageBox { type = yesNo, master = ide,
1437 contents = $"You must provide a valid source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1438 text = $"Invalid Source Directory" }.Modal() == no)
1441 else if(MessageBox { type = yesNo, master = ide,
1442 contents = $"You must provide a source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1443 text = $"No Source Directory Provided" }.Modal() == no)
1447 ide.workspace.bpCount++;
1448 bp = { line = lineNumber, type = user, enabled = true, level = -1 };
1449 ide.workspace.breakpoints.Add(bp);
1450 bp.absoluteFilePath = absolutePath;
1451 bp.relativeFilePath = relativePath;
1452 ide.breakpointsView.AddBreakpoint(bp);
1457 DebuggerState oldState = state;
1462 GdbDebugBreak(true);
1465 SetBreakpoint(bp, false);
1468 if(oldState == running)
1469 GdbExecContinue(false);
1472 ide.workspace.Save();
1475 void UpdateRemovedBreakpoint(Breakpoint bp)
1477 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::UpdateRemovedBreakpoint()");
1478 if(targeted && bp.inserted)
1480 DebuggerState oldState = state;
1485 GdbDebugBreak(true);
1488 UnsetBreakpoint(bp);
1491 if(oldState == running)
1492 GdbExecContinue(false);
1498 void ParseFrame(Frame frame, char * string)
1501 Array<char *> frameTokens { minAllocSize = 50 };
1502 Array<char *> argsTokens { minAllocSize = 50 };
1503 Array<char *> argumentTokens { minAllocSize = 50 };
1504 DebugListItem item { };
1507 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ParseFrame()");
1508 TokenizeList(string, ',', frameTokens);
1509 for(i = 0; i < frameTokens.count; i++)
1511 if(TokenizeListItem(frameTokens[i], item))
1513 StripQuotes(item.value, item.value);
1514 if(!strcmp(item.name, "level"))
1515 frame.level = atoi(item.value);
1516 else if(!strcmp(item.name, "addr"))
1517 frame.addr = item.value;
1518 else if(!strcmp(item.name, "func"))
1519 frame.func = item.value;
1520 else if(!strcmp(item.name, "args"))
1522 if(!strcmp(item.value, "[]"))
1523 frame.argsCount = 0;
1526 item.value = StripBrackets(item.value);
1527 TokenizeList(item.value, ',', argsTokens);
1528 for(j = 0; j < argsTokens.count; j++)
1530 argsTokens[j] = StripCurlies(argsTokens[j]);
1531 TokenizeList(argsTokens[j], ',', argumentTokens);
1532 for(k = 0; k < argumentTokens.count; k++)
1535 frame.args.Add(arg);
1536 if(TokenizeListItem(argumentTokens[k], item))
1538 if(!strcmp(item.name, "name"))
1540 StripQuotes(item.value, item.value);
1541 arg.name = item.value;
1543 else if(!strcmp(item.name, "value"))
1545 StripQuotes(item.value, item.value);
1546 arg.val = item.value;
1549 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "frame args item (", item.name, "=", item.value, ") is unheard of");
1552 _dpl(0, "Bad frame args item");
1554 argumentTokens.RemoveAll();
1556 frame.argsCount = argsTokens.count;
1557 argsTokens.RemoveAll();
1560 else if(!strcmp(item.name, "from"))
1561 frame.from = item.value;
1562 else if(!strcmp(item.name, "file"))
1563 frame.file = item.value;
1564 else if(!strcmp(item.name, "line"))
1565 frame.line = atoi(item.value);
1566 else if(!strcmp(item.name, "fullname"))
1567 frame.absoluteFile = item.value;
1569 // GDB 6.3 on OS X is giving "fullname" and "dir", all in absolute, but file name only in 'file'
1570 String path = ide.workspace.GetPathWorkspaceRelativeOrAbsolute(item.value);
1571 if(strcmp(frame.file, path))
1574 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1579 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "frame member (", item.name, "=", item.value, ") is unheard of");
1582 _dpl(0, "Bad frame");
1587 delete argumentTokens;
1591 Breakpoint GetBreakpointById(int id, bool * isInternal)
1593 Breakpoint bp = null;
1594 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetBreakpointById(", id, ")");
1596 *isInternal = false;
1599 for(i : sysBPs; i.bp && i.bp.id == id)
1606 if(!bp && bpRunToCursor && bpRunToCursor.bp && bpRunToCursor.bp.id == id)
1610 for(i : ide.workspace.breakpoints; i.bp && i.bp.id == id)
1620 GdbDataBreakpoint ParseBreakpoint(char * string, Array<char *> outTokens)
1623 GdbDataBreakpoint bp { };
1624 DebugListItem item { };
1625 Array<char *> bpTokens { minAllocSize = 16 };
1626 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ParseBreakpoint()");
1627 string = StripCurlies(string);
1628 TokenizeList(string, ',', bpTokens);
1629 for(i = 0; i < bpTokens.count; i++)
1631 if(TokenizeListItem(bpTokens[i], item))
1633 StripQuotes(item.value, item.value);
1634 if(!strcmp(item.name, "number"))
1636 if(!strchr(item.value, '.'))
1637 bp.id = atoi(item.value);
1638 bp.number = item.value;
1640 else if(!strcmp(item.name, "type"))
1641 bp.type = item.value;
1642 else if(!strcmp(item.name, "disp"))
1643 bp.disp = item.value;
1644 else if(!strcmp(item.name, "enabled"))
1645 bp.enabled = (!strcmpi(item.value, "y"));
1646 else if(!strcmp(item.name, "addr"))
1648 if(outTokens && !strcmp(item.value, "<MULTIPLE>"))
1651 Array<GdbDataBreakpoint> bpArray = bp.multipleBPs = { };
1652 while(outTokens.count > ++c)
1654 GdbDataBreakpoint multBp = ParseBreakpoint(outTokens[c], null);
1655 bpArray.Add(multBp);
1659 bp.addr = item.value;
1661 else if(!strcmp(item.name, "func"))
1662 bp.func = item.value;
1663 else if(!strcmp(item.name, "file"))
1664 bp.file = item.value;
1665 else if(!strcmp(item.name, "fullname"))
1666 bp.fullname = item.value;
1667 else if(!strcmp(item.name, "line"))
1668 bp.line = atoi(item.value);
1669 else if(!strcmp(item.name, "at"))
1671 else if(!strcmp(item.name, "times"))
1672 bp.times = atoi(item.value);
1673 else if(!strcmp(item.name, "original-location") || !strcmp(item.name, "thread-groups"))
1674 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "breakpoint member (", item.name, "=", item.value, ") is ignored");
1676 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "breakpoint member (", item.name, "=", item.value, ") is unheard of");
1682 void ShowDebuggerViews()
1684 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ShowDebuggerViews()");
1685 ide.outputView.Show();
1686 ide.outputView.SelectTab(debug);
1687 ide.threadsView.Show();
1688 ide.callStackView.Show();
1689 ide.watchesView.Show();
1693 void HideDebuggerViews()
1695 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::HideDebuggerViews()");
1696 ide.RepositionWindows(true);
1699 void ::GdbCommand(bool focus, char * format, ...)
1703 // TODO: Improve this limit
1704 static char string[MAX_F_STRING*4];
1706 va_start(args, format);
1707 vsnprintf(string, sizeof(string), format, args);
1708 string[sizeof(string)-1] = 0;
1712 ide.debugger.serialSemaphore.TryWait();
1714 #ifdef GDB_DEBUG_CONSOLE
1715 _dpl2(_dpct, dplchan::gdbCommand, 0, string);
1717 #ifdef GDB_DEBUG_OUTPUT
1718 ide.outputView.gdbBox.Logf("cmd: %s\n", string);
1720 #ifdef GDB_DEBUG_GUI
1722 ide.gdbDialog.AddCommand(string);
1725 strcat(string,"\n");
1726 gdbHandle.Puts(string);
1729 Process_ShowWindows(targetProcessId);
1732 ide.debugger.serialSemaphore.Wait();
1737 bool ValidateBreakpoint(Breakpoint bp)
1739 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ValidateBreakpoint()");
1740 if(modules && bp.line && bp.bp)
1742 if(bp.bp.line != bp.line)
1748 ide.outputView.debugBox.Logf("WOULD HAVE -- Invalid breakpoint disabled: %s:%d\n", bp.relativeFilePath, bp.line);
1750 //UnsetBreakpoint(bp);
1751 //bp.enabled = false;
1757 ide.outputView.debugBox.Logf("Debugger Error: ValidateBreakpoint error\n");
1758 bp.line = bp.bp.line;
1765 void GdbBreakpointsInsert()
1767 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbBreakpointsInsert()");
1770 if(userAction != stepOut && (userAction != stepOver || state == loaded))
1772 DirExpression objDir = ide.project.GetObjDir(currentCompiler, prjConfig, bitDepth);
1773 for(bp : sysBPs; !bp.inserted)
1775 bool insert = false;
1776 if(bp.type == internalModulesLoaded)
1778 char path[MAX_LOCATION];
1779 char name[MAX_LOCATION];
1780 char fixedModuleName[MAX_FILENAME];
1783 bool moduleLoadBlock = false;
1785 ReplaceSpaces(fixedModuleName, ide.project.moduleName);
1786 snprintf(name, sizeof(name),"%s.main.ec", fixedModuleName);
1787 name[sizeof(name)-1] = 0;
1788 strcpy(path, ide.workspace.projectDir);
1789 PathCatSlash(path, objDir.dir);
1790 PathCatSlash(path, name);
1791 f = FileOpen(path, read);
1794 for(lineNumber = 1; !f.Eof(); lineNumber++)
1796 if(f.GetLine(line, sizeof(line) - 1))
1798 bool moduleLoadLine;
1799 TrimLSpaces(line, line);
1800 moduleLoadLine = !strncmp(line, "eModule_Load", strlen("eModule_Load"));
1801 if(!moduleLoadBlock && moduleLoadLine)
1802 moduleLoadBlock = true;
1803 else if(moduleLoadBlock && !moduleLoadLine && strlen(line) > 0)
1809 char relative[MAX_LOCATION];
1810 bp.absoluteFilePath = path;
1811 MakePathRelative(path, ide.workspace.projectDir, relative);
1812 bp.relativeFilePath = relative;
1813 bp.line = lineNumber;
1819 else if(bp.type == internalModuleLoad)
1823 for(prj : ide.workspace.projects)
1825 if(!strcmp(prj.moduleName, "ecere"))
1827 ProjectNode node = prj.topNode.Find("instance.c", false);
1830 char path[MAX_LOCATION];
1831 char relative[MAX_LOCATION];
1832 node.GetFullFilePath(path);
1833 bp.absoluteFilePath = path;
1834 MakePathRelative(path, prj.topNode.path, relative);
1835 bp.relativeFilePath = relative;
1846 SetBreakpoint(bp, false);
1851 if(bpRunToCursor && !bpRunToCursor.inserted)
1852 SetBreakpoint(bpRunToCursor, false);
1854 if(!ignoreBreakpoints)
1856 for(bp : ide.workspace.breakpoints; !bp.inserted && bp.type == user)
1860 if(!SetBreakpoint(bp, false))
1861 SetBreakpoint(bp, true);
1869 bp.bp = GdbDataBreakpoint { };
1876 void UnsetBreakpoint(Breakpoint bp)
1878 char * s; _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::UnsetBreakpoint(", s=bp.CopyLocationString(false), ")"); delete s;
1879 if(symbols && bp.inserted)
1881 GdbCommand(false, "-break-delete %s", bp.bp.number);
1882 bp.inserted = false;
1887 bool SetBreakpoint(Breakpoint bp, bool removePath)
1889 char * s; _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SetBreakpoint(", s=bp.CopyLocationString(false), ", ", removePath ? "**** removePath(true) ****" : "", ")"); delete s;
1890 breakpointError = false;
1893 char * location = bp.CopyLocationString(removePath);
1894 sentBreakInsert = true;
1895 GdbCommand(false, "-break-insert %s", location);
1897 if(!breakpointError)
1899 if(bpItem && bpItem.multipleBPs && bpItem.multipleBPs.count)
1902 GdbDataBreakpoint first = null;
1903 for(n : bpItem.multipleBPs)
1905 if(!fstrcmp(n.fullname, bp.absoluteFilePath))
1915 GdbCommand(false, "-break-disable %s", n.number);
1919 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error breakpoint already disabled.");
1924 bpItem.addr = first.addr;
1925 bpItem.func = first.func;
1926 bpItem.file = first.file;
1927 bpItem.fullname = first.fullname;
1928 bpItem.line = first.line;
1929 //bpItem.thread-groups = first.thread-groups;
1930 bpItem.multipleBPs.Free();
1931 delete bpItem.multipleBPs;
1934 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error multiple breakpoints all disabled.");
1936 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error multiple breakpoints in exact same file not supported.");
1940 bp.inserted = (bp.bp && bp.bp.number && strcmp(bp.bp.number, "0"));
1942 ValidateBreakpoint(bp);
1943 /*if(bp == bpRunToCursor)
1944 runToCursorDebugStart = false;*/
1947 return !breakpointError;
1950 void GdbBreakpointsDelete(bool deleteRunToCursor, bool deleteInternalBreakpoints, bool deleteUserBreakpoints)
1952 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbBreakpointsDelete(deleteRunToCursor(", deleteRunToCursor, "))");
1955 if(deleteInternalBreakpoints)
1958 UnsetBreakpoint(bp);
1960 if(deleteUserBreakpoints)
1962 for(bp : ide.workspace.breakpoints)
1963 UnsetBreakpoint(bp);
1965 if(deleteRunToCursor && bpRunToCursor)
1966 UnsetBreakpoint(bpRunToCursor);
1972 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbGetStack()");
1974 stackFrames.Free(Frame::Free);
1975 GdbCommand(false, "-stack-info-depth");
1977 GdbCommand(false, "-stack-info-depth 192");
1978 if(frameCount && frameCount <= 192)
1979 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 191));
1982 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 95));
1983 GdbCommand(false, "-stack-list-frames %d %d", Max(frameCount - 96, 96), frameCount - 1);
1985 GdbCommand(false, "");
1990 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbTargetSet()");
1993 char escaped[MAX_LOCATION];
1994 strescpy(escaped, targetFile);
1995 GdbCommand(false, "file \"%s\"", escaped); //GDB/MI Missing Implementation -symbol-file, -target-attach
2002 const char *vgdbCommand = "/usr/bin/vgdb"; // TODO: vgdb command config option
2003 //GdbCommand(false, "-target-select remote | %s --pid=%d", "vgdb", targetProcessId);
2004 printf("target remote | %s --pid=%d\n", vgdbCommand, targetProcessId);
2005 GdbCommand(false, "target remote | %s --pid=%d", vgdbCommand, targetProcessId); // TODO: vgdb command config option
2008 /*for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
2009 GdbCommand(false, "-environment-directory \"%s\"", prj.topNode.path);*/
2011 for(dir : ide.workspace.sourceDirs; dir && dir[0])
2013 bool interference = false;
2014 for(prj : ide.workspace.projects)
2016 if(!fstrcmp(prj.topNode.path, dir))
2018 interference = true;
2022 if(!interference && dir[0])
2023 GdbCommand(false, "-environment-directory \"%s\"", dir);
2031 /*void GdbTargetRelease()
2035 GdbBreakpointsDelete(true, true, true);
2036 GdbCommand(false, "file"); //GDB/MI Missing Implementation -target-detach
2042 void GdbDebugBreak(bool internal)
2044 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbDebugBreak()");
2048 breakType = DebuggerAction::internal;
2050 if(ide) ide.Update(null);
2052 if(Process_Break(targetProcessId)) //GdbCommand(false, "-exec-interrupt");
2053 serialSemaphore.Wait();
2056 ChangeState(loaded);
2057 targetProcessId = 0;
2062 ide.outputView.debugBox.Logf("Debugger Error: GdbDebugBreak with not target id should never happen\n");
2067 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecRun()");
2070 ShowDebuggerViews();
2072 GdbCommand(true, "-exec-continue");
2074 GdbCommand(true, "-exec-run");
2077 void GdbExecContinue(bool focus)
2079 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecContinue()");
2081 GdbCommand(focus, "-exec-continue");
2086 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecNext()");
2088 GdbCommand(true, "-exec-next");
2093 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecStep()");
2095 GdbCommand(true, "-exec-step");
2098 void GdbExecFinish()
2100 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecFinish()");
2102 GdbCommand(true, "-exec-finish");
2105 void GdbExecCommon()
2107 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecCommon()");
2108 GdbBreakpointsInsert();
2111 #ifdef GDB_DEBUG_GUI
2112 void SendGDBCommand(char * command)
2114 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SendGDBCommand()");
2115 DebuggerState oldState = state;
2120 GdbDebugBreak(true);
2123 GdbCommand(false, command);
2126 if(oldState == running)
2127 GdbExecContinue(false);
2131 void ClearBreakDisplay()
2133 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ClearBreakDisplay()");
2135 activeFrameLevel = -1;
2145 stackFrames.Free(Frame::Free);
2146 WatchesCodeEditorLinkRelease();
2147 ide.callStackView.Clear();
2148 ide.threadsView.Clear();
2154 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbAbortExec()");
2156 GdbCommand(false, "-interpreter-exec console \"kill\""); // should use -exec-abort -- GDB/MI implementation incomplete
2160 bool GdbInit(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
2163 char oldDirectory[MAX_LOCATION];
2164 char tempPath[MAX_LOCATION];
2165 char command[MAX_F_STRING*4];
2166 Project project = ide.project;
2167 DirExpression targetDirExp = project.GetTargetDir(compiler, config, bitDepth);
2168 PathBackup pathBackup { };
2170 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbInit()");
2171 if(currentCompiler != compiler)
2173 delete currentCompiler;
2174 currentCompiler = compiler;
2175 incref currentCompiler;
2178 this.bitDepth = bitDepth;
2179 usingValgrind = useValgrind;
2181 ChangeState(loaded);
2183 sentBreakInsert = false;
2184 breakpointError = false;
2185 ignoreBreakpoints = false;
2191 ide.outputView.ShowClearSelectTab(debug);
2192 ide.outputView.debugBox.Logf($"Starting debug mode\n");
2194 #ifdef GDB_DEBUG_OUTPUT
2195 ide.outputView.gdbBox.Logf("run: Starting GDB\n");
2198 strcpy(tempPath, ide.workspace.projectDir);
2199 PathCatSlash(tempPath, targetDirExp.dir);
2201 targetDir = CopyString(tempPath);
2202 project.CatTargetFileName(tempPath, compiler, config);
2204 targetFile = CopyString(tempPath);
2206 GetWorkingDir(oldDirectory, MAX_LOCATION);
2207 if(ide.workspace.debugDir && ide.workspace.debugDir[0])
2209 char temp[MAX_LOCATION];
2210 strcpy(temp, ide.workspace.projectDir);
2211 PathCatSlash(temp, ide.workspace.debugDir);
2212 ChangeWorkingDir(temp);
2215 ChangeWorkingDir(ide.workspace.projectDir);
2217 ide.SetPath(true, compiler, config, bitDepth);
2219 // TODO: This pollutes the environment, but at least it works
2220 // It shouldn't really affect the IDE as the PATH gets restored and other variables set for testing will unlikely cause problems
2221 // What is the proper solution for this? DualPipeOpenEnv?
2222 // gdb set environment commands don't seem to take effect
2223 for(e : ide.workspace.environmentVars)
2225 SetEnvironment(e.name, e.string);
2230 char * clArgs = ide.workspace.commandLineArgs;
2231 const char *valgrindCommand = "valgrind"; // TODO: valgrind command config option //TODO: valgrind options
2232 ValgrindLeakCheck vgLeakCheck = ide.workspace.vgLeakCheck;
2233 int vgRedzoneSize = ide.workspace.vgRedzoneSize;
2234 bool vgTrackOrigins = ide.workspace.vgTrackOrigins;
2235 vgLogFile = CreateTemporaryFile(vgLogPath, "ecereidevglog");
2239 vgLogThread.Create();
2243 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't open temporary log file for Valgrind output\n");
2246 if(result && !CheckCommandAvailable(valgrindCommand))
2248 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for Valgrind is not available.\n", valgrindCommand);
2253 char * vgRedzoneSizeFlag = vgRedzoneSize == -1 ? "" : PrintString(" --redzone-size=", vgRedzoneSize);
2254 sprintf(command, "%s --vgdb=yes --vgdb-error=0 --log-file=%s --leak-check=%s%s --track-origins=%s %s%s%s",
2255 valgrindCommand, vgLogPath, (char*)vgLeakCheck, vgRedzoneSizeFlag, vgTrackOrigins ? "yes" : "no", targetFile, clArgs ? " " : "", clArgs ? clArgs : "");
2256 if(vgRedzoneSize != -1)
2257 delete vgRedzoneSizeFlag;
2258 vgTargetHandle = DualPipeOpen(PipeOpenMode { output = true, /*error = true, */input = true }, command);
2261 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start Valgrind\n");
2267 incref vgTargetHandle;
2268 vgTargetThread.Create();
2270 targetProcessId = vgTargetHandle.GetProcessID();
2271 waitingForPID = false;
2272 if(!targetProcessId)
2274 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get Valgrind process ID\n");
2281 serialSemaphore.Wait();
2289 (compiler.targetPlatform == win32 && bitDepth == 64) ? "x86_64-w64-mingw32-gdb" :
2290 (compiler.targetPlatform == win32 && bitDepth == 32) ? "i686-w64-mingw32-gdb" :
2292 if(!CheckCommandAvailable(command))
2294 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for GDB is not available.\n", command);
2299 strcat(command, " -n -silent --interpreter=mi2"); //-async //\"%s\"
2301 gdbHandle = DualPipeOpen(PipeOpenMode { output = true, /*error = true, */input = true }, command);
2304 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start GDB\n");
2314 gdbProcessId = gdbHandle.GetProcessID();
2317 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get GDB process ID\n");
2324 serialSemaphore.Wait();
2327 GdbCommand(false, "-gdb-set verbose off");
2328 //GdbCommand(false, "-gdb-set exec-done-display on");
2329 GdbCommand(false, "-gdb-set step-mode off");
2330 GdbCommand(false, "-gdb-set unwindonsignal on");
2331 //GdbCommand(false, "-gdb-set shell on");
2332 GdbCommand(false, "set print elements 992");
2333 GdbCommand(false, "-gdb-set backtrace limit 100000");
2337 //ChangeState(terminated);
2343 #if defined(__unix__)
2345 CreateTemporaryDir(progFifoDir, "ecereide");
2346 strcpy(progFifoPath, progFifoDir);
2347 PathCat(progFifoPath, "ideprogfifo");
2348 if(!mkfifo(progFifoPath, 0600))
2350 //fileCreated = true;
2355 ide.outputView.debugBox.Logf(createFIFOMsg, progFifoPath);
2362 progThread.terminate = false;
2363 progThread.Create();
2367 #if defined(__WIN32__)
2368 GdbCommand(false, "-gdb-set new-console on");
2371 #if defined(__unix__)
2373 GdbCommand(false, "-inferior-tty-set %s", progFifoPath);
2377 GdbCommand(false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
2379 for(e : ide.workspace.environmentVars)
2381 GdbCommand(false, "set environment %s=%s", e.name, e.string);
2386 ChangeWorkingDir(oldDirectory);
2392 delete targetDirExp;
2398 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExit()");
2399 if(gdbHandle && gdbProcessId)
2401 GdbCommand(false, "-gdb-exit");
2416 ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
2422 for(bp : ide.workspace.breakpoints)
2424 bp.inserted = false;
2430 bp.inserted = false;
2435 bpRunToCursor.inserted = false;
2436 delete bpRunToCursor.bp;
2439 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2440 ClearBreakDisplay();
2443 #if defined(__unix__)
2444 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
2446 progThread.terminate = true;
2449 fifoFile.CloseInput();
2455 DeleteFile(progFifoPath);
2456 progFifoPath[0] = '\0';
2462 void WatchesCodeEditorLinkInit()
2464 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkInit()");
2466 char tempPath[MAX_LOCATION];
2467 char path[MAX_LOCATION];
2469 //void MakeFilePathProjectRelative(char * path, char * relativePath)
2470 if(!ide.projectView.project.GetRelativePath(activeFrame.file, tempPath))
2471 strcpy(tempPath, activeFrame.file);
2473 strcpy(path, ide.workspace.projectDir);
2474 PathCat(path, tempPath);
2475 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2478 for(srcDir : ide.workspace.sourceDirs)
2480 strcpy(path, srcDir);
2481 PathCat(path, tempPath);
2482 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2483 if(codeEditor) break;
2488 /*if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2489 activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);*/
2490 if(!activeFrame || !activeFrame.absoluteFile)
2493 codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal, false);
2496 codeEditor.inUseDebug = true;
2499 //watchesInit = true;
2502 void WatchesCodeEditorLinkRelease()
2504 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkRelease()");
2509 codeEditor.inUseDebug = false;
2510 if(!codeEditor.visible)
2511 codeEditor.Destroy(0);
2517 bool ResolveWatch(Watch wh)
2519 bool result = false;
2521 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ResolveWatch()");
2533 char watchmsg[MAX_F_STRING];
2534 if(state == stopped && !codeEditor)
2535 wh.value = CopyString($"No source file found for selected frame");
2536 //if(codeEditor && state == stopped || state != stopped)
2539 Module backupPrivateModule;
2540 Context backupContext;
2541 Class backupThisClass;
2545 backupPrivateModule = GetPrivateModule();
2546 backupContext = GetCurrentContext();
2547 backupThisClass = GetThisClass();
2550 SetPrivateModule(codeEditor.privateModule);
2551 SetCurrentContext(codeEditor.globalContext);
2552 SetTopContext(codeEditor.globalContext);
2553 SetGlobalContext(codeEditor.globalContext);
2554 SetGlobalData(&codeEditor.globalData);
2557 exp = ParseExpressionString(wh.expression);
2559 if(exp && !parseError)
2561 char expString[4096];
2563 PrintExpression(exp, expString);
2565 if(GetPrivateModule())
2568 DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2569 ProcessExpressionType(exp);
2571 wh.type = exp.expType;
2574 DebugComputeExpression(exp);
2575 if(ExpressionIsError(exp))
2577 GDBFallBack(exp, expString);
2580 /*if(exp.hasAddress)
2582 char temp[MAX_F_STRING];
2583 sprintf(temp, "0x%x", exp.address);
2584 wh.address = CopyString(temp);
2585 // wh.address = CopyStringf("0x%x", exp.address);
2590 Type dataType = exp.expType;
2593 char temp[MAX_F_STRING];
2594 switch(dataType.kind)
2597 sprintf(temp, "%i", exp.val.c);
2600 sprintf(temp, "%i", exp.val.s);
2605 sprintf(temp, "%i", exp.val.i);
2608 sprintf(temp, "%i", exp.val.i64);
2611 sprintf(temp, "%i", exp.val.p);
2616 long v = (long)exp.val.f;
2617 sprintf(temp, "%i", v);
2622 long v = (long)exp.val.d;
2623 sprintf(temp, "%i", v);
2628 wh.intVal = CopyString(temp);
2629 switch(dataType.kind)
2632 sprintf(temp, "0x%x", exp.val.c);
2635 sprintf(temp, "0x%x", exp.val.s);
2639 sprintf(temp, "0x%x", exp.val.i);
2642 sprintf(temp, "0x%x", exp.val.i64);
2645 sprintf(temp, "0x%x", exp.val.i64);
2648 sprintf(temp, "0x%x", exp.val.p);
2653 long v = (long)exp.val.f;
2654 sprintf(temp, "0x%x", v);
2659 long v = (long)exp.val.d;
2660 sprintf(temp, "0x%x", v);
2665 wh.hexVal = CopyString(temp);
2666 switch(dataType.kind)
2669 sprintf(temp, "0o%o", exp.val.c);
2672 sprintf(temp, "0o%o", exp.val.s);
2676 sprintf(temp, "0o%o", exp.val.i);
2679 sprintf(temp, "0o%o", exp.val.i64);
2682 sprintf(temp, "0o%o", exp.val.i64);
2685 sprintf(temp, "0o%o", exp.val.p);
2690 long v = (long)exp.val.f;
2691 sprintf(temp, "0o%o", v);
2696 long v = (long)exp.val.d;
2697 sprintf(temp, "0o%o", v);
2702 wh.octVal = CopyString(temp);
2705 // WHATS THIS HERE ?
2706 if(exp.type == constantExp && exp.constant)
2707 wh.constant = CopyString(exp.constant);
2713 case symbolErrorExp:
2714 snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2716 case structMemberSymbolErrorExp:
2717 // todo get info as in next case (ExpClassMemberSymbolError)
2718 snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2720 case classMemberSymbolErrorExp:
2723 Expression memberExp = exp.member.exp;
2724 Identifier memberID = exp.member.member;
2725 Type type = memberExp.expType;
2728 _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2731 char string[256] = "";
2733 PrintTypeNoConst(type, string, false, true);
2734 classSym = FindClass(string);
2735 _class = classSym ? classSym.registered : null;
2738 snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2740 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2743 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2746 case memoryErrorExp:
2747 // Need to ensure when set to memoryErrorExp, constant is set
2748 snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2750 case dereferenceErrorExp:
2751 snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2753 case unknownErrorExp:
2754 snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2756 case noDebuggerErrorExp:
2757 snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2759 case debugStateErrorExp:
2760 snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2763 snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2767 // Temporary Code for displaying Strings
2768 if((exp.expType && ((exp.expType.kind == pointerType ||
2769 exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) ||
2770 (wh.type && wh.type.kind == classType && wh.type._class &&
2771 wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2772 !strcmp(wh.type._class.registered.name, "String")))
2775 if(exp.expType.kind != arrayType || exp.hasAddress)
2781 //char temp[MAX_F_STRING * 32];
2783 ExpressionType evalError = dummyExp;
2784 /*if(exp.expType.kind == arrayType)
2785 sprintf(temp, "(char*)0x%x", exp.address);
2787 sprintf(temp, "(char*)%s", exp.constant);*/
2789 //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2790 // address = strtoul(exp.constant, null, 0);
2791 address = _strtoui64(exp.constant, null, 0);
2792 //_dpl(0, "0x", address);
2793 // snprintf(value, sizeof(value), "0x%08x ", address);
2795 if(address > 0xFFFFFFFFLL)
2796 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2798 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2799 value[sizeof(value)-1] = 0;
2802 strcat(value, $"Null string");
2806 len = strlen(value);
2808 while(!string && size > 2)
2810 string = GdbReadMemory(address, size);
2813 if(string && string[0])
2816 if(UTF8Validate(string))
2821 for(c = 0; (ch = string[c]) && c<4096; c++)
2824 value[len++] = '\0';
2829 ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2830 strcat(value, ") (ISO8859-1)");
2837 strcat(value, $"Empty string");
2841 strcat(value, $"Couldn't read memory");
2843 wh.value = CopyString(value);
2846 else if(wh.type && wh.type.kind == classType && wh.type._class &&
2847 wh.type._class.registered && wh.type._class.registered.type == enumClass)
2849 uint64 value = strtoul(exp.constant, null, 0);
2850 Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2851 EnumClassData enumeration = (EnumClassData)enumClass.data;
2853 for(item = enumeration.values.first; item; item = item.next)
2854 if((int)item.data == value)
2857 wh.value = CopyString(item.name);
2859 wh.value = CopyString($"Invalid Enum Value");
2862 else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class &&
2863 wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2870 if(exp.constant[0] == '\'')
2872 if((int)((byte *)exp.constant)[1] > 127)
2875 value = UTF8GetChar(exp.constant + 1, &nb);
2876 if(nb < 2) value = exp.constant[1];
2877 signedValue = value;
2881 signedValue = exp.constant[1];
2883 // Precomp Syntax error with boot strap here:
2884 byte b = (byte)(char)signedValue;
2885 value = (unichar) b;
2891 if(wh.type.kind == charType && wh.type.isSigned)
2893 signedValue = (int)(char)strtol(exp.constant, null, 0);
2895 // Precomp Syntax error with boot strap here:
2896 byte b = (byte)(char)signedValue;
2897 value = (unichar) b;
2902 value = (uint)strtoul(exp.constant, null, 0);
2903 signedValue = (int)value;
2907 UTF32toUTF8Len(&value, 1, charString, 5);
2909 snprintf(string, sizeof(string), "\'\\0' (0)");
2910 else if(value == '\t')
2911 snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2912 else if(value == '\n')
2913 snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2914 else if(value == '\r')
2915 snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2916 else if(wh.type.kind == charType && wh.type.isSigned)
2917 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2918 else if(value > 256 || wh.type.kind != charType)
2920 if(value > 0x10FFFF || !GetCharCategory(value))
2921 snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2923 snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2926 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2927 string[sizeof(string)-1] = 0;
2929 wh.value = CopyString(string);
2934 wh.value = CopyString(exp.constant);
2941 wh.value = PrintHexUInt64(exp.address);
2946 char tempString[256];
2947 if(exp.member.memberType == propertyMember)
2948 snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2950 snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression,
2951 exp.type.OnGetString(tempString, null, null));
2957 snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2958 if(exp) FreeExpression(exp);
2961 SetPrivateModule(backupPrivateModule);
2962 SetCurrentContext(backupContext);
2963 SetTopContext(backupContext);
2964 SetGlobalContext(backupContext);
2965 SetThisClass(backupThisClass);
2968 // wh.value = CopyString("No source file found for selected frame");
2970 watchmsg[sizeof(watchmsg)-1] = 0;
2972 wh.value = CopyString(watchmsg);
2974 ide.watchesView.UpdateWatch(wh);
2978 void EvaluateWatches()
2980 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateWatches()");
2981 for(wh : ide.workspace.watches)
2985 char * ::GdbEvaluateExpression(char * expression)
2987 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbEvaluateExpression(", expression, ")");
2990 GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2992 ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2996 // to be removed... use GdbReadMemory that returns a byte array instead
2997 char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
2999 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemoryString(", address, ")");
3004 _dpl(0, "GdbReadMemoryString called with size = 0!");
3006 // GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
3007 if(GetRuntimePlatform() == win32)
3008 GdbCommand(false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
3010 GdbCommand(false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
3012 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
3016 byte * ::GdbReadMemory(uint64 address, int bytes)
3018 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemory(", address, ")");
3021 //GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
3022 if(GetRuntimePlatform() == win32)
3023 GdbCommand(false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
3025 GdbCommand(false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
3028 _dpl(0, "GdbReadMemory called with bytes = 0!");
3031 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
3032 else if(eval.result && strcmp(eval.result, "N/A"))
3034 byte * result = new byte[bytes];
3035 byte * string = eval.result;
3039 result[c++] = (byte)strtol(string, &string, 10);
3055 void EventHit(GdbDataStop stopItem, Breakpoint bpInternal, Breakpoint bpUser)
3057 char * s1 = null; char * s2 = null;
3058 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EventHit(",
3059 "bpInternal(", bpInternal ? s1=bpInternal.CopyLocationString(false) : null, "), ",
3060 "bpUser(", bpUser ? s2=bpUser.CopyLocationString(false) : null, ")) -- ",
3061 "ignoreBreakpoints(", ignoreBreakpoints, "), ",
3062 "hitCursorBreakpoint(", bpUser && bpUser.type == runToCursor, ")");
3063 delete s1; delete s2;
3065 if(bpUser && stopItem.frame.line && bpUser.line != stopItem.frame.line)
3067 // updating user breakpoint on hit location difference
3068 // todo, print something?
3069 bpUser.line = stopItem.frame.line;
3070 ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3071 ide.workspace.Save();
3076 if(bpInternal.type == internalModulesLoaded)
3078 if(!bpUser && !userBreakOnInternalBreakpoint)
3080 if(userAction == stepOut)//if(prevStopItem.reason == functionFinished)
3081 StepOut(ignoreBreakpoints);
3083 GdbExecContinue(false);
3088 bool conditionMet = true;
3089 if(bpUser.condition)
3090 conditionMet = ResolveWatch(bpUser.condition);
3092 if(!ignoreBreakpoints && (bpUser.level == -1 || bpUser.level == frameCount-1) && conditionMet)
3099 GdbExecContinue(false);
3103 GdbExecContinue(false);
3104 ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3105 if(bpUser == bpRunToCursor)
3107 UnsetBreakpoint(bpUser);
3108 delete bpRunToCursor;
3112 if(!bpUser && !bpInternal)
3113 GdbExecContinue(false);
3116 void ValgrindTargetThreadExit()
3118 ide.outputView.debugBox.Logf($"ValgrindTargetThreadExit\n");
3121 vgTargetHandle.Wait();
3122 delete vgTargetHandle;
3124 HandleExit(null, null);
3127 void GdbThreadExit()
3129 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbThreadExit()");
3130 if(state != terminated)
3132 ChangeState(terminated);
3133 targetProcessId = 0;
3134 ClearBreakDisplay();
3140 serialSemaphore.Release();
3145 ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
3146 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3148 HideDebuggerViews();
3150 //ChangeState(terminated);
3154 void GdbThreadMain(char * output)
3157 Array<char *> outTokens { minAllocSize = 50 };
3158 Array<char *> subTokens { minAllocSize = 50 };
3159 DebugListItem item { };
3160 DebugListItem item2 { };
3161 bool setWaitingForPID = false;
3163 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
3164 #ifdef GDB_DEBUG_CONSOLE
3165 _dpl2(_dpct, dplchan::gdbOutput, 0, output);
3167 #ifdef GDB_DEBUG_OUTPUT
3169 int len = strlen(output);
3177 for(c = 0; c < len / 1024; c++)
3179 strncpy(tmp, start, 1024);
3180 ide.outputView.gdbBox.Logf("out: %s\n", tmp);
3183 ide.outputView.gdbBox.Logf("out: %s\n", start);
3187 ide.outputView.gdbBox.Logf("out: %s\n", output);
3191 #ifdef GDB_DEBUG_CONSOLE
3192 strcpy(lastGdbOutput, output);
3194 #ifdef GDB_DEBUG_GUI
3195 if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
3202 if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
3205 ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
3211 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
3213 //if(outTokens.count == 1)
3218 ChangeState(loaded);
3219 targetProcessId = 0;
3220 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3222 if(!strcmp(item.name, "reason"))
3224 char * reason = item.value;
3225 StripQuotes(reason, reason);
3226 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3229 if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
3231 StripQuotes(item2.value, item2.value);
3232 if(!strcmp(item2.name, "exit-code"))
3233 exitCode = item2.value;
3239 HandleExit(reason, exitCode);
3243 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "kill reply (", item.name, "=", item.value, ") is unheard of");
3246 HandleExit(null, null);
3249 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3251 if(!strcmp(item.name, "bkpt"))
3253 sentBreakInsert = false;
3258 bpItem = ParseBreakpoint(item.value, outTokens);
3259 //breakType = bpValidation;
3261 else if(!strcmp(item.name, "depth"))
3263 StripQuotes(item.value, item.value);
3264 frameCount = atoi(item.value);
3266 stackFrames.Free(Frame::Free);
3268 else if(!strcmp(item.name, "stack"))
3271 if(stackFrames.count)
3272 ide.callStackView.Logf("...\n");
3275 item.value = StripBrackets(item.value);
3276 TokenizeList(item.value, ',', subTokens);
3277 for(i = 0; i < subTokens.count; i++)
3279 if(TokenizeListItem(subTokens[i], item))
3281 if(!strcmp(item.name, "frame"))
3284 stackFrames.Add(frame);
3285 item.value = StripCurlies(item.value);
3286 ParseFrame(frame, item.value);
3287 if(frame.file && frame.from)
3288 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "unexpected frame file and from members present");
3292 if(activeFrameLevel == -1)
3294 if(ide.projectView.IsModuleInProject(frame.file));
3296 if(frame.level != 0)
3298 //stopItem.frame = frame;
3299 breakType = selectFrame;
3302 activeFrame = frame;
3303 activeFrameLevel = frame.level;
3306 ide.callStackView.Logf("%3d ", frame.level);
3307 if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
3308 ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
3309 else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
3310 ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
3311 else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
3312 ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
3313 else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
3314 ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
3316 ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
3321 ide.callStackView.Logf("%3d ", frame.level);
3326 ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
3330 ide.callStackView.Logf("%s\n", frame.func);
3332 ide.callStackView.Logf($"unknown source\n");
3336 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "stack content (", item.name, "=", item.value, ") is unheard of");
3339 if(activeFrameLevel == -1)
3341 activeFrameLevel = 0;
3342 activeFrame = stackFrames.first;
3344 ide.callStackView.Home();
3346 subTokens.RemoveAll();
3348 /*else if(!strcmp(item.name, "frame"))
3351 item.value = StripCurlies(item.value);
3352 ParseFrame(&frame, item.value);
3354 else if(!strcmp(item.name, "thread-ids"))
3356 ide.threadsView.Clear();
3357 item.value = StripCurlies(item.value);
3358 TokenizeList(item.value, ',', subTokens);
3359 for(i = subTokens.count - 1; ; i--)
3361 if(TokenizeListItem(subTokens[i], item))
3363 if(!strcmp(item.name, "thread-id"))
3366 StripQuotes(item.value, item.value);
3367 value = atoi(item.value);
3368 ide.threadsView.Logf("%3d \n", value);
3371 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "threads content (", item.name, "=", item.value, ") is unheard of");
3376 ide.threadsView.Home();
3378 subTokens.RemoveAll();
3379 //if(!strcmp(outTokens[2], "number-of-threads"))
3381 else if(!strcmp(item.name, "new-thread-id"))
3383 StripQuotes(item.value, item.value);
3384 activeThread = atoi(item.value);
3386 else if(!strcmp(item.name, "value"))
3388 StripQuotes(item.value, item.value);
3389 eval.result = CopyString(item.value);
3390 eval.active = false;
3392 else if(!strcmp(item.name, "addr"))
3394 for(i = 2; i < outTokens.count; i++)
3396 if(TokenizeListItem(outTokens[i], item))
3398 if(!strcmp(item.name, "total-bytes"))
3400 StripQuotes(item.value, item.value);
3401 eval.bytes = atoi(item.value);
3403 else if(!strcmp(item.name, "next-row"))
3405 StripQuotes(item.value, item.value);
3406 eval.nextBlockAddress = _strtoui64(item.value, null, 0);
3408 else if(!strcmp(item.name, "memory"))
3412 //StripQuotes(item.value, item.value);
3413 item.value = StripBrackets(item.value);
3414 // this should be treated as a list...
3415 item.value = StripCurlies(item.value);
3416 TokenizeList(item.value, ',', subTokens);
3417 for(j = 0; j < subTokens.count; j++)
3419 if(TokenizeListItem(subTokens[j], item))
3421 if(!strcmp(item.name, "data"))
3423 item.value = StripBrackets(item.value);
3424 StripQuotes2(item.value, item.value);
3425 eval.result = CopyString(item.value);
3426 eval.active = false;
3430 subTokens.RemoveAll();
3435 else if(!strcmp(item.name, "source-path") || !strcmp(item.name, "BreakpointTable"))
3436 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "command reply (", item.name, "=", item.value, ") is ignored");
3438 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "command reply (", item.name, "=", item.value, ") is unheard of");
3441 else if(!strcmp(outTokens[0], "^running"))
3443 waitingForPID = true;
3444 setWaitingForPID = true;
3445 ClearBreakDisplay();
3447 else if(!strcmp(outTokens[0], "^exit"))
3449 ChangeState(terminated);
3450 // ide.outputView.debugBox.Logf("Exit\n");
3451 // ide.Update(null);
3453 serialSemaphore.Release();
3455 else if(!strcmp(outTokens[0], "^error"))
3459 sentBreakInsert = false;
3460 breakpointError = true;
3463 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3465 if(!strcmp(item.name, "msg"))
3467 StripQuotes(item.value, item.value);
3470 eval.active = false;
3472 if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3473 eval.error = symbolNotFound;
3474 else if(strstr(item.value, "Cannot access memory at address"))
3475 eval.error = memoryCantBeRead;
3477 eval.error = unknown;
3479 else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3482 else if(!strncmp(item.value, "Cannot access memory at address", 31))
3485 else if(!strcmp(item.value, "Cannot find bounds of current function"))
3487 ChangeState(stopped);
3488 gdbHandle.Printf("-exec-continue\n");
3490 else if(!strcmp(item.value, "ptrace: No such process."))
3492 ChangeState(loaded);
3493 targetProcessId = 0;
3495 else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3498 else if(!strcmp(item.value, "You can't do that without a process to debug."))
3500 ChangeState(loaded);
3501 targetProcessId = 0;
3503 else if(strstr(item.value, "No such file or directory."))
3505 ChangeState(loaded);
3506 targetProcessId = 0;
3508 else if(strstr(item.value, "During startup program exited with code "))
3510 ChangeState(loaded);
3511 targetProcessId = 0;
3516 if(strlen(item.value) < MAX_F_STRING)
3519 ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3523 ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3529 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "error content (", item.name, "=", item.value, ") is unheard of");
3532 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "result-record: ", outTokens[0]);
3533 outTokens.RemoveAll();
3536 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "status-async-output: ", outTokens[0]);
3539 if(TokenizeList(output, ',', outTokens))
3541 if(!strcmp(outTokens[0], "=library-loaded"))
3542 FGODetectLoadedLibraryForAddedProjectIssues(outTokens);
3543 else if(!strcmp(outTokens[0], "=thread-group-created") || !strcmp(outTokens[0], "=thread-group-added") ||
3544 !strcmp(outTokens[0], "=thread-group-started") || !strcmp(outTokens[0], "=thread-group-exited") ||
3545 !strcmp(outTokens[0], "=thread-created") || !strcmp(outTokens[0], "=thread-exited") ||
3546 !strcmp(outTokens[0], "=cmd-param-changed") || !strcmp(outTokens[0], "=library-unloaded") ||
3547 !strcmp(outTokens[0], "=breakpoint-modified"))
3548 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, outTokens[0], outTokens.count>1 ? outTokens[1] : "",
3549 outTokens.count>2 ? outTokens[2] : "", outTokens.count>3 ? outTokens[3] : "",
3550 outTokens.count>4 ? outTokens[4] : "", outTokens.count>5 ? outTokens[5] : "",
3551 outTokens.count>6 ? outTokens[6] : "", outTokens.count>7 ? outTokens[7] : "",
3552 outTokens.count>8 ? outTokens[8] : "", outTokens.count>9 ? outTokens[9] : "");
3554 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "notify-async-output: ", outTokens[0]);
3556 outTokens.RemoveAll();
3560 if(TokenizeList(output, ',', outTokens))
3562 if(!strcmp(outTokens[0],"*running"))
3564 waitingForPID = true;
3565 setWaitingForPID = true;
3567 else if(!strcmp(outTokens[0], "*stopped"))
3570 ChangeState(stopped);
3572 for(tk = 1; tk < outTokens.count; tk++)
3574 if(TokenizeListItem(outTokens[tk], item))
3576 if(!strcmp(item.name, "reason"))
3578 char * reason = item.value;
3579 StripQuotes(reason, reason);
3580 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3583 if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3586 StripQuotes(item2.value, item2.value);
3587 if(!strcmp(item2.name, "exit-code"))
3588 exitCode = item2.value;
3594 HandleExit(reason, exitCode);
3597 else if(!strcmp(reason, "breakpoint-hit"))
3603 stopItem = GdbDataStop { };
3604 stopItem.reason = breakpointHit;
3606 for(i = tk+1; i < outTokens.count; i++)
3608 TokenizeListItem(outTokens[i], item);
3609 StripQuotes(item.value, item.value);
3610 if(!strcmp(item.name, "bkptno"))
3611 stopItem.bkptno = atoi(item.value);
3612 else if(!strcmp(item.name, "thread-id"))
3613 stopItem.threadid = atoi(item.value);
3614 else if(!strcmp(item.name, "frame"))
3616 item.value = StripCurlies(item.value);
3617 ParseFrame(stopItem.frame, item.value);
3619 else if(!strcmp(item.name, "disp") || !strcmp(item.name, "stopped-threads") || !strcmp(item.name, "core"))
3620 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3622 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown breakpoint hit item name (", item.name, "=", item.value, ")");
3627 else if(!strcmp(reason, "end-stepping-range"))
3633 stopItem = GdbDataStop { };
3634 stopItem.reason = endSteppingRange;
3636 for(i = tk+1; i < outTokens.count; i++)
3638 TokenizeListItem(outTokens[i], item);
3639 StripQuotes(item.value, item.value);
3640 if(!strcmp(item.name, "thread-id"))
3641 stopItem.threadid = atoi(item.value);
3642 else if(!strcmp(item.name, "frame"))
3644 item.value = StripCurlies(item.value);
3645 ParseFrame(stopItem.frame, item.value);
3647 else if(!strcmp(item.name, "reason") || !strcmp(item.name, "bkptno"))
3648 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3650 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown end of stepping range item name (", item.name, "=", item.value, ")");
3656 else if(!strcmp(reason, "function-finished"))
3662 stopItem = GdbDataStop { };
3663 stopItem.reason = functionFinished;
3665 for(i = tk+1; i < outTokens.count; i++)
3667 TokenizeListItem(outTokens[i], item);
3668 StripQuotes(item.value, item.value);
3669 if(!strcmp(item.name, "thread-id"))
3670 stopItem.threadid = atoi(item.value);
3671 else if(!strcmp(item.name, "frame"))
3673 item.value = StripCurlies(item.value);
3674 ParseFrame(stopItem.frame, item.value);
3676 else if(!strcmp(item.name, "gdb-result-var"))
3677 stopItem.gdbResultVar = CopyString(item.value);
3678 else if(!strcmp(item.name, "return-value"))
3679 stopItem.returnValue = CopyString(item.value);
3681 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown function finished item name (", item.name, "=", item.value, ")");
3684 event = functionEnd;
3687 else if(!strcmp(reason, "signal-received"))
3693 stopItem = GdbDataStop { };
3694 stopItem.reason = signalReceived;
3696 for(i = tk+1; i < outTokens.count; i++)
3698 TokenizeListItem(outTokens[i], item);
3699 StripQuotes(item.value, item.value);
3700 if(!strcmp(item.name, "signal-name"))
3701 stopItem.name = CopyString(item.value);
3702 else if(!strcmp(item.name, "signal-meaning"))
3703 stopItem.meaning = CopyString(item.value);
3704 else if(!strcmp(item.name, "thread-id"))
3705 stopItem.threadid = atoi(item.value);
3706 else if(!strcmp(item.name, "frame"))
3708 item.value = StripCurlies(item.value);
3709 ParseFrame(stopItem.frame, item.value);
3712 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown signal reveived item name (", item.name, "=", item.value, ")");
3714 if(!strcmp(stopItem.name, "SIGTRAP"))
3733 else if(!strcmp(reason, "watchpoint-trigger"))
3734 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint trigger not handled");
3735 else if(!strcmp(reason, "read-watchpoint-trigger"))
3736 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason read watchpoint trigger not handled");
3737 else if(!strcmp(reason, "access-watchpoint-trigger"))
3738 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason access watchpoint trigger not handled");
3739 else if(!strcmp(reason, "watchpoint-scope"))
3740 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint scope not handled");
3741 else if(!strcmp(reason, "location-reached"))
3742 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason location reached not handled");
3744 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown reason: ", reason);
3752 if(usingValgrind && event == none && !stopItem)
3753 event = valgrindStartPause;
3758 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown exec-async-output: ", outTokens[0]);
3759 outTokens.RemoveAll();
3762 if(!strcmpi(output, "(gdb) "))
3766 char exeFile[MAX_LOCATION];
3767 int oldProcessID = targetProcessId;
3768 GetLastDirectory(targetFile, exeFile);
3770 while(!targetProcessId/*true*/)
3772 targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3773 if(targetProcessId || gdbHandle.Peek()) break;
3778 ChangeState(running);
3779 else if(!oldProcessID)
3781 ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3782 // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3783 gdbHandle.Printf("-gdb-exit\n");
3785 ChangeState(terminated); //loaded;
3790 for(bp : ide.workspace.breakpoints)
3791 bp.inserted = false;
3794 bp.inserted = false;
3796 bpRunToCursor.inserted = false;
3798 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3799 ClearBreakDisplay();
3801 #if defined(__unix__)
3802 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
3804 progThread.terminate = true;
3807 fifoFile.CloseInput();
3814 DeleteFile(progFifoPath);
3815 progFifoPath[0] = '\0';
3822 serialSemaphore.Release();
3825 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown prompt", output);
3829 if(!strncmp(output, "&\"warning:", 10))
3832 content = strstr(output, "\"");
3833 StripQuotes(content, content);
3834 content = strstr(content, ":");
3840 ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3847 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown output: ", output);
3849 if(!setWaitingForPID)
3850 waitingForPID = false;
3851 setWaitingForPID = false;
3859 // From GDB Output functions
3860 void FGODetectLoadedLibraryForAddedProjectIssues(Array<char *> outTokens)
3862 char path[MAX_LOCATION] = "";
3863 char file[MAX_FILENAME] = "";
3865 DebugListItem item { };
3866 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGODetectLoadedLibraryForAddedProjectIssues()");
3867 for(token : outTokens)
3869 if(TokenizeListItem(token, item))
3871 if(!strcmp(item.name, "target-name"))
3873 StripQuotes(item.value, path);
3874 MakeSystemPath(path);
3875 GetLastDirectory(path, file);
3877 else if(!strcmp(item.name, "symbols-loaded"))
3879 symbolsLoaded = (atoi(item.value) == 1);
3884 if(path[0] && file[0])
3886 for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
3890 char prjTargetPath[MAX_LOCATION];
3891 char prjTargetFile[MAX_FILENAME];
3892 DirExpression targetDirExp = prj.GetTargetDir(currentCompiler, prj.config, bitDepth);
3893 strcpy(prjTargetPath, prj.topNode.path);
3894 PathCat(prjTargetPath, targetDirExp.dir);
3895 prjTargetFile[0] = '\0';
3896 prj.CatTargetFileName(prjTargetFile, currentCompiler, prj.config);
3897 PathCat(prjTargetPath, prjTargetFile);
3898 MakeSystemPath(prjTargetPath);
3900 match = !fstrcmp(prjTargetFile, file);
3901 if(!match && (dot = strstr(prjTargetFile, ".so.")))
3903 char * dot3 = strstr(dot+4, ".");
3907 match = !fstrcmp(prjTargetFile, file);
3912 match = !fstrcmp(prjTargetFile, file);
3917 // TODO: nice visual feedback to better warn user. use some ide notification system or other means.
3918 /* -- 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)
3920 ide.outputView.debugBox.Logf($"Attention! No symbols for loaded library %s matched to the %s added project.\n", path, prj.topNode.name);
3922 match = !fstrcmp(prjTargetPath, path);
3923 if(!match && (dot = strstr(prjTargetPath, ".so.")))
3925 char * dot3 = strstr(dot+4, ".");
3929 match = !fstrcmp(prjTargetPath, path);
3934 match = !fstrcmp(prjTargetPath, path);
3938 ide.outputView.debugBox.Logf($"Loaded library %s doesn't match the %s target of the %s added project.\n", path, prjTargetPath, prj.topNode.name);
3945 void FGOBreakpointModified(Array<char *> outTokens)
3947 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGOBreakpointModified() -- TODO only if needed: support breakpoint modified");
3949 DebugListItem item { };
3950 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3952 if(!strcmp(item.name, "bkpt"))
3954 GdbDataBreakpoint modBp = ParseBreakpoint(item.value, outTokens);
3962 ExpressionType ::DebugEvalExpTypeError(char * result)
3964 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::DebugEvalExpTypeError()");
3969 case symbolNotFound:
3970 return symbolErrorExp;
3971 case memoryCantBeRead:
3972 return memoryErrorExp;
3974 return unknownErrorExp;
3977 char * ::EvaluateExpression(char * expression, ExpressionType * error)
3980 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateExpression(", expression, ")");
3981 if(ide.projectView && ide.debugger.state == stopped)
3983 result = GdbEvaluateExpression(expression);
3984 *error = DebugEvalExpTypeError(result);
3989 *error = noDebuggerErrorExp;
3994 char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
3997 char * result = GdbReadMemoryString(address, size, format, 1, 1);
3998 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ReadMemory(", address, ")");
3999 if(!result || !strcmp(result, "N/A"))
4000 *error = memoryErrorExp;
4002 *error = DebugEvalExpTypeError(result);
4007 class ValgrindLogThread : Thread
4013 static char output[4096];
4014 Array<char> dynamicBuffer { minAllocSize = 4096 };
4015 File oldValgrindHandle = vgLogFile;
4016 incref oldValgrindHandle;
4019 while(debugger.state != terminated && vgLogFile)
4023 result = vgLogFile.Read(output, 1, sizeof(output));
4025 if(debugger.state == terminated || !vgLogFile/* || vgLogFile.Eof()*/)
4032 for(c = 0; c<result; c++)
4034 if(output[c] == '\n')
4036 int pos = dynamicBuffer.size;
4037 dynamicBuffer.size += c - start;
4038 memcpy(&dynamicBuffer[pos], output + start, c - start);
4039 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4040 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4041 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4042 dynamicBuffer.size++;
4043 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4045 // printf("%s\n", dynamicBuffer.array);
4047 if(strstr(&dynamicBuffer[0], "vgdb me"))
4048 debugger.serialSemaphore.Release();
4049 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4050 dynamicBuffer.size = 0;
4056 int pos = dynamicBuffer.size;
4057 dynamicBuffer.size += c - start;
4058 memcpy(&dynamicBuffer[pos], output + start, c - start);
4061 else if(debugger.state == stopped)
4064 printf("Got end of file from GDB!\n");
4071 delete dynamicBuffer;
4072 ide.outputView.debugBox.Logf($"ValgrindLogThreadExit\n");
4073 //if(oldValgrindHandle == vgLogFile)
4074 debugger.GdbThreadExit/*ValgrindLogThreadExit*/();
4075 delete oldValgrindHandle;
4081 class ValgrindTargetThread : Thread
4087 static char output[4096];
4088 Array<char> dynamicBuffer { minAllocSize = 4096 };
4089 DualPipe oldValgrindHandle = vgTargetHandle;
4090 incref oldValgrindHandle;
4093 while(debugger.state != terminated && vgTargetHandle && !vgTargetHandle.Eof())
4097 result = vgTargetHandle.Read(output, 1, sizeof(output));
4099 if(debugger.state == terminated || !vgTargetHandle || vgTargetHandle.Eof())
4106 for(c = 0; c<result; c++)
4108 if(output[c] == '\n')
4110 int pos = dynamicBuffer.size;
4111 dynamicBuffer.size += c - start;
4112 memcpy(&dynamicBuffer[pos], output + start, c - start);
4113 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4114 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4115 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4116 dynamicBuffer.size++;
4117 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4119 // printf("%s\n", dynamicBuffer.array);
4121 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4123 dynamicBuffer.size = 0;
4129 int pos = dynamicBuffer.size;
4130 dynamicBuffer.size += c - start;
4131 memcpy(&dynamicBuffer[pos], output + start, c - start);
4137 printf("Got end of file from GDB!\n");
4141 delete dynamicBuffer;
4142 //if(oldValgrindHandle == vgTargetHandle)
4143 debugger.ValgrindTargetThreadExit();
4144 delete oldValgrindHandle;
4150 class GdbThread : Thread
4156 static char output[4096];
4157 Array<char> dynamicBuffer { minAllocSize = 4096 };
4158 DualPipe oldGdbHandle = gdbHandle;
4159 incref oldGdbHandle;
4162 while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
4166 result = gdbHandle.Read(output, 1, sizeof(output));
4168 if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
4175 for(c = 0; c<result; c++)
4177 if(output[c] == '\n')
4179 int pos = dynamicBuffer.size;
4180 dynamicBuffer.size += c - start;
4181 memcpy(&dynamicBuffer[pos], output + start, c - start);
4182 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4183 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4184 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4185 dynamicBuffer.size++;
4186 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4188 // _dpl(0, dynamicBuffer.array);
4190 debugger.GdbThreadMain(&dynamicBuffer[0]);
4191 dynamicBuffer.size = 0;
4197 int pos = dynamicBuffer.size;
4198 dynamicBuffer.size += c - start;
4199 memcpy(&dynamicBuffer[pos], output + start, c - start);
4205 _dpl(0, "Got end of file from GDB!");
4209 delete dynamicBuffer;
4210 //if(oldGdbHandle == gdbHandle)
4211 debugger.GdbThreadExit();
4212 delete oldGdbHandle;
4218 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
4219 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
4221 #if defined(__unix__)
4226 #include <sys/types.h>
4231 class ProgramThread : Thread
4237 bool fileCreated = false;
4239 static char output[1000];
4242 /*if(!mkfifo(progFifoPath, mask))
4249 ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
4253 if(FileExists(progFifoPath)) //fileCreated)
4255 fifoFile = FileOpen(progFifoPath, read);
4259 ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
4264 fd = fileno((FILE *)fifoFile.input);
4265 //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
4269 while(!terminate && fifoFile && !fifoFile.Eof())
4272 struct timeval time;
4280 selectResult = select(fd + 1, &rs, null, null, &time);
4281 if(FD_ISSET(fd, &rs))
4283 int result = (int)read(fd, output, sizeof(output)-1);
4284 if(!result || (result < 0 && errno != EAGAIN))
4288 output[result] = '\0';
4289 if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
4292 ide.outputView.debugBox.Log(output);
4301 //fifoFile.CloseInput();
4304 ide.outputView.debugBox.Log("\n");
4308 if(FileExists(progFifoPath)) //fileCreated)
4310 DeleteFile(progFifoPath);
4311 progFifoPath[0] = '\0';
4319 class Argument : struct
4321 Argument prev, next;
4323 property char * name { set { delete name; if(value) name = CopyString(value); } }
4325 property char * val { set { delete val; if(value) val = CopyString(value); } }
4339 class Frame : struct
4344 property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4346 property char * func { set { delete func; if(value) func = CopyString(value); } }
4350 property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
4352 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4353 char * absoluteFile;
4354 property char * absoluteFile { set { delete absoluteFile; if(value) absoluteFile = CopyUnescapedUnixPath(value); } }
4363 delete absoluteFile;
4364 args.Free(Argument::Free);
4373 class GdbDataStop : struct
4375 DebuggerReason reason;
4390 char * gdbResultVar;
4400 if(reason == signalReceived)
4405 else if(reason == functionFinished)
4407 delete gdbResultVar;
4411 if(frame) frame.Free();
4420 class GdbDataBreakpoint : struct
4424 property char * number { set { delete number; if(value) number = CopyString(value); } }
4426 property char * type { set { delete type; if(value) type = CopyString(value); } }
4428 property char * disp { set { delete disp; if(value) disp = CopyString(value); } }
4431 property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4433 property char * func { set { delete func; if(value) func = CopyString(value); } }
4435 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4437 property char * fullname { set { delete fullname; if(value) fullname = CopyUnescapedUnixPath(value); } }
4440 property char * at { set { delete at; if(value) at = CopyString(value); } }
4443 Array<GdbDataBreakpoint> multipleBPs;
4448 PrintLn("{", "#", number, " T", type, " D", disp, " E", enabled, " H", times, " (", func, ") (", file, ":", line, ") (", fullname, ") (", addr, ") (", at, ")", "}");
4459 if(multipleBPs) multipleBPs.Free();
4463 ~GdbDataBreakpoint()
4469 class Breakpoint : struct
4474 property char * function { set { delete function; if(value) function = CopyString(value); } }
4475 char * relativeFilePath;
4476 property char * relativeFilePath { set { delete relativeFilePath; if(value) relativeFilePath = CopyString(value); } }
4477 char * absoluteFilePath;
4478 property char * absoluteFilePath { set { delete absoluteFilePath; if(value) absoluteFilePath = CopyString(value); } }
4487 BreakpointType type;
4489 GdbDataBreakpoint bp;
4491 char * CopyLocationString(bool removePath)
4494 char * file = relativeFilePath ? relativeFilePath : absoluteFilePath;
4495 bool removingPath = removePath && file;
4498 char * fileName = new char[MAX_FILENAME];
4499 GetLastDirectory(file, fileName);
4505 location = PrintString(file, ":", function);
4507 location = CopyString(function);
4510 location = PrintString(file, ":", line);
4516 char * CopyUserLocationString()
4519 char * loc = CopyLocationString(false);
4521 for(p : ide.workspace.projects)
4523 if(p.topNode.FindByFullPath(absoluteFilePath, false))
4531 location = PrintString("(", prj.name, ")", loc);
4541 if(relativeFilePath && relativeFilePath[0])
4543 f.Printf(" * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
4545 f.Printf(" ~ %s\n", condition.expression);
4555 delete relativeFilePath;
4556 delete absoluteFilePath;
4566 class Watch : struct
4577 f.Printf(" ~ %s\n", expression);
4601 class DebugListItem : struct
4607 struct DebugEvaluationData
4612 uint64 nextBlockAddress;
4614 DebuggerEvaluationError error;
4617 class CodeLocation : struct
4620 char * absoluteFile;
4623 CodeLocation ::ParseCodeLocation(char * location)
4627 char * colon = null;
4629 char loc[MAX_LOCATION];
4630 strcpy(loc, location);
4631 for(temp = loc; temp = strstr(temp, ":"); temp++)
4639 int line = atoi(colon);
4642 CodeLocation codloc { line = line };
4643 codloc.file = CopyString(loc);
4644 codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
4656 delete absoluteFile;
4665 void GDBFallBack(Expression exp, String expString)
4668 ExpressionType evalError = dummyExp;
4669 result = Debugger::EvaluateExpression(expString, &evalError);
4672 exp.constant = result;
4673 exp.type = constantExp;