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);
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)
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)
882 ide.callStackView.Show();
884 if(frame.absoluteFile)
885 editor = (CodeEditor)ide.OpenFile(frame.absoluteFile, normal, true, null, no, normal, false);
886 if(!editor && frame.file)
887 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
888 if(!frame.absoluteFile && askForLocation && frame.file)
891 char title[MAX_LOCATION];
892 snprintf(title, sizeof(title), $"Provide source file location for %s", (s = CopySystemPath(frame.file)));
893 title[sizeof(title)-1] = 0;
895 if(SourceDirDialog(title, ide.workspace.projectDir, frame.file, sourceDir))
897 AddSourceDir(sourceDir);
898 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
901 if(!editor && frame.absoluteFile)
902 editor = (CodeEditor)ide.OpenFile(frame.absoluteFile, normal, true, null, no, normal, false);
904 if(editor && frame.line)
906 EditBox editBox = editor.editBox;
907 editBox.GoToLineNum(frame.line - 1);
908 editBox.GoToPosition(editBox.line, frame.line - 1, 0);
916 void SelectThread(int thread)
918 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SelectThread(", thread, ")");
919 userAction = selectThread;
922 if(thread != activeThread)
924 activeFrameLevel = -1;
925 ide.callStackView.Clear();
926 GdbCommand(false, "-thread-select %d", thread);
928 // Why was SelectFrame missing here?
929 SelectFrame(activeFrameLevel);
930 GoToStackFrameLine(activeFrameLevel, true);
931 WatchesCodeEditorLinkRelease();
932 WatchesCodeEditorLinkInit();
936 ide.callStackView.Show();
940 void SelectFrame(int frame)
942 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SelectFrame(", frame, ")");
943 userAction = selectFrame; // not always user action, right? doesn't matter for now.
946 if(frame != activeFrameLevel || !codeEditor || !codeEditor.visible)
948 activeFrameLevel = frame; // there is no active frame number in the gdb reply
949 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
950 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
951 if(activeFrame.level == activeFrameLevel)
954 WatchesCodeEditorLinkRelease();
955 WatchesCodeEditorLinkInit();
962 void HandleExit(char * reason, char * code)
964 bool returnedExitCode = false;
965 char verboseExitCode[128];
967 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::HandleExit(", reason, ", ", code, ")");
968 ChangeState(loaded); // this state change seems to be superfluous, might be in case of gdb crash
973 snprintf(verboseExitCode, sizeof(verboseExitCode), $" with exit code %s", code);
974 verboseExitCode[sizeof(verboseExitCode)-1] = 0;
977 verboseExitCode[0] = '\0';
981 // ClearBreakDisplay();
985 for(wh : ide.workspace.watches)
987 if(wh.type) FreeType(wh.type);
990 ide.watchesView.UpdateWatch(wh);
994 #if defined(__unix__)
997 progThread.terminate = true;
1000 fifoFile.CloseInput();
1010 char program[MAX_LOCATION];
1011 GetSystemPathBuffer(program, targetFile);
1013 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
1014 else if(!strcmp(reason, "exited-normally"))
1015 ide.outputView.debugBox.Logf($"The program %s has exited normally%s.\n", program, verboseExitCode);
1016 else if(!strcmp(reason, "exited"))
1017 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
1018 else if(!strcmp(reason, "exited-signalled"))
1019 ide.outputView.debugBox.Logf($"The program %s has exited with a signal%s.\n", program, verboseExitCode);
1021 ide.outputView.debugBox.Logf($"The program %s has exited (gdb provided an unknown reason)%s.\n", program, verboseExitCode);
1026 DebuggerState StartSession(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool restart, bool userBreakOnInternalBreakpoint, bool ignoreBreakpoints/*, bool runToCursorDebugStart*/)
1028 DebuggerState result = none;
1029 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StartSession(restart(", restart, "), userBreakOnInternalBreakpoint(", userBreakOnInternalBreakpoint, "), ignoreBreakpoints(", ignoreBreakpoints, ")"/*, runToCursorDebugStart(", runToCursorDebugStart, ")"*/);
1030 if(restart && state == running && targetProcessId)
1032 breakType = DebuggerAction::restart;
1033 GdbDebugBreak(false);
1037 if(restart && state == stopped)
1039 if(needReset && state == loaded)
1040 GdbExit(); // this reset is to get a clean state with all the breakpoints until a better state can be maintained on program exit
1042 if(result == none || result == terminated)
1044 ide.outputView.ShowClearSelectTab(debug);
1045 ide.outputView.debugBox.Logf($"Starting debug mode\n");
1052 for(bp : ide.workspace.breakpoints)
1058 //this.runToCursorDebugStart = runToCursorDebugStart;
1060 if(GdbInit(compiler, config, bitDepth, useValgrind))
1065 this.ignoreBreakpoints = ignoreBreakpoints;
1066 this.userBreakOnInternalBreakpoint = userBreakOnInternalBreakpoint;
1067 if(result == loaded || result == stopped)
1068 GdbBreakpointsDelete(false, (userAction == stepOver || userAction == stepOut), ignoreBreakpoints);
1073 void Start(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
1075 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Start()");
1077 if(StartSession(compiler, config, bitDepth, useValgrind, true, false, false/*, false*/) == loaded)
1081 void StepInto(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
1083 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepInto()");
1084 userAction = stepInto;
1085 switch(StartSession(compiler, config, bitDepth, useValgrind, false, true, false/*, false*/))
1087 case loaded: GdbExecRun(); break;
1088 case stopped: GdbExecStep(); break;
1092 void StepOver(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool ignoreBreakpoints)
1094 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepOver()");
1095 userAction = stepOver;
1096 switch(StartSession(compiler, config, bitDepth, useValgrind, false, true, ignoreBreakpoints/*, false*/))
1098 case loaded: GdbExecRun(); break;
1099 case stopped: GdbExecNext(); break;
1103 void StepOut(bool ignoreBreakpoints)
1105 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepOut()");
1106 userAction = stepOut;
1107 if(state == stopped)
1109 this.ignoreBreakpoints = ignoreBreakpoints;
1110 GdbBreakpointsDelete(true, true, ignoreBreakpoints);
1114 GdbExecContinue(true);
1118 void RunToCursor(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, char * absoluteFilePath, int lineNumber, bool ignoreBreakpoints, bool atSameLevel)
1120 char relativeFilePath[MAX_LOCATION];
1121 DebuggerState st = state;
1122 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::RunToCursor()");
1123 userAction = runToCursor;
1126 // ide.outputView.ShowClearSelectTab(debug);
1127 // ide.outputView.debugBox.Logf($"Starting debug mode\n");
1129 if(!ide.projectView.project.GetRelativePath(absoluteFilePath, relativeFilePath))
1130 strcpy(relativeFilePath, absoluteFilePath);
1132 if(bpRunToCursor && bpRunToCursor.inserted && symbols)
1134 UnsetBreakpoint(bpRunToCursor);
1135 delete bpRunToCursor;
1138 bpRunToCursor = Breakpoint { };
1139 bpRunToCursor.absoluteFilePath = absoluteFilePath;
1140 bpRunToCursor.relativeFilePath = relativeFilePath;
1141 bpRunToCursor.line = lineNumber;
1142 bpRunToCursor.type = runToCursor;
1143 bpRunToCursor.enabled = true;
1144 bpRunToCursor.level = atSameLevel ? frameCount - activeFrameLevel -1 : -1;
1146 switch(StartSession(compiler, config, bitDepth, useValgrind, false, false, ignoreBreakpoints/*, true*/))
1152 GdbExecContinue(true);
1157 void GetCallStackCursorLine(bool * error, int * lineCursor, int * lineTopFrame)
1159 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetCallStackCursorLine()");
1160 if(activeFrameLevel == -1)
1168 *error = signalOn && activeThread == signalThread;
1169 *lineCursor = activeFrameLevel - ((frameCount > 192 && activeFrameLevel > 191) ? frameCount - 192 - 1 : 0) + 1;
1170 *lineTopFrame = activeFrameLevel ? 1 : 0;
1174 int GetMarginIconsLineNumbers(char * fileName, int lines[], bool enabled[], int max, bool * error, int * lineCursor, int * lineTopFrame)
1176 char winFilePath[MAX_LOCATION];
1177 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1179 Iterator<Breakpoint> it { ide.workspace.breakpoints };
1180 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetMarginIconsLineNumbers()");
1181 while(it.Next() && count < max)
1183 Breakpoint bp = it.data;
1186 if(bp.absoluteFilePath && bp.absoluteFilePath[0] && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1188 lines[count] = bp.line;
1189 enabled[count] = bp.enabled;
1194 if(activeFrameLevel == -1)
1202 *error = signalOn && activeThread == signalThread;
1203 if(activeFrame && activeFrame.absoluteFile && !fstrcmp(absoluteFilePath, activeFrame.absoluteFile))
1204 *lineCursor = activeFrame.line;
1207 if(activeFrame && stopItem && stopItem.frame && activeFrame.level == stopItem.frame.level)
1209 else if(stopItem && stopItem.frame && stopItem.frame.absoluteFile && !fstrcmp(absoluteFilePath, stopItem.frame.absoluteFile))
1210 *lineTopFrame = stopItem.frame.line;
1214 if(*lineTopFrame == *lineCursor && *lineTopFrame)
1220 void ChangeWatch(DataRow row, char * expression)
1222 Watch wh = (Watch)row.tag;
1223 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ChangeWatch(", expression, ")");
1226 delete wh.expression;
1228 wh.expression = CopyString(expression);
1231 Iterator<Watch> it { ide.workspace.watches };
1233 ide.workspace.watches.Delete(it.pointer);
1239 row.tag = (int64)wh;
1240 ide.workspace.watches.Add(wh);
1242 wh.expression = CopyString(expression);
1244 ide.workspace.Save();
1245 //if(expression && state == stopped)
1250 void MoveIcons(char * fileName, int lineNumber, int move, bool start)
1252 char winFilePath[MAX_LOCATION];
1253 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1256 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::MoveIcons()");
1257 for(bpLink = ide.workspace.breakpoints.first; bpLink; bpLink = next)
1259 Breakpoint bp = (Breakpoint)bpLink.data;
1262 if(bp.type == user && bp.absoluteFilePath && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1264 if(bp.line > lineNumber || (bp.line == lineNumber && start))
1266 if(move < 0 && (bp.line < lineNumber - move))
1267 ide.workspace.RemoveBreakpoint(bp);
1271 ide.breakpointsView.UpdateBreakpoint(bp.row);
1272 ide.workspace.Save();
1278 // moving code cursors is futile, on next step, stop, hit, cursors will be offset anyways
1281 bool SourceDirDialog(char * title, char * startDir, char * test, char * sourceDir)
1285 String srcDir = null;
1287 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SourceDirDialog()");
1288 debuggerFileDialog.text = title;
1289 debuggerFileDialog.currentDirectory = startDir;
1290 debuggerFileDialog.master = ide;
1292 while(debuggerFileDialog.Modal())
1294 strcpy(sourceDir, debuggerFileDialog.filePath);
1295 if(!fstrcmp(ide.workspace.projectDir, sourceDir) &&
1296 MessageBox { type = yesNo, master = ide,
1297 contents = $"This is the project directory.\nWould you like to try again?",
1298 text = $"Invalid Source Directory" }.Modal() == no)
1302 for(dir : ide.workspace.sourceDirs)
1304 if(!fstrcmp(dir, sourceDir))
1312 MessageBox { type = yesNo, master = ide,
1313 contents = $"This source directory is already specified.\nWould you like to try again?",
1314 text = $"Invalid Source Directory" }.Modal() == no)
1320 char file[MAX_LOCATION];
1321 strcpy(file, sourceDir);
1322 PathCat(file, test);
1323 result = FileExists(file);
1325 MessageBox { type = yesNo, master = ide,
1326 contents = $"Unable to locate source file.\nWould you like to try again?",
1327 text = $"Invalid Source Directory" }.Modal() == no)
1341 void AddSourceDir(char * sourceDir)
1343 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::AddSourceDir(", sourceDir, ")");
1344 ide.workspace.sourceDirs.Add(CopyString(sourceDir));
1345 ide.workspace.Save();
1349 DebuggerState oldState = state;
1354 GdbDebugBreak(true);
1357 GdbCommand(false, "-environment-directory \"%s\"", sourceDir);
1360 if(oldState == running)
1361 GdbExecContinue(false);
1365 void ToggleBreakpoint(char * fileName, int lineNumber, Project prj)
1367 char winFilePath[MAX_LOCATION];
1368 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1369 char absolutePath[MAX_LOCATION];
1370 char relativePath[MAX_LOCATION];
1371 char sourceDir[MAX_LOCATION];
1372 Breakpoint bp = null;
1374 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ToggleBreakpoint(", fileName, ":", lineNumber, ")");
1375 strcpy(absolutePath, absoluteFilePath);
1376 for(i : ide.workspace.breakpoints; i.type == user && i.absoluteFilePath && !fstrcmp(i.absoluteFilePath, absolutePath) && i.line == lineNumber)
1385 ide.workspace.RemoveBreakpoint(bp);
1393 // FIXED: This is how it should have been... Source locations are only for files not in project
1394 // if(IsPathInsideOf(absolutePath, ide.workspace.projectDir))
1395 // MakePathRelative(absolutePath, ide.workspace.projectDir, relativePath);
1396 bool result = false;
1398 result = prj.GetRelativePath(absolutePath, relativePath);
1400 result = ide.projectView.project.GetRelativePath(absolutePath, relativePath);
1401 //if(ide.projectView.project.GetRelativePath(absolutePath, relativePath));
1405 char title[MAX_LOCATION];
1406 char directory[MAX_LOCATION];
1407 StripLastDirectory(absolutePath, directory);
1408 snprintf(title, sizeof(title), $"Provide source files location directory for %s", absolutePath);
1409 title[sizeof(title)-1] = 0;
1412 String srcDir = null;
1413 for(dir : ide.workspace.sourceDirs)
1415 if(IsPathInsideOf(absolutePath, dir))
1417 MakePathRelative(absoluteFilePath, dir, relativePath);
1425 if(SourceDirDialog(title, directory, null, sourceDir))
1427 if(IsPathInsideOf(absolutePath, sourceDir))
1429 AddSourceDir(sourceDir);
1430 MakePathRelative(absoluteFilePath, sourceDir, relativePath);
1433 else if(MessageBox { type = yesNo, master = ide,
1434 contents = $"You must provide a valid source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1435 text = $"Invalid Source Directory" }.Modal() == no)
1438 else if(MessageBox { type = yesNo, master = ide,
1439 contents = $"You must provide a source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1440 text = $"No Source Directory Provided" }.Modal() == no)
1444 ide.workspace.bpCount++;
1445 bp = { line = lineNumber, type = user, enabled = true, level = -1 };
1446 ide.workspace.breakpoints.Add(bp);
1447 bp.absoluteFilePath = absolutePath;
1448 bp.relativeFilePath = relativePath;
1449 ide.breakpointsView.AddBreakpoint(bp);
1454 DebuggerState oldState = state;
1459 GdbDebugBreak(true);
1462 SetBreakpoint(bp, false);
1465 if(oldState == running)
1466 GdbExecContinue(false);
1469 ide.workspace.Save();
1472 void UpdateRemovedBreakpoint(Breakpoint bp)
1474 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::UpdateRemovedBreakpoint()");
1475 if(targeted && bp.inserted)
1477 DebuggerState oldState = state;
1482 GdbDebugBreak(true);
1485 UnsetBreakpoint(bp);
1488 if(oldState == running)
1489 GdbExecContinue(false);
1495 void ParseFrame(Frame frame, char * string)
1498 Array<char *> frameTokens { minAllocSize = 50 };
1499 Array<char *> argsTokens { minAllocSize = 50 };
1500 Array<char *> argumentTokens { minAllocSize = 50 };
1501 DebugListItem item { };
1504 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ParseFrame()");
1505 TokenizeList(string, ',', frameTokens);
1506 for(i = 0; i < frameTokens.count; i++)
1508 if(TokenizeListItem(frameTokens[i], item))
1510 StripQuotes(item.value, item.value);
1511 if(!strcmp(item.name, "level"))
1512 frame.level = atoi(item.value);
1513 else if(!strcmp(item.name, "addr"))
1514 frame.addr = item.value;
1515 else if(!strcmp(item.name, "func"))
1516 frame.func = item.value;
1517 else if(!strcmp(item.name, "args"))
1519 if(!strcmp(item.value, "[]"))
1520 frame.argsCount = 0;
1523 item.value = StripBrackets(item.value);
1524 TokenizeList(item.value, ',', argsTokens);
1525 for(j = 0; j < argsTokens.count; j++)
1527 argsTokens[j] = StripCurlies(argsTokens[j]);
1528 TokenizeList(argsTokens[j], ',', argumentTokens);
1529 for(k = 0; k < argumentTokens.count; k++)
1532 frame.args.Add(arg);
1533 if(TokenizeListItem(argumentTokens[k], item))
1535 if(!strcmp(item.name, "name"))
1537 StripQuotes(item.value, item.value);
1538 arg.name = item.value;
1540 else if(!strcmp(item.name, "value"))
1542 StripQuotes(item.value, item.value);
1543 arg.val = item.value;
1546 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "frame args item (", item.name, "=", item.value, ") is unheard of");
1549 _dpl(0, "Bad frame args item");
1551 argumentTokens.RemoveAll();
1553 frame.argsCount = argsTokens.count;
1554 argsTokens.RemoveAll();
1557 else if(!strcmp(item.name, "from"))
1558 frame.from = item.value;
1559 else if(!strcmp(item.name, "file"))
1560 frame.file = item.value;
1561 else if(!strcmp(item.name, "line"))
1562 frame.line = atoi(item.value);
1563 else if(!strcmp(item.name, "fullname"))
1564 frame.absoluteFile = item.value;
1566 // GDB 6.3 on OS X is giving "fullname" and "dir", all in absolute, but file name only in 'file'
1567 String path = ide.workspace.GetPathWorkspaceRelativeOrAbsolute(item.value);
1568 if(strcmp(frame.file, path))
1571 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1576 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "frame member (", item.name, "=", item.value, ") is unheard of");
1579 _dpl(0, "Bad frame");
1584 delete argumentTokens;
1588 Breakpoint GetBreakpointById(int id, bool * isInternal)
1590 Breakpoint bp = null;
1591 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetBreakpointById(", id, ")");
1593 *isInternal = false;
1596 for(i : sysBPs; i.bp && i.bp.id == id)
1603 if(!bp && bpRunToCursor && bpRunToCursor.bp && bpRunToCursor.bp.id == id)
1607 for(i : ide.workspace.breakpoints; i.bp && i.bp.id == id)
1617 GdbDataBreakpoint ParseBreakpoint(char * string, Array<char *> outTokens)
1620 GdbDataBreakpoint bp { };
1621 DebugListItem item { };
1622 Array<char *> bpTokens { minAllocSize = 16 };
1623 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ParseBreakpoint()");
1624 string = StripCurlies(string);
1625 TokenizeList(string, ',', bpTokens);
1626 for(i = 0; i < bpTokens.count; i++)
1628 if(TokenizeListItem(bpTokens[i], item))
1630 StripQuotes(item.value, item.value);
1631 if(!strcmp(item.name, "number"))
1633 if(!strchr(item.value, '.'))
1634 bp.id = atoi(item.value);
1635 bp.number = item.value;
1637 else if(!strcmp(item.name, "type"))
1638 bp.type = item.value;
1639 else if(!strcmp(item.name, "disp"))
1640 bp.disp = item.value;
1641 else if(!strcmp(item.name, "enabled"))
1642 bp.enabled = (!strcmpi(item.value, "y"));
1643 else if(!strcmp(item.name, "addr"))
1645 if(outTokens && !strcmp(item.value, "<MULTIPLE>"))
1648 Array<GdbDataBreakpoint> bpArray = bp.multipleBPs = { };
1649 while(outTokens.count > ++c)
1651 GdbDataBreakpoint multBp = ParseBreakpoint(outTokens[c], null);
1652 bpArray.Add(multBp);
1656 bp.addr = item.value;
1658 else if(!strcmp(item.name, "func"))
1659 bp.func = item.value;
1660 else if(!strcmp(item.name, "file"))
1661 bp.file = item.value;
1662 else if(!strcmp(item.name, "fullname"))
1663 bp.fullname = item.value;
1664 else if(!strcmp(item.name, "line"))
1665 bp.line = atoi(item.value);
1666 else if(!strcmp(item.name, "at"))
1668 else if(!strcmp(item.name, "times"))
1669 bp.times = atoi(item.value);
1670 else if(!strcmp(item.name, "original-location") || !strcmp(item.name, "thread-groups"))
1671 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "breakpoint member (", item.name, "=", item.value, ") is ignored");
1673 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "breakpoint member (", item.name, "=", item.value, ") is unheard of");
1679 void ShowDebuggerViews()
1681 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ShowDebuggerViews()");
1682 ide.outputView.Show();
1683 ide.outputView.SelectTab(debug);
1684 ide.threadsView.Show();
1685 ide.callStackView.Show();
1686 ide.watchesView.Show();
1690 void HideDebuggerViews()
1692 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::HideDebuggerViews()");
1693 ide.RepositionWindows(true);
1696 void ::GdbCommand(bool focus, char * format, ...)
1700 // TODO: Improve this limit
1701 static char string[MAX_F_STRING*4];
1703 va_start(args, format);
1704 vsnprintf(string, sizeof(string), format, args);
1705 string[sizeof(string)-1] = 0;
1709 ide.debugger.serialSemaphore.TryWait();
1711 #ifdef GDB_DEBUG_CONSOLE
1712 _dpl2(_dpct, dplchan::gdbCommand, 0, string);
1714 #ifdef GDB_DEBUG_OUTPUT
1715 ide.outputView.gdbBox.Logf("cmd: %s\n", string);
1717 #ifdef GDB_DEBUG_GUI
1719 ide.gdbDialog.AddCommand(string);
1722 strcat(string,"\n");
1723 gdbHandle.Puts(string);
1726 Process_ShowWindows(targetProcessId);
1729 ide.debugger.serialSemaphore.Wait();
1734 bool ValidateBreakpoint(Breakpoint bp)
1736 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ValidateBreakpoint()");
1737 if(modules && bp.line && bp.bp)
1739 if(bp.bp.line != bp.line)
1745 ide.outputView.debugBox.Logf("WOULD HAVE -- Invalid breakpoint disabled: %s:%d\n", bp.relativeFilePath, bp.line);
1747 //UnsetBreakpoint(bp);
1748 //bp.enabled = false;
1754 ide.outputView.debugBox.Logf("Debugger Error: ValidateBreakpoint error\n");
1755 bp.line = bp.bp.line;
1762 void GdbBreakpointsInsert()
1764 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbBreakpointsInsert()");
1767 if(userAction != stepOut && (userAction != stepOver || state == loaded))
1769 DirExpression objDir = ide.project.GetObjDir(currentCompiler, prjConfig, bitDepth);
1770 for(bp : sysBPs; !bp.inserted)
1772 bool insert = false;
1773 if(bp.type == internalModulesLoaded)
1775 char path[MAX_LOCATION];
1776 char name[MAX_LOCATION];
1777 char fixedModuleName[MAX_FILENAME];
1780 bool moduleLoadBlock = false;
1782 ReplaceSpaces(fixedModuleName, ide.project.moduleName);
1783 snprintf(name, sizeof(name),"%s.main.ec", fixedModuleName);
1784 name[sizeof(name)-1] = 0;
1785 strcpy(path, ide.workspace.projectDir);
1786 PathCatSlash(path, objDir.dir);
1787 PathCatSlash(path, name);
1788 f = FileOpen(path, read);
1791 for(lineNumber = 1; !f.Eof(); lineNumber++)
1793 if(f.GetLine(line, sizeof(line) - 1))
1795 bool moduleLoadLine;
1796 TrimLSpaces(line, line);
1797 moduleLoadLine = !strncmp(line, "eModule_Load", strlen("eModule_Load"));
1798 if(!moduleLoadBlock && moduleLoadLine)
1799 moduleLoadBlock = true;
1800 else if(moduleLoadBlock && !moduleLoadLine && strlen(line) > 0)
1806 char relative[MAX_LOCATION];
1807 bp.absoluteFilePath = path;
1808 MakePathRelative(path, ide.workspace.projectDir, relative);
1809 bp.relativeFilePath = relative;
1810 bp.line = lineNumber;
1816 else if(bp.type == internalModuleLoad)
1820 for(prj : ide.workspace.projects)
1822 if(!strcmp(prj.moduleName, "ecere"))
1824 ProjectNode node = prj.topNode.Find("instance.c", false);
1827 char path[MAX_LOCATION];
1828 char relative[MAX_LOCATION];
1829 node.GetFullFilePath(path);
1830 bp.absoluteFilePath = path;
1831 MakePathRelative(path, prj.topNode.path, relative);
1832 bp.relativeFilePath = relative;
1843 SetBreakpoint(bp, false);
1848 if(bpRunToCursor && !bpRunToCursor.inserted)
1849 SetBreakpoint(bpRunToCursor, false);
1851 if(!ignoreBreakpoints)
1853 for(bp : ide.workspace.breakpoints; !bp.inserted && bp.type == user)
1857 if(!SetBreakpoint(bp, false))
1858 SetBreakpoint(bp, true);
1866 bp.bp = GdbDataBreakpoint { };
1873 void UnsetBreakpoint(Breakpoint bp)
1875 char * s; _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::UnsetBreakpoint(", s=bp.CopyLocationString(false), ")"); delete s;
1876 if(symbols && bp.inserted)
1878 GdbCommand(false, "-break-delete %s", bp.bp.number);
1879 bp.inserted = false;
1884 bool SetBreakpoint(Breakpoint bp, bool removePath)
1886 char * s; _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SetBreakpoint(", s=bp.CopyLocationString(false), ", ", removePath ? "**** removePath(true) ****" : "", ")"); delete s;
1887 breakpointError = false;
1890 char * location = bp.CopyLocationString(removePath);
1891 sentBreakInsert = true;
1892 GdbCommand(false, "-break-insert %s", location);
1894 if(!breakpointError)
1896 if(bpItem && bpItem.multipleBPs && bpItem.multipleBPs.count)
1899 GdbDataBreakpoint first = null;
1900 for(n : bpItem.multipleBPs)
1902 if(!fstrcmp(n.fullname, bp.absoluteFilePath))
1912 GdbCommand(false, "-break-disable %s", n.number);
1916 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error breakpoint already disabled.");
1921 bpItem.addr = first.addr;
1922 bpItem.func = first.func;
1923 bpItem.file = first.file;
1924 bpItem.fullname = first.fullname;
1925 bpItem.line = first.line;
1926 //bpItem.thread-groups = first.thread-groups;
1927 bpItem.multipleBPs.Free();
1928 delete bpItem.multipleBPs;
1931 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error multiple breakpoints all disabled.");
1933 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error multiple breakpoints in exact same file not supported.");
1937 bp.inserted = (bp.bp && bp.bp.number && strcmp(bp.bp.number, "0"));
1939 ValidateBreakpoint(bp);
1940 /*if(bp == bpRunToCursor)
1941 runToCursorDebugStart = false;*/
1944 return !breakpointError;
1947 void GdbBreakpointsDelete(bool deleteRunToCursor, bool deleteInternalBreakpoints, bool deleteUserBreakpoints)
1949 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbBreakpointsDelete(deleteRunToCursor(", deleteRunToCursor, "))");
1952 if(deleteInternalBreakpoints)
1955 UnsetBreakpoint(bp);
1957 if(deleteUserBreakpoints)
1959 for(bp : ide.workspace.breakpoints)
1960 UnsetBreakpoint(bp);
1962 if(deleteRunToCursor && bpRunToCursor)
1963 UnsetBreakpoint(bpRunToCursor);
1969 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbGetStack()");
1971 stackFrames.Free(Frame::Free);
1972 GdbCommand(false, "-stack-info-depth");
1974 GdbCommand(false, "-stack-info-depth 192");
1975 if(frameCount && frameCount <= 192)
1976 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 191));
1979 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 95));
1980 GdbCommand(false, "-stack-list-frames %d %d", Max(frameCount - 96, 96), frameCount - 1);
1982 GdbCommand(false, "");
1987 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbTargetSet()");
1990 char escaped[MAX_LOCATION];
1991 strescpy(escaped, targetFile);
1992 GdbCommand(false, "file \"%s\"", escaped); //GDB/MI Missing Implementation -symbol-file, -target-attach
1999 const char *vgdbCommand = "/usr/bin/vgdb"; // TODO: vgdb command config option
2000 //GdbCommand(false, "-target-select remote | %s --pid=%d", "vgdb", targetProcessId);
2001 printf("target remote | %s --pid=%d\n", vgdbCommand, targetProcessId);
2002 GdbCommand(false, "target remote | %s --pid=%d", vgdbCommand, targetProcessId); // TODO: vgdb command config option
2005 /*for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
2006 GdbCommand(false, "-environment-directory \"%s\"", prj.topNode.path);*/
2008 for(dir : ide.workspace.sourceDirs; dir && dir[0])
2010 bool interference = false;
2011 for(prj : ide.workspace.projects)
2013 if(!fstrcmp(prj.topNode.path, dir))
2015 interference = true;
2019 if(!interference && dir[0])
2020 GdbCommand(false, "-environment-directory \"%s\"", dir);
2028 /*void GdbTargetRelease()
2032 GdbBreakpointsDelete(true, true, true);
2033 GdbCommand(false, "file"); //GDB/MI Missing Implementation -target-detach
2039 void GdbDebugBreak(bool internal)
2041 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbDebugBreak()");
2045 breakType = DebuggerAction::internal;
2047 if(ide) ide.Update(null);
2049 if(Process_Break(targetProcessId)) //GdbCommand(false, "-exec-interrupt");
2050 serialSemaphore.Wait();
2053 ChangeState(loaded);
2054 targetProcessId = 0;
2059 ide.outputView.debugBox.Logf("Debugger Error: GdbDebugBreak with not target id should never happen\n");
2064 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecRun()");
2067 ShowDebuggerViews();
2069 GdbCommand(true, "-exec-continue");
2071 GdbCommand(true, "-exec-run");
2074 void GdbExecContinue(bool focus)
2076 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecContinue()");
2078 GdbCommand(focus, "-exec-continue");
2083 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecNext()");
2085 GdbCommand(true, "-exec-next");
2090 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecStep()");
2092 GdbCommand(true, "-exec-step");
2095 void GdbExecFinish()
2097 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecFinish()");
2099 GdbCommand(true, "-exec-finish");
2102 void GdbExecCommon()
2104 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecCommon()");
2105 GdbBreakpointsInsert();
2108 #ifdef GDB_DEBUG_GUI
2109 void SendGDBCommand(char * command)
2111 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SendGDBCommand()");
2112 DebuggerState oldState = state;
2117 GdbDebugBreak(true);
2120 GdbCommand(false, command);
2123 if(oldState == running)
2124 GdbExecContinue(false);
2128 void ClearBreakDisplay()
2130 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ClearBreakDisplay()");
2132 activeFrameLevel = -1;
2142 stackFrames.Free(Frame::Free);
2143 WatchesCodeEditorLinkRelease();
2144 ide.callStackView.Clear();
2145 ide.threadsView.Clear();
2151 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbAbortExec()");
2153 GdbCommand(false, "-interpreter-exec console \"kill\""); // should use -exec-abort -- GDB/MI implementation incomplete
2157 bool GdbInit(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
2160 char oldDirectory[MAX_LOCATION];
2161 char tempPath[MAX_LOCATION];
2162 char command[MAX_F_STRING*4];
2163 Project project = ide.project;
2164 DirExpression targetDirExp = project.GetTargetDir(compiler, config, bitDepth);
2165 PathBackup pathBackup { };
2167 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbInit()");
2168 if(currentCompiler != compiler)
2170 delete currentCompiler;
2171 currentCompiler = compiler;
2172 incref currentCompiler;
2175 this.bitDepth = bitDepth;
2176 usingValgrind = useValgrind;
2178 ChangeState(loaded);
2180 sentBreakInsert = false;
2181 breakpointError = false;
2182 ignoreBreakpoints = false;
2188 ide.outputView.ShowClearSelectTab(debug);
2189 ide.outputView.debugBox.Logf($"Starting debug mode\n");
2191 #ifdef GDB_DEBUG_OUTPUT
2192 ide.outputView.gdbBox.Logf("run: Starting GDB\n");
2195 strcpy(tempPath, ide.workspace.projectDir);
2196 PathCatSlash(tempPath, targetDirExp.dir);
2198 targetDir = CopyString(tempPath);
2199 project.CatTargetFileName(tempPath, compiler, config);
2201 targetFile = CopyString(tempPath);
2203 GetWorkingDir(oldDirectory, MAX_LOCATION);
2204 if(ide.workspace.debugDir && ide.workspace.debugDir[0])
2206 char temp[MAX_LOCATION];
2207 strcpy(temp, ide.workspace.projectDir);
2208 PathCatSlash(temp, ide.workspace.debugDir);
2209 ChangeWorkingDir(temp);
2212 ChangeWorkingDir(ide.workspace.projectDir);
2214 ide.SetPath(true, compiler, config, bitDepth);
2216 // TODO: This pollutes the environment, but at least it works
2217 // It shouldn't really affect the IDE as the PATH gets restored and other variables set for testing will unlikely cause problems
2218 // What is the proper solution for this? DualPipeOpenEnv?
2219 // gdb set environment commands don't seem to take effect
2220 for(e : ide.workspace.environmentVars)
2222 SetEnvironment(e.name, e.string);
2227 char * clArgs = ide.workspace.commandLineArgs;
2228 const char *valgrindCommand = "valgrind"; // TODO: valgrind command config option //TODO: valgrind options
2229 ValgrindLeakCheck vgLeakCheck = ide.workspace.vgLeakCheck;
2230 int vgRedzoneSize = ide.workspace.vgRedzoneSize;
2231 bool vgTrackOrigins = ide.workspace.vgTrackOrigins;
2232 vgLogFile = CreateTemporaryFile(vgLogPath, "ecereidevglog");
2236 vgLogThread.Create();
2240 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't open temporary log file for Valgrind output\n");
2243 if(result && !CheckCommandAvailable(valgrindCommand))
2245 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for Valgrind is not available.\n", valgrindCommand);
2250 char * vgRedzoneSizeFlag = vgRedzoneSize == -1 ? "" : PrintString(" --redzone-size=", vgRedzoneSize);
2251 sprintf(command, "%s --vgdb=yes --vgdb-error=0 --log-file=%s --leak-check=%s%s --track-origins=%s %s%s%s",
2252 valgrindCommand, vgLogPath, (char*)vgLeakCheck, vgRedzoneSizeFlag, vgTrackOrigins ? "yes" : "no", targetFile, clArgs ? " " : "", clArgs ? clArgs : "");
2253 if(vgRedzoneSize != -1)
2254 delete vgRedzoneSizeFlag;
2255 vgTargetHandle = DualPipeOpen(PipeOpenMode { output = true, /*error = true, */input = true }, command);
2258 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start Valgrind\n");
2264 incref vgTargetHandle;
2265 vgTargetThread.Create();
2267 targetProcessId = vgTargetHandle.GetProcessID();
2268 waitingForPID = false;
2269 if(!targetProcessId)
2271 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get Valgrind process ID\n");
2278 serialSemaphore.Wait();
2286 (compiler.targetPlatform == win32 && bitDepth == 64) ? "x86_64-w64-mingw32-gdb" :
2287 (compiler.targetPlatform == win32 && bitDepth == 32) ? "i686-w64-mingw32-gdb" :
2289 if(!CheckCommandAvailable(command))
2291 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for GDB is not available.\n", command);
2296 strcat(command, " -n -silent --interpreter=mi2"); //-async //\"%s\"
2298 gdbHandle = DualPipeOpen(PipeOpenMode { output = true, /*error = true, */input = true }, command);
2301 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start GDB\n");
2311 gdbProcessId = gdbHandle.GetProcessID();
2314 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get GDB process ID\n");
2321 serialSemaphore.Wait();
2324 GdbCommand(false, "-gdb-set verbose off");
2325 //GdbCommand(false, "-gdb-set exec-done-display on");
2326 GdbCommand(false, "-gdb-set step-mode off");
2327 GdbCommand(false, "-gdb-set unwindonsignal on");
2328 //GdbCommand(false, "-gdb-set shell on");
2329 GdbCommand(false, "set print elements 992");
2330 GdbCommand(false, "-gdb-set backtrace limit 100000");
2334 //ChangeState(terminated);
2340 #if defined(__unix__)
2342 CreateTemporaryDir(progFifoDir, "ecereide");
2343 strcpy(progFifoPath, progFifoDir);
2344 PathCat(progFifoPath, "ideprogfifo");
2345 if(!mkfifo(progFifoPath, 0600))
2347 //fileCreated = true;
2352 ide.outputView.debugBox.Logf(createFIFOMsg, progFifoPath);
2359 progThread.terminate = false;
2360 progThread.Create();
2364 #if defined(__WIN32__)
2365 GdbCommand(false, "-gdb-set new-console on");
2368 #if defined(__unix__)
2370 GdbCommand(false, "-inferior-tty-set %s", progFifoPath);
2374 GdbCommand(false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
2376 for(e : ide.workspace.environmentVars)
2378 GdbCommand(false, "set environment %s=%s", e.name, e.string);
2383 ChangeWorkingDir(oldDirectory);
2389 delete targetDirExp;
2395 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExit()");
2396 if(gdbHandle && gdbProcessId)
2398 GdbCommand(false, "-gdb-exit");
2413 ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
2419 for(bp : ide.workspace.breakpoints)
2421 bp.inserted = false;
2427 bp.inserted = false;
2432 bpRunToCursor.inserted = false;
2433 delete bpRunToCursor.bp;
2436 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2437 ClearBreakDisplay();
2440 #if defined(__unix__)
2441 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
2443 progThread.terminate = true;
2446 fifoFile.CloseInput();
2452 DeleteFile(progFifoPath);
2453 progFifoPath[0] = '\0';
2459 void WatchesCodeEditorLinkInit()
2461 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkInit()");
2463 char tempPath[MAX_LOCATION];
2464 char path[MAX_LOCATION];
2466 //void MakeFilePathProjectRelative(char * path, char * relativePath)
2467 if(!ide.projectView.project.GetRelativePath(activeFrame.file, tempPath))
2468 strcpy(tempPath, activeFrame.file);
2470 strcpy(path, ide.workspace.projectDir);
2471 PathCat(path, tempPath);
2472 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2475 for(srcDir : ide.workspace.sourceDirs)
2477 strcpy(path, srcDir);
2478 PathCat(path, tempPath);
2479 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2480 if(codeEditor) break;
2485 /*if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2486 activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);*/
2487 if(!activeFrame || !activeFrame.absoluteFile)
2490 codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal, false);
2493 codeEditor.inUseDebug = true;
2496 //watchesInit = true;
2499 void WatchesCodeEditorLinkRelease()
2501 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesCodeEditorLinkRelease()");
2506 codeEditor.inUseDebug = false;
2507 if(!codeEditor.visible)
2508 codeEditor.Destroy(0);
2514 bool ResolveWatch(Watch wh)
2516 bool result = false;
2518 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ResolveWatch()");
2530 char watchmsg[MAX_F_STRING];
2531 if(state == stopped && !codeEditor)
2532 wh.value = CopyString($"No source file found for selected frame");
2533 //if(codeEditor && state == stopped || state != stopped)
2536 Module backupPrivateModule;
2537 Context backupContext;
2538 Class backupThisClass;
2542 backupPrivateModule = GetPrivateModule();
2543 backupContext = GetCurrentContext();
2544 backupThisClass = GetThisClass();
2547 SetPrivateModule(codeEditor.privateModule);
2548 SetCurrentContext(codeEditor.globalContext);
2549 SetTopContext(codeEditor.globalContext);
2550 SetGlobalContext(codeEditor.globalContext);
2551 SetGlobalData(&codeEditor.globalData);
2554 exp = ParseExpressionString(wh.expression);
2556 if(exp && !parseError)
2558 char expString[4096];
2560 PrintExpression(exp, expString);
2562 if(GetPrivateModule())
2565 DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2566 ProcessExpressionType(exp);
2568 wh.type = exp.expType;
2571 DebugComputeExpression(exp);
2572 if(ExpressionIsError(exp))
2574 GDBFallBack(exp, expString);
2577 /*if(exp.hasAddress)
2579 char temp[MAX_F_STRING];
2580 sprintf(temp, "0x%x", exp.address);
2581 wh.address = CopyString(temp);
2582 // wh.address = CopyStringf("0x%x", exp.address);
2587 Type dataType = exp.expType;
2590 char temp[MAX_F_STRING];
2591 switch(dataType.kind)
2594 sprintf(temp, "%i", exp.val.c);
2597 sprintf(temp, "%i", exp.val.s);
2602 sprintf(temp, "%i", exp.val.i);
2605 sprintf(temp, "%i", exp.val.i64);
2608 sprintf(temp, "%i", exp.val.p);
2613 long v = (long)exp.val.f;
2614 sprintf(temp, "%i", v);
2619 long v = (long)exp.val.d;
2620 sprintf(temp, "%i", v);
2625 wh.intVal = CopyString(temp);
2626 switch(dataType.kind)
2629 sprintf(temp, "0x%x", exp.val.c);
2632 sprintf(temp, "0x%x", exp.val.s);
2636 sprintf(temp, "0x%x", exp.val.i);
2639 sprintf(temp, "0x%x", exp.val.i64);
2642 sprintf(temp, "0x%x", exp.val.i64);
2645 sprintf(temp, "0x%x", exp.val.p);
2650 long v = (long)exp.val.f;
2651 sprintf(temp, "0x%x", v);
2656 long v = (long)exp.val.d;
2657 sprintf(temp, "0x%x", v);
2662 wh.hexVal = CopyString(temp);
2663 switch(dataType.kind)
2666 sprintf(temp, "0o%o", exp.val.c);
2669 sprintf(temp, "0o%o", exp.val.s);
2673 sprintf(temp, "0o%o", exp.val.i);
2676 sprintf(temp, "0o%o", exp.val.i64);
2679 sprintf(temp, "0o%o", exp.val.i64);
2682 sprintf(temp, "0o%o", exp.val.p);
2687 long v = (long)exp.val.f;
2688 sprintf(temp, "0o%o", v);
2693 long v = (long)exp.val.d;
2694 sprintf(temp, "0o%o", v);
2699 wh.octVal = CopyString(temp);
2702 // WHATS THIS HERE ?
2703 if(exp.type == constantExp && exp.constant)
2704 wh.constant = CopyString(exp.constant);
2710 case symbolErrorExp:
2711 snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2713 case structMemberSymbolErrorExp:
2714 // todo get info as in next case (ExpClassMemberSymbolError)
2715 snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2717 case classMemberSymbolErrorExp:
2720 Expression memberExp = exp.member.exp;
2721 Identifier memberID = exp.member.member;
2722 Type type = memberExp.expType;
2725 _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2728 char string[256] = "";
2730 PrintTypeNoConst(type, string, false, true);
2731 classSym = FindClass(string);
2732 _class = classSym ? classSym.registered : null;
2735 snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2737 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2740 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2743 case memoryErrorExp:
2744 // Need to ensure when set to memoryErrorExp, constant is set
2745 snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2747 case dereferenceErrorExp:
2748 snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2750 case unknownErrorExp:
2751 snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2753 case noDebuggerErrorExp:
2754 snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2756 case debugStateErrorExp:
2757 snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2760 snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2764 // Temporary Code for displaying Strings
2765 if((exp.expType && ((exp.expType.kind == pointerType ||
2766 exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) ||
2767 (wh.type && wh.type.kind == classType && wh.type._class &&
2768 wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2769 !strcmp(wh.type._class.registered.name, "String")))
2772 if(exp.expType.kind != arrayType || exp.hasAddress)
2778 //char temp[MAX_F_STRING * 32];
2780 ExpressionType evalError = dummyExp;
2781 /*if(exp.expType.kind == arrayType)
2782 sprintf(temp, "(char*)0x%x", exp.address);
2784 sprintf(temp, "(char*)%s", exp.constant);*/
2786 //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2787 // address = strtoul(exp.constant, null, 0);
2788 address = _strtoui64(exp.constant, null, 0);
2789 //_dpl(0, "0x", address);
2790 // snprintf(value, sizeof(value), "0x%08x ", address);
2792 if(address > 0xFFFFFFFFLL)
2793 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2795 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2796 value[sizeof(value)-1] = 0;
2799 strcat(value, $"Null string");
2803 len = strlen(value);
2805 while(!string && size > 2)
2807 string = GdbReadMemory(address, size);
2810 if(string && string[0])
2813 if(UTF8Validate(string))
2818 for(c = 0; (ch = string[c]) && c<4096; c++)
2821 value[len++] = '\0';
2826 ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2827 strcat(value, ") (ISO8859-1)");
2834 strcat(value, $"Empty string");
2838 strcat(value, $"Couldn't read memory");
2840 wh.value = CopyString(value);
2843 else if(wh.type && wh.type.kind == classType && wh.type._class &&
2844 wh.type._class.registered && wh.type._class.registered.type == enumClass)
2846 uint64 value = strtoul(exp.constant, null, 0);
2847 Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2848 EnumClassData enumeration = (EnumClassData)enumClass.data;
2850 for(item = enumeration.values.first; item; item = item.next)
2851 if((int)item.data == value)
2854 wh.value = CopyString(item.name);
2856 wh.value = CopyString($"Invalid Enum Value");
2859 else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class &&
2860 wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2867 if(exp.constant[0] == '\'')
2869 if((int)((byte *)exp.constant)[1] > 127)
2872 value = UTF8GetChar(exp.constant + 1, &nb);
2873 if(nb < 2) value = exp.constant[1];
2874 signedValue = value;
2878 signedValue = exp.constant[1];
2880 // Precomp Syntax error with boot strap here:
2881 byte b = (byte)(char)signedValue;
2882 value = (unichar) b;
2888 if(wh.type.kind == charType && wh.type.isSigned)
2890 signedValue = (int)(char)strtol(exp.constant, null, 0);
2892 // Precomp Syntax error with boot strap here:
2893 byte b = (byte)(char)signedValue;
2894 value = (unichar) b;
2899 value = (uint)strtoul(exp.constant, null, 0);
2900 signedValue = (int)value;
2904 UTF32toUTF8Len(&value, 1, charString, 5);
2906 snprintf(string, sizeof(string), "\'\\0' (0)");
2907 else if(value == '\t')
2908 snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2909 else if(value == '\n')
2910 snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2911 else if(value == '\r')
2912 snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2913 else if(wh.type.kind == charType && wh.type.isSigned)
2914 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2915 else if(value > 256 || wh.type.kind != charType)
2917 if(value > 0x10FFFF || !GetCharCategory(value))
2918 snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2920 snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2923 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2924 string[sizeof(string)-1] = 0;
2926 wh.value = CopyString(string);
2931 wh.value = CopyString(exp.constant);
2938 wh.value = PrintHexUInt64(exp.address);
2943 char tempString[256];
2944 if(exp.member.memberType == propertyMember)
2945 snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2947 snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression,
2948 exp.type.OnGetString(tempString, null, null));
2954 snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2955 if(exp) FreeExpression(exp);
2958 SetPrivateModule(backupPrivateModule);
2959 SetCurrentContext(backupContext);
2960 SetTopContext(backupContext);
2961 SetGlobalContext(backupContext);
2962 SetThisClass(backupThisClass);
2965 // wh.value = CopyString("No source file found for selected frame");
2967 watchmsg[sizeof(watchmsg)-1] = 0;
2969 wh.value = CopyString(watchmsg);
2971 ide.watchesView.UpdateWatch(wh);
2975 void EvaluateWatches()
2977 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateWatches()");
2978 for(wh : ide.workspace.watches)
2982 char * ::GdbEvaluateExpression(char * expression)
2984 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbEvaluateExpression(", expression, ")");
2987 GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2989 ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2993 // to be removed... use GdbReadMemory that returns a byte array instead
2994 char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
2996 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemoryString(", address, ")");
3001 _dpl(0, "GdbReadMemoryString called with size = 0!");
3003 // GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
3004 if(GetRuntimePlatform() == win32)
3005 GdbCommand(false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
3007 GdbCommand(false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
3009 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
3013 byte * ::GdbReadMemory(uint64 address, int bytes)
3015 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemory(", address, ")");
3018 //GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
3019 if(GetRuntimePlatform() == win32)
3020 GdbCommand(false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
3022 GdbCommand(false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
3025 _dpl(0, "GdbReadMemory called with bytes = 0!");
3028 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
3029 else if(eval.result && strcmp(eval.result, "N/A"))
3031 byte * result = new byte[bytes];
3032 byte * string = eval.result;
3036 result[c++] = (byte)strtol(string, &string, 10);
3052 void EventHit(GdbDataStop stopItem, Breakpoint bpInternal, Breakpoint bpUser)
3054 char * s1 = null; char * s2 = null;
3055 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EventHit(",
3056 "bpInternal(", bpInternal ? s1=bpInternal.CopyLocationString(false) : null, "), ",
3057 "bpUser(", bpUser ? s2=bpUser.CopyLocationString(false) : null, ")) -- ",
3058 "ignoreBreakpoints(", ignoreBreakpoints, "), ",
3059 "hitCursorBreakpoint(", bpUser && bpUser.type == runToCursor, ")");
3060 delete s1; delete s2;
3062 if(bpUser && stopItem.frame.line && bpUser.line != stopItem.frame.line)
3064 // updating user breakpoint on hit location difference
3065 // todo, print something?
3066 bpUser.line = stopItem.frame.line;
3067 ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3068 ide.workspace.Save();
3073 if(bpInternal.type == internalModulesLoaded)
3075 if(!bpUser && !userBreakOnInternalBreakpoint)
3077 if(userAction == stepOut)//if(prevStopItem.reason == functionFinished)
3078 StepOut(ignoreBreakpoints);
3080 GdbExecContinue(false);
3085 bool conditionMet = true;
3086 if(bpUser.condition)
3087 conditionMet = ResolveWatch(bpUser.condition);
3089 if(!ignoreBreakpoints && (bpUser.level == -1 || bpUser.level == frameCount-1) && conditionMet)
3096 GdbExecContinue(false);
3100 GdbExecContinue(false);
3101 ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3102 if(bpUser == bpRunToCursor)
3104 UnsetBreakpoint(bpUser);
3105 delete bpRunToCursor;
3109 if(!bpUser && !bpInternal)
3110 GdbExecContinue(false);
3113 void ValgrindTargetThreadExit()
3115 ide.outputView.debugBox.Logf($"ValgrindTargetThreadExit\n");
3118 vgTargetHandle.Wait();
3119 delete vgTargetHandle;
3121 HandleExit(null, null);
3124 void GdbThreadExit()
3126 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbThreadExit()");
3127 if(state != terminated)
3129 ChangeState(terminated);
3130 targetProcessId = 0;
3131 ClearBreakDisplay();
3137 serialSemaphore.Release();
3142 ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
3143 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3145 HideDebuggerViews();
3147 //ChangeState(terminated);
3151 void GdbThreadMain(char * output)
3154 Array<char *> outTokens { minAllocSize = 50 };
3155 Array<char *> subTokens { minAllocSize = 50 };
3156 DebugListItem item { };
3157 DebugListItem item2 { };
3158 bool setWaitingForPID = false;
3160 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
3161 #ifdef GDB_DEBUG_CONSOLE
3162 _dpl2(_dpct, dplchan::gdbOutput, 0, output);
3164 #ifdef GDB_DEBUG_OUTPUT
3166 int len = strlen(output);
3174 for(c = 0; c < len / 1024; c++)
3176 strncpy(tmp, start, 1024);
3177 ide.outputView.gdbBox.Logf("out: %s\n", tmp);
3180 ide.outputView.gdbBox.Logf("out: %s\n", start);
3184 ide.outputView.gdbBox.Logf("out: %s\n", output);
3188 #ifdef GDB_DEBUG_CONSOLE
3189 strcpy(lastGdbOutput, output);
3191 #ifdef GDB_DEBUG_GUI
3192 if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
3199 if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
3202 ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
3208 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
3210 //if(outTokens.count == 1)
3215 ChangeState(loaded);
3216 targetProcessId = 0;
3217 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3219 if(!strcmp(item.name, "reason"))
3221 char * reason = item.value;
3222 StripQuotes(reason, reason);
3223 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3226 if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
3228 StripQuotes(item2.value, item2.value);
3229 if(!strcmp(item2.name, "exit-code"))
3230 exitCode = item2.value;
3236 HandleExit(reason, exitCode);
3240 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "kill reply (", item.name, "=", item.value, ") is unheard of");
3243 HandleExit(null, null);
3246 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3248 if(!strcmp(item.name, "bkpt"))
3250 sentBreakInsert = false;
3255 bpItem = ParseBreakpoint(item.value, outTokens);
3256 //breakType = bpValidation;
3258 else if(!strcmp(item.name, "depth"))
3260 StripQuotes(item.value, item.value);
3261 frameCount = atoi(item.value);
3263 stackFrames.Free(Frame::Free);
3265 else if(!strcmp(item.name, "stack"))
3268 if(stackFrames.count)
3269 ide.callStackView.Logf("...\n");
3272 item.value = StripBrackets(item.value);
3273 TokenizeList(item.value, ',', subTokens);
3274 for(i = 0; i < subTokens.count; i++)
3276 if(TokenizeListItem(subTokens[i], item))
3278 if(!strcmp(item.name, "frame"))
3281 stackFrames.Add(frame);
3282 item.value = StripCurlies(item.value);
3283 ParseFrame(frame, item.value);
3284 if(frame.file && frame.from)
3285 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "unexpected frame file and from members present");
3289 if(activeFrameLevel == -1)
3291 if(ide.projectView.IsModuleInProject(frame.file));
3293 if(frame.level != 0)
3295 //stopItem.frame = frame;
3296 breakType = selectFrame;
3299 activeFrame = frame;
3300 activeFrameLevel = frame.level;
3303 ide.callStackView.Logf("%3d ", frame.level);
3304 if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
3305 ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
3306 else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
3307 ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
3308 else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
3309 ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
3310 else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
3311 ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
3313 ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
3318 ide.callStackView.Logf("%3d ", frame.level);
3323 ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
3327 ide.callStackView.Logf("%s\n", frame.func);
3329 ide.callStackView.Logf($"unknown source\n");
3333 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "stack content (", item.name, "=", item.value, ") is unheard of");
3336 if(activeFrameLevel == -1)
3338 activeFrameLevel = 0;
3339 activeFrame = stackFrames.first;
3341 ide.callStackView.Home();
3343 subTokens.RemoveAll();
3345 /*else if(!strcmp(item.name, "frame"))
3348 item.value = StripCurlies(item.value);
3349 ParseFrame(&frame, item.value);
3351 else if(!strcmp(item.name, "thread-ids"))
3353 ide.threadsView.Clear();
3354 item.value = StripCurlies(item.value);
3355 TokenizeList(item.value, ',', subTokens);
3356 for(i = subTokens.count - 1; ; i--)
3358 if(TokenizeListItem(subTokens[i], item))
3360 if(!strcmp(item.name, "thread-id"))
3363 StripQuotes(item.value, item.value);
3364 value = atoi(item.value);
3365 ide.threadsView.Logf("%3d \n", value);
3368 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "threads content (", item.name, "=", item.value, ") is unheard of");
3373 ide.threadsView.Home();
3375 subTokens.RemoveAll();
3376 //if(!strcmp(outTokens[2], "number-of-threads"))
3378 else if(!strcmp(item.name, "new-thread-id"))
3380 StripQuotes(item.value, item.value);
3381 activeThread = atoi(item.value);
3383 else if(!strcmp(item.name, "value"))
3385 StripQuotes(item.value, item.value);
3386 eval.result = CopyString(item.value);
3387 eval.active = false;
3389 else if(!strcmp(item.name, "addr"))
3391 for(i = 2; i < outTokens.count; i++)
3393 if(TokenizeListItem(outTokens[i], item))
3395 if(!strcmp(item.name, "total-bytes"))
3397 StripQuotes(item.value, item.value);
3398 eval.bytes = atoi(item.value);
3400 else if(!strcmp(item.name, "next-row"))
3402 StripQuotes(item.value, item.value);
3403 eval.nextBlockAddress = _strtoui64(item.value, null, 0);
3405 else if(!strcmp(item.name, "memory"))
3409 //StripQuotes(item.value, item.value);
3410 item.value = StripBrackets(item.value);
3411 // this should be treated as a list...
3412 item.value = StripCurlies(item.value);
3413 TokenizeList(item.value, ',', subTokens);
3414 for(j = 0; j < subTokens.count; j++)
3416 if(TokenizeListItem(subTokens[j], item))
3418 if(!strcmp(item.name, "data"))
3420 item.value = StripBrackets(item.value);
3421 StripQuotes2(item.value, item.value);
3422 eval.result = CopyString(item.value);
3423 eval.active = false;
3427 subTokens.RemoveAll();
3432 else if(!strcmp(item.name, "source-path") || !strcmp(item.name, "BreakpointTable"))
3433 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "command reply (", item.name, "=", item.value, ") is ignored");
3435 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "command reply (", item.name, "=", item.value, ") is unheard of");
3438 else if(!strcmp(outTokens[0], "^running"))
3440 waitingForPID = true;
3441 setWaitingForPID = true;
3442 ClearBreakDisplay();
3444 else if(!strcmp(outTokens[0], "^exit"))
3446 ChangeState(terminated);
3447 // ide.outputView.debugBox.Logf("Exit\n");
3448 // ide.Update(null);
3450 serialSemaphore.Release();
3452 else if(!strcmp(outTokens[0], "^error"))
3456 sentBreakInsert = false;
3457 breakpointError = true;
3460 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3462 if(!strcmp(item.name, "msg"))
3464 StripQuotes(item.value, item.value);
3467 eval.active = false;
3469 if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3470 eval.error = symbolNotFound;
3471 else if(strstr(item.value, "Cannot access memory at address"))
3472 eval.error = memoryCantBeRead;
3474 eval.error = unknown;
3476 else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3479 else if(!strncmp(item.value, "Cannot access memory at address", 31))
3482 else if(!strcmp(item.value, "Cannot find bounds of current function"))
3484 ChangeState(stopped);
3485 gdbHandle.Printf("-exec-continue\n");
3487 else if(!strcmp(item.value, "ptrace: No such process."))
3489 ChangeState(loaded);
3490 targetProcessId = 0;
3492 else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3495 else if(!strcmp(item.value, "You can't do that without a process to debug."))
3497 ChangeState(loaded);
3498 targetProcessId = 0;
3500 else if(strstr(item.value, "No such file or directory."))
3502 ChangeState(loaded);
3503 targetProcessId = 0;
3505 else if(strstr(item.value, "During startup program exited with code "))
3507 ChangeState(loaded);
3508 targetProcessId = 0;
3513 if(strlen(item.value) < MAX_F_STRING)
3516 ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3520 ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3526 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "error content (", item.name, "=", item.value, ") is unheard of");
3529 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "result-record: ", outTokens[0]);
3530 outTokens.RemoveAll();
3533 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "status-async-output: ", outTokens[0]);
3536 if(TokenizeList(output, ',', outTokens))
3538 if(!strcmp(outTokens[0], "=library-loaded"))
3539 FGODetectLoadedLibraryForAddedProjectIssues(outTokens);
3540 else if(!strcmp(outTokens[0], "=thread-group-created") || !strcmp(outTokens[0], "=thread-group-added") ||
3541 !strcmp(outTokens[0], "=thread-group-started") || !strcmp(outTokens[0], "=thread-group-exited") ||
3542 !strcmp(outTokens[0], "=thread-created") || !strcmp(outTokens[0], "=thread-exited") ||
3543 !strcmp(outTokens[0], "=cmd-param-changed") || !strcmp(outTokens[0], "=library-unloaded") ||
3544 !strcmp(outTokens[0], "=breakpoint-modified"))
3545 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, outTokens[0], outTokens.count>1 ? outTokens[1] : "",
3546 outTokens.count>2 ? outTokens[2] : "", outTokens.count>3 ? outTokens[3] : "",
3547 outTokens.count>4 ? outTokens[4] : "", outTokens.count>5 ? outTokens[5] : "",
3548 outTokens.count>6 ? outTokens[6] : "", outTokens.count>7 ? outTokens[7] : "",
3549 outTokens.count>8 ? outTokens[8] : "", outTokens.count>9 ? outTokens[9] : "");
3551 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "notify-async-output: ", outTokens[0]);
3553 outTokens.RemoveAll();
3557 if(TokenizeList(output, ',', outTokens))
3559 if(!strcmp(outTokens[0],"*running"))
3561 waitingForPID = true;
3562 setWaitingForPID = true;
3564 else if(!strcmp(outTokens[0], "*stopped"))
3567 ChangeState(stopped);
3569 for(tk = 1; tk < outTokens.count; tk++)
3571 if(TokenizeListItem(outTokens[tk], item))
3573 if(!strcmp(item.name, "reason"))
3575 char * reason = item.value;
3576 StripQuotes(reason, reason);
3577 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3580 if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3583 StripQuotes(item2.value, item2.value);
3584 if(!strcmp(item2.name, "exit-code"))
3585 exitCode = item2.value;
3591 HandleExit(reason, exitCode);
3594 else if(!strcmp(reason, "breakpoint-hit"))
3600 stopItem = GdbDataStop { };
3601 stopItem.reason = breakpointHit;
3603 for(i = tk+1; i < outTokens.count; i++)
3605 TokenizeListItem(outTokens[i], item);
3606 StripQuotes(item.value, item.value);
3607 if(!strcmp(item.name, "bkptno"))
3608 stopItem.bkptno = atoi(item.value);
3609 else if(!strcmp(item.name, "thread-id"))
3610 stopItem.threadid = atoi(item.value);
3611 else if(!strcmp(item.name, "frame"))
3613 item.value = StripCurlies(item.value);
3614 ParseFrame(stopItem.frame, item.value);
3616 else if(!strcmp(item.name, "disp") || !strcmp(item.name, "stopped-threads") || !strcmp(item.name, "core"))
3617 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3619 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown breakpoint hit item name (", item.name, "=", item.value, ")");
3624 else if(!strcmp(reason, "end-stepping-range"))
3630 stopItem = GdbDataStop { };
3631 stopItem.reason = endSteppingRange;
3633 for(i = tk+1; i < outTokens.count; i++)
3635 TokenizeListItem(outTokens[i], item);
3636 StripQuotes(item.value, item.value);
3637 if(!strcmp(item.name, "thread-id"))
3638 stopItem.threadid = atoi(item.value);
3639 else if(!strcmp(item.name, "frame"))
3641 item.value = StripCurlies(item.value);
3642 ParseFrame(stopItem.frame, item.value);
3644 else if(!strcmp(item.name, "reason") || !strcmp(item.name, "bkptno"))
3645 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "(", item.name, "=", item.value, ")");
3647 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown end of stepping range item name (", item.name, "=", item.value, ")");
3653 else if(!strcmp(reason, "function-finished"))
3659 stopItem = GdbDataStop { };
3660 stopItem.reason = functionFinished;
3662 for(i = tk+1; i < outTokens.count; i++)
3664 TokenizeListItem(outTokens[i], item);
3665 StripQuotes(item.value, item.value);
3666 if(!strcmp(item.name, "thread-id"))
3667 stopItem.threadid = atoi(item.value);
3668 else if(!strcmp(item.name, "frame"))
3670 item.value = StripCurlies(item.value);
3671 ParseFrame(stopItem.frame, item.value);
3673 else if(!strcmp(item.name, "gdb-result-var"))
3674 stopItem.gdbResultVar = CopyString(item.value);
3675 else if(!strcmp(item.name, "return-value"))
3676 stopItem.returnValue = CopyString(item.value);
3678 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown function finished item name (", item.name, "=", item.value, ")");
3681 event = functionEnd;
3684 else if(!strcmp(reason, "signal-received"))
3690 stopItem = GdbDataStop { };
3691 stopItem.reason = signalReceived;
3693 for(i = tk+1; i < outTokens.count; i++)
3695 TokenizeListItem(outTokens[i], item);
3696 StripQuotes(item.value, item.value);
3697 if(!strcmp(item.name, "signal-name"))
3698 stopItem.name = CopyString(item.value);
3699 else if(!strcmp(item.name, "signal-meaning"))
3700 stopItem.meaning = CopyString(item.value);
3701 else if(!strcmp(item.name, "thread-id"))
3702 stopItem.threadid = atoi(item.value);
3703 else if(!strcmp(item.name, "frame"))
3705 item.value = StripCurlies(item.value);
3706 ParseFrame(stopItem.frame, item.value);
3709 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown signal reveived item name (", item.name, "=", item.value, ")");
3711 if(!strcmp(stopItem.name, "SIGTRAP"))
3730 else if(!strcmp(reason, "watchpoint-trigger"))
3731 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint trigger not handled");
3732 else if(!strcmp(reason, "read-watchpoint-trigger"))
3733 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason read watchpoint trigger not handled");
3734 else if(!strcmp(reason, "access-watchpoint-trigger"))
3735 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason access watchpoint trigger not handled");
3736 else if(!strcmp(reason, "watchpoint-scope"))
3737 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint scope not handled");
3738 else if(!strcmp(reason, "location-reached"))
3739 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason location reached not handled");
3741 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown reason: ", reason);
3749 if(usingValgrind && event == none && !stopItem)
3750 event = valgrindStartPause;
3755 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown exec-async-output: ", outTokens[0]);
3756 outTokens.RemoveAll();
3759 if(!strcmpi(output, "(gdb) "))
3763 char exeFile[MAX_LOCATION];
3764 int oldProcessID = targetProcessId;
3765 GetLastDirectory(targetFile, exeFile);
3767 while(!targetProcessId/*true*/)
3769 targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3770 if(targetProcessId || gdbHandle.Peek()) break;
3775 ChangeState(running);
3776 else if(!oldProcessID)
3778 ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3779 // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3780 gdbHandle.Printf("-gdb-exit\n");
3782 ChangeState(terminated); //loaded;
3787 for(bp : ide.workspace.breakpoints)
3788 bp.inserted = false;
3791 bp.inserted = false;
3793 bpRunToCursor.inserted = false;
3795 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3796 ClearBreakDisplay();
3798 #if defined(__unix__)
3799 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
3801 progThread.terminate = true;
3804 fifoFile.CloseInput();
3811 DeleteFile(progFifoPath);
3812 progFifoPath[0] = '\0';
3819 serialSemaphore.Release();
3822 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown prompt", output);
3826 if(!strncmp(output, "&\"warning:", 10))
3829 content = strstr(output, "\"");
3830 StripQuotes(content, content);
3831 content = strstr(content, ":");
3837 ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3844 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown output: ", output);
3846 if(!setWaitingForPID)
3847 waitingForPID = false;
3848 setWaitingForPID = false;
3856 // From GDB Output functions
3857 void FGODetectLoadedLibraryForAddedProjectIssues(Array<char *> outTokens)
3859 char path[MAX_LOCATION] = "";
3860 char file[MAX_FILENAME] = "";
3862 DebugListItem item { };
3863 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGODetectLoadedLibraryForAddedProjectIssues()");
3864 for(token : outTokens)
3866 if(TokenizeListItem(token, item))
3868 if(!strcmp(item.name, "target-name"))
3870 StripQuotes(item.value, path);
3871 MakeSystemPath(path);
3872 GetLastDirectory(path, file);
3874 else if(!strcmp(item.name, "symbols-loaded"))
3876 symbolsLoaded = (atoi(item.value) == 1);
3881 if(path[0] && file[0])
3883 for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
3887 char prjTargetPath[MAX_LOCATION];
3888 char prjTargetFile[MAX_FILENAME];
3889 DirExpression targetDirExp = prj.GetTargetDir(currentCompiler, prj.config, bitDepth);
3890 strcpy(prjTargetPath, prj.topNode.path);
3891 PathCat(prjTargetPath, targetDirExp.dir);
3892 prjTargetFile[0] = '\0';
3893 prj.CatTargetFileName(prjTargetFile, currentCompiler, prj.config);
3894 PathCat(prjTargetPath, prjTargetFile);
3895 MakeSystemPath(prjTargetPath);
3897 match = !fstrcmp(prjTargetFile, file);
3898 if(!match && (dot = strstr(prjTargetFile, ".so.")))
3900 char * dot3 = strstr(dot+4, ".");
3904 match = !fstrcmp(prjTargetFile, file);
3909 match = !fstrcmp(prjTargetFile, file);
3914 // TODO: nice visual feedback to better warn user. use some ide notification system or other means.
3915 /* -- 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)
3917 ide.outputView.debugBox.Logf($"Attention! No symbols for loaded library %s matched to the %s added project.\n", path, prj.topNode.name);
3919 match = !fstrcmp(prjTargetPath, path);
3920 if(!match && (dot = strstr(prjTargetPath, ".so.")))
3922 char * dot3 = strstr(dot+4, ".");
3926 match = !fstrcmp(prjTargetPath, path);
3931 match = !fstrcmp(prjTargetPath, path);
3935 ide.outputView.debugBox.Logf($"Loaded library %s doesn't match the %s target of the %s added project.\n", path, prjTargetPath, prj.topNode.name);
3942 void FGOBreakpointModified(Array<char *> outTokens)
3944 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGOBreakpointModified() -- TODO only if needed: support breakpoint modified");
3946 DebugListItem item { };
3947 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3949 if(!strcmp(item.name, "bkpt"))
3951 GdbDataBreakpoint modBp = ParseBreakpoint(item.value, outTokens);
3959 ExpressionType ::DebugEvalExpTypeError(char * result)
3961 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::DebugEvalExpTypeError()");
3966 case symbolNotFound:
3967 return symbolErrorExp;
3968 case memoryCantBeRead:
3969 return memoryErrorExp;
3971 return unknownErrorExp;
3974 char * ::EvaluateExpression(char * expression, ExpressionType * error)
3977 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::EvaluateExpression(", expression, ")");
3978 if(ide.projectView && ide.debugger.state == stopped)
3980 result = GdbEvaluateExpression(expression);
3981 *error = DebugEvalExpTypeError(result);
3986 *error = noDebuggerErrorExp;
3991 char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
3994 char * result = GdbReadMemoryString(address, size, format, 1, 1);
3995 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ReadMemory(", address, ")");
3996 if(!result || !strcmp(result, "N/A"))
3997 *error = memoryErrorExp;
3999 *error = DebugEvalExpTypeError(result);
4004 class ValgrindLogThread : Thread
4010 static char output[4096];
4011 Array<char> dynamicBuffer { minAllocSize = 4096 };
4012 File oldValgrindHandle = vgLogFile;
4013 incref oldValgrindHandle;
4016 while(debugger.state != terminated && vgLogFile)
4020 result = vgLogFile.Read(output, 1, sizeof(output));
4022 if(debugger.state == terminated || !vgLogFile/* || vgLogFile.Eof()*/)
4029 for(c = 0; c<result; c++)
4031 if(output[c] == '\n')
4033 int pos = dynamicBuffer.size;
4034 dynamicBuffer.size += c - start;
4035 memcpy(&dynamicBuffer[pos], output + start, c - start);
4036 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4037 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4038 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4039 dynamicBuffer.size++;
4040 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4042 // printf("%s\n", dynamicBuffer.array);
4044 if(strstr(&dynamicBuffer[0], "vgdb me"))
4045 debugger.serialSemaphore.Release();
4046 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4047 dynamicBuffer.size = 0;
4053 int pos = dynamicBuffer.size;
4054 dynamicBuffer.size += c - start;
4055 memcpy(&dynamicBuffer[pos], output + start, c - start);
4058 else if(debugger.state == stopped)
4061 printf("Got end of file from GDB!\n");
4068 delete dynamicBuffer;
4069 ide.outputView.debugBox.Logf($"ValgrindLogThreadExit\n");
4070 //if(oldValgrindHandle == vgLogFile)
4071 debugger.GdbThreadExit/*ValgrindLogThreadExit*/();
4072 delete oldValgrindHandle;
4078 class ValgrindTargetThread : Thread
4084 static char output[4096];
4085 Array<char> dynamicBuffer { minAllocSize = 4096 };
4086 DualPipe oldValgrindHandle = vgTargetHandle;
4087 incref oldValgrindHandle;
4090 while(debugger.state != terminated && vgTargetHandle && !vgTargetHandle.Eof())
4094 result = vgTargetHandle.Read(output, 1, sizeof(output));
4096 if(debugger.state == terminated || !vgTargetHandle || vgTargetHandle.Eof())
4103 for(c = 0; c<result; c++)
4105 if(output[c] == '\n')
4107 int pos = dynamicBuffer.size;
4108 dynamicBuffer.size += c - start;
4109 memcpy(&dynamicBuffer[pos], output + start, c - start);
4110 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4111 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4112 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4113 dynamicBuffer.size++;
4114 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4116 // printf("%s\n", dynamicBuffer.array);
4118 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4120 dynamicBuffer.size = 0;
4126 int pos = dynamicBuffer.size;
4127 dynamicBuffer.size += c - start;
4128 memcpy(&dynamicBuffer[pos], output + start, c - start);
4134 printf("Got end of file from GDB!\n");
4138 delete dynamicBuffer;
4139 //if(oldValgrindHandle == vgTargetHandle)
4140 debugger.ValgrindTargetThreadExit();
4141 delete oldValgrindHandle;
4147 class GdbThread : Thread
4153 static char output[4096];
4154 Array<char> dynamicBuffer { minAllocSize = 4096 };
4155 DualPipe oldGdbHandle = gdbHandle;
4156 incref oldGdbHandle;
4159 while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
4163 result = gdbHandle.Read(output, 1, sizeof(output));
4165 if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
4172 for(c = 0; c<result; c++)
4174 if(output[c] == '\n')
4176 int pos = dynamicBuffer.size;
4177 dynamicBuffer.size += c - start;
4178 memcpy(&dynamicBuffer[pos], output + start, c - start);
4179 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4180 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4181 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4182 dynamicBuffer.size++;
4183 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4185 // _dpl(0, dynamicBuffer.array);
4187 debugger.GdbThreadMain(&dynamicBuffer[0]);
4188 dynamicBuffer.size = 0;
4194 int pos = dynamicBuffer.size;
4195 dynamicBuffer.size += c - start;
4196 memcpy(&dynamicBuffer[pos], output + start, c - start);
4202 _dpl(0, "Got end of file from GDB!");
4206 delete dynamicBuffer;
4207 //if(oldGdbHandle == gdbHandle)
4208 debugger.GdbThreadExit();
4209 delete oldGdbHandle;
4215 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
4216 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
4218 #if defined(__unix__)
4223 #include <sys/types.h>
4228 class ProgramThread : Thread
4234 bool fileCreated = false;
4236 static char output[1000];
4239 /*if(!mkfifo(progFifoPath, mask))
4246 ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
4250 if(FileExists(progFifoPath)) //fileCreated)
4252 fifoFile = FileOpen(progFifoPath, read);
4256 ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
4261 fd = fileno((FILE *)fifoFile.input);
4262 //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
4266 while(!terminate && fifoFile && !fifoFile.Eof())
4269 struct timeval time;
4277 selectResult = select(fd + 1, &rs, null, null, &time);
4278 if(FD_ISSET(fd, &rs))
4280 int result = (int)read(fd, output, sizeof(output)-1);
4281 if(!result || (result < 0 && errno != EAGAIN))
4285 output[result] = '\0';
4286 if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
4289 ide.outputView.debugBox.Log(output);
4298 //fifoFile.CloseInput();
4301 ide.outputView.debugBox.Log("\n");
4305 if(FileExists(progFifoPath)) //fileCreated)
4307 DeleteFile(progFifoPath);
4308 progFifoPath[0] = '\0';
4316 class Argument : struct
4318 Argument prev, next;
4320 property char * name { set { delete name; if(value) name = CopyString(value); } }
4322 property char * val { set { delete val; if(value) val = CopyString(value); } }
4336 class Frame : struct
4341 property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4343 property char * func { set { delete func; if(value) func = CopyString(value); } }
4347 property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
4349 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4350 char * absoluteFile;
4351 property char * absoluteFile { set { delete absoluteFile; if(value) absoluteFile = CopyUnescapedUnixPath(value); } }
4360 delete absoluteFile;
4361 args.Free(Argument::Free);
4370 class GdbDataStop : struct
4372 DebuggerReason reason;
4387 char * gdbResultVar;
4397 if(reason == signalReceived)
4402 else if(reason == functionFinished)
4404 delete gdbResultVar;
4408 if(frame) frame.Free();
4417 class GdbDataBreakpoint : struct
4421 property char * number { set { delete number; if(value) number = CopyString(value); } }
4423 property char * type { set { delete type; if(value) type = CopyString(value); } }
4425 property char * disp { set { delete disp; if(value) disp = CopyString(value); } }
4428 property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4430 property char * func { set { delete func; if(value) func = CopyString(value); } }
4432 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4434 property char * fullname { set { delete fullname; if(value) fullname = CopyUnescapedUnixPath(value); } }
4437 property char * at { set { delete at; if(value) at = CopyString(value); } }
4440 Array<GdbDataBreakpoint> multipleBPs;
4445 PrintLn("{", "#", number, " T", type, " D", disp, " E", enabled, " H", times, " (", func, ") (", file, ":", line, ") (", fullname, ") (", addr, ") (", at, ")", "}");
4456 if(multipleBPs) multipleBPs.Free();
4460 ~GdbDataBreakpoint()
4466 class Breakpoint : struct
4471 property char * function { set { delete function; if(value) function = CopyString(value); } }
4472 char * relativeFilePath;
4473 property char * relativeFilePath { set { delete relativeFilePath; if(value) relativeFilePath = CopyString(value); } }
4474 char * absoluteFilePath;
4475 property char * absoluteFilePath { set { delete absoluteFilePath; if(value) absoluteFilePath = CopyString(value); } }
4484 BreakpointType type;
4486 GdbDataBreakpoint bp;
4488 char * CopyLocationString(bool removePath)
4491 char * file = relativeFilePath ? relativeFilePath : absoluteFilePath;
4492 bool removingPath = removePath && file;
4495 char * fileName = new char[MAX_FILENAME];
4496 GetLastDirectory(file, fileName);
4502 location = PrintString(file, ":", function);
4504 location = CopyString(function);
4507 location = PrintString(file, ":", line);
4513 char * CopyUserLocationString()
4516 char * loc = CopyLocationString(false);
4518 for(p : ide.workspace.projects)
4520 if(p.topNode.FindByFullPath(absoluteFilePath, false))
4528 location = PrintString("(", prj.name, ")", loc);
4538 if(relativeFilePath && relativeFilePath[0])
4540 f.Printf(" * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
4542 f.Printf(" ~ %s\n", condition.expression);
4552 delete relativeFilePath;
4553 delete absoluteFilePath;
4563 class Watch : struct
4574 f.Printf(" ~ %s\n", expression);
4598 class DebugListItem : struct
4604 struct DebugEvaluationData
4609 uint64 nextBlockAddress;
4611 DebuggerEvaluationError error;
4614 class CodeLocation : struct
4617 char * absoluteFile;
4620 CodeLocation ::ParseCodeLocation(char * location)
4624 char * colon = null;
4626 char loc[MAX_LOCATION];
4627 strcpy(loc, location);
4628 for(temp = loc; temp = strstr(temp, ":"); temp++)
4636 int line = atoi(colon);
4639 CodeLocation codloc { line = line };
4640 codloc.file = CopyString(loc);
4641 codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
4653 delete absoluteFile;
4662 void GDBFallBack(Expression exp, String expString)
4665 ExpressionType evalError = dummyExp;
4666 result = Debugger::EvaluateExpression(expString, &evalError);
4669 exp.constant = result;
4670 exp.type = constantExp;