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...
36 public char * StripQuotes2(char * string, char * output)
40 bool quoted = false, escaped = false;
42 for(c = 0; ch = string[c]; c++)
46 if(escaped || ch != '\"')
49 escaped = !escaped && ch == '\\';
64 static void strescpy(char * d, char * s)
117 static char * CopyUnescapedSystemPath(char * p)
119 char * d = new char[strlen(p) + 1];
121 #if defined(__WIN32__)
122 ChangeCh(d, '/', '\\');
127 static char * CopyUnescapedUnixPath(char * p)
129 char * d = new char[strlen(p) + 1];
131 #if defined(__WIN32__)
132 ChangeCh(d, '\\', '/');
137 static char * CopyUnescapedString(char * s)
139 char * d = new char[strlen(s) + 1];
144 // String Unescape Copy
146 // TOFIX: THIS DOESN'T HANDLE NUMERIC ESCAPE CODES (OCTAL/HEXADECIMAL...)?
147 // Seems very similar to ReadString in pass15.ec (which also misses numeric escape codes :) )
149 static void struscpy(char * d, char * s)
201 static char * StripBrackets(char * string)
203 int length = strlen(string);
204 if(length > 1 && *string == '[' && string[length - 1] == ']')
207 string[length - 1] = '\0';
214 static char * StripCurlies(char * string)
216 int length = strlen(string);
217 if(length > 1 && *string == '{' && string[length - 1] == '}')
220 string[length - 1] = '\0';
227 static int StringGetInt(char * string, int start)
230 int i, len = strlen(string);
232 for(i = start; i < len && i < start + 8; i++)
234 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')
235 strncat(number, &string[i], 1);
242 static int TokenizeList(char * string, const char seperator, Array<char *> tokens)
246 bool quoted = false, escaped = false;
247 char * start = string, ch;
249 for(; (ch = *string); string++)
256 if(escaped || ch != '\"')
257 escaped = !escaped && ch == '\\';
263 else if(ch == '{' || ch == '[' || ch == '(' || ch == '<')
265 else if(ch == '}' || ch == ']' || ch == ')' || ch == '>')
267 else if(ch == seperator && !level)
276 //tokens[count] = start;
277 //tokens[count++] = start;
284 static bool TokenizeListItem(char * string, DebugListItem item)
286 char * equal = strstr(string, "=");
300 static void DebuggerProtocolUnknown(char * message, char * gdbOutput)
302 #ifdef _DEBUG_GDB_PROTOCOL
303 ide.outputView.debugBox.Logf("Debugger Protocol Error: %s (%s)\n", message, gdbOutput);
307 // define GdbGetLineSize = 1638400;
308 define GdbGetLineSize = 5638400;
309 #if defined(__unix__)
310 char progFifoPath[MAX_LOCATION];
311 char progFifoDir[MAX_LOCATION];
314 enum DebuggerState { none, prompt, loaded, running, stopped, terminated };
315 enum DebuggerEvent { none, hit, breakEvent, signal, stepEnd, functionEnd, exit };
316 enum DebuggerAction { none, internal, restart, stop, selectFrame }; //, bpValidation
317 enum BreakpointType { none, internalMain, internalWinMain, internalModulesLoaded, user, runToCursor, internalModuleLoad };
318 enum DebuggerEvaluationError { none, symbolNotFound, memoryCantBeRead, unknown };
320 FileDialog debuggerFileDialog { type = selectDir };
322 static DualPipe gdbHandle;
323 static DebugEvaluationData eval { };
325 static int targetProcessId;
327 static bool gdbReady;
328 static bool breakpointError;
332 Semaphore serialSemaphore { };
337 //bool breakpointsInserted;
339 bool sentBreakInsert;
340 bool ignoreBreakpoints;
341 bool userBreakOnInternBreak;
348 int activeFrameLevel;
359 DebuggerAction breakType;
360 //DebuggerCommand lastCommand; // THE COMPILER COMPILES STUFF THAT DOES NOT EXIST???
362 GdbDataStop stopItem;
363 GdbDataBreakpoint bpItem;
366 List<Breakpoint> sysBPs { };
367 Breakpoint bpRunToCursor;
373 CompilerConfig currentCompiler;
374 ProjectConfig prjConfig;
377 CodeEditor codeEditor;
379 GdbThread gdbThread { debugger = this };
382 delay = 0.0, userData = this;
386 bool monitor = false;
387 DebuggerEvent curEvent = event;
388 GdbDataStop stopItem = this.stopItem;
394 this.stopItem = null;
397 if(curEvent && curEvent != exit)
400 printf("No stop item\n");
408 Restart(currentCompiler, prjConfig, bitDepth);
417 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
418 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
419 if(activeFrame.level == activeFrameLevel)
425 // GdbCommand(false, "-break-info %d", bpItem.number);
435 activeThread = stopItem.threadid;
436 GdbCommand(false, "-thread-list-ids");
441 Breakpoint bp = null;
443 for(i : ide.workspace.breakpoints; i.bp && i.bp.number == stopItem.bkptno)
450 for(i : sysBPs; i.bp && i.bp.number == stopItem.bkptno)
456 if(bp && bp.type != user && stopItem && stopItem.frame)
458 // In case the user put a breakpoint where an internal breakpoint is, avoid the confusion...
459 for(i : ide.workspace.breakpoints)
461 if(i.bp && i.line == stopItem.frame.line && !fstrcmp(i.absoluteFilePath, stopItem.frame.absoluteFile))
470 if(!(!userBreakOnInternBreak &&
471 bp && (bp.type == internalMain || bp.type == internalWinMain ||
472 bp.type == internalModulesLoaded || bp.type == internalModuleLoad)))
474 hitThread = stopItem.threadid;
478 signalThread = stopItem.threadid;
490 activeThread = stopItem.threadid;
491 GdbCommand(false, "-thread-list-ids");
493 if(activeFrameLevel > 0)
494 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
496 WatchesCodeEditorLinkInit();
506 ide.outputView.debugBox.Logf($"Signal received: %s - %s\n", stopItem.name, stopItem.meaning);
507 ide.outputView.debugBox.Logf(" %s:%d\n", (s = CopySystemPath(stopItem.frame.file)), stopItem.frame.line);
513 // Why was SelectFrame missing here?
514 SelectFrame(activeFrameLevel);
515 GoToStackFrameLine(activeFrameLevel, curEvent == signal || curEvent == stepEnd /*false*/);
516 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
518 if(curEvent == signal)
519 ide.outputView.Show();
520 if(curEvent == signal || curEvent == breakEvent)
522 if(curEvent == breakEvent)
523 ide.threadsView.Show();
524 ide.callStackView.Show();
526 ide.ShowCodeEditor();
527 if(curEvent == breakEvent)
528 ide.callStackView.Activate();
536 ignoreBreakpoints = false;
547 #ifdef GDB_DEBUG_CONSOLE
548 char lastGdbOutput[GdbGetLineSize];
550 #if defined(__unix__)
551 ProgramThread progThread { };
554 void ChangeState(DebuggerState value)
556 bool same = value == state;
557 // if(same) PrintLn("Debugger::ChangeState -- changing to same state");
559 if(!same && ide) ide.AdjustDebugMenus();
564 // Stop(); // Don't need to call Stop here, because ~ProjectView() will call it explicitly.
566 stackFrames.Free(Frame::Free);
576 waitingForPID = false;
581 sentBreakInsert = false;
582 ignoreBreakpoints = false;
583 userBreakOnInternBreak = false;
586 activeFrameLevel = 0;
603 bpRunToCursor = null;
606 delete currentCompiler;
610 /*GdbThread gdbThread
616 ideProcessId = Process_GetCurrentProcessId();
618 sysBPs.Add(Breakpoint { type = internalMain, enabled = true, level = -1 });
619 #if defined(__WIN32__)
620 sysBPs.Add(Breakpoint { type = internalWinMain, enabled = true, level = -1 });
622 sysBPs.Add(Breakpoint { type = internalModulesLoaded, enabled = true, level = -1 });
623 sysBPs.Add(Breakpoint { type = internalModuleLoad, enabled = true, level = -1 });
635 property bool isActive { get { return state == running || state == stopped; } }
636 property bool isPrepared { get { return state == loaded || state == running || state == stopped; } }
640 GdbExecContinue(true);
648 GdbDebugBreak(false);
660 GdbDebugBreak(false);
674 void Restart(CompilerConfig compiler, ProjectConfig config, int bitDepth)
682 GdbDebugBreak(false);
689 if(!GdbInit(compiler, config, bitDepth))
697 bool GoToCodeLine(char * location)
700 codloc = CodeLocation::ParseCodeLocation(location);
703 CodeEditor editor = (CodeEditor)ide.OpenFile(codloc.absoluteFile, normal, true, null, no, normal, false);
706 EditBox editBox = editor.editBox;
707 editBox.GoToLineNum(codloc.line - 1);
708 editBox.GoToPosition(editBox.line, codloc.line - 1, 0);
715 bool GoToStackFrameLine(int stackLevel, bool askForLocation)
719 char filePath[MAX_LOCATION];
720 char sourceDir[MAX_LOCATION];
722 CodeEditor editor = null;
723 if(stackLevel == -1) // this (the two lines) is part of that fix that I would not put in for some time
725 for(frame = stackFrames.first; frame; frame = frame.next)
726 if(frame.level == stackLevel)
730 ide.callStackView.Show();
732 if(!frame.absoluteFile && frame.file)
733 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
734 if(!frame.absoluteFile && askForLocation && frame.file)
737 char title[MAX_LOCATION];
738 snprintf(title, sizeof(title), $"Provide source file location for %s", (s = CopySystemPath(frame.file)));
739 title[sizeof(title)-1] = 0;
741 if(SourceDirDialog(title, ide.workspace.projectDir, frame.file, sourceDir))
743 AddSourceDir(sourceDir);
744 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
747 if(frame.absoluteFile)
748 editor = (CodeEditor)ide.OpenFile(frame.absoluteFile, normal, true, null, no, normal, false);
750 if(editor && frame.line)
752 EditBox editBox = editor.editBox;
753 editBox.GoToLineNum(frame.line - 1);
754 editBox.GoToPosition(editBox.line, frame.line - 1, 0);
762 void SelectThread(int thread)
766 if(thread != activeThread)
768 activeFrameLevel = -1;
769 ide.callStackView.Clear();
770 GdbCommand(false, "-thread-select %d", thread);
772 // Why was SelectFrame missing here?
773 SelectFrame(activeFrameLevel);
774 GoToStackFrameLine(activeFrameLevel, true);
775 WatchesCodeEditorLinkRelease();
776 WatchesCodeEditorLinkInit();
780 ide.callStackView.Show();
784 void SelectFrame(int frame)
788 if(frame != activeFrameLevel || !codeEditor || !codeEditor.visible)
790 activeFrameLevel = frame; // there is no active frame number in the gdb reply
791 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
792 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
793 if(activeFrame.level == activeFrameLevel)
796 WatchesCodeEditorLinkRelease();
797 WatchesCodeEditorLinkInit();
804 void HandleExit(char * reason, char * code)
806 bool returnedExitCode = false;
807 char verboseExitCode[128];
809 ChangeState(loaded); // this state change seems to be superfluous, might be in case of gdb crash
814 snprintf(verboseExitCode, sizeof(verboseExitCode), $" with exit code %s", code);
815 verboseExitCode[sizeof(verboseExitCode)-1] = 0;
818 verboseExitCode[0] = '\0';
822 // ClearBreakDisplay();
826 for(wh : ide.workspace.watches)
828 if(wh.type) FreeType(wh.type);
831 ide.watchesView.UpdateWatch(wh);
835 #if defined(__unix__)
836 progThread.terminate = true;
839 fifoFile.CloseInput();
848 char program[MAX_LOCATION];
849 GetSystemPathBuffer(program, targetFile);
851 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
852 else if(!strcmp(reason, "exited-normally"))
853 ide.outputView.debugBox.Logf($"The program %s has exited normally%s.\n", program, verboseExitCode);
854 else if(!strcmp(reason, "exited"))
855 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
856 else if(!strcmp(reason, "exited-signalled"))
857 ide.outputView.debugBox.Logf($"The program %s has exited with a signal%s.\n", program, verboseExitCode);
859 ide.outputView.debugBox.Logf($"The program %s has exited (gdb provided an unknown reason)%s.\n", program, verboseExitCode);
864 void Start(CompilerConfig compiler, ProjectConfig config, int bitDepth)
866 ide.outputView.debugBox.Clear();
871 if(!GdbInit(compiler, config, bitDepth))
879 void StepInto(CompilerConfig compiler, ProjectConfig config, int bitDepth)
885 if(!GdbInit(compiler, config, bitDepth))
888 ide.outputView.ShowClearSelectTab(debug);
889 ide.outputView.debugBox.Logf($"Starting debug mode\n");
890 userBreakOnInternBreak = true;
899 void StepOver(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool ignoreBkpts)
905 if(!GdbInit(compiler, config, bitDepth))
908 ide.outputView.ShowClearSelectTab(debug);
909 ide.outputView.debugBox.Logf($"Starting debug mode\n");
910 ignoreBreakpoints = ignoreBkpts;
911 userBreakOnInternBreak = true;
915 ignoreBreakpoints = ignoreBkpts;
916 if(ignoreBreakpoints)
917 GdbBreakpointsDelete(true);
923 void StepOut(bool ignoreBkpts)
927 ignoreBreakpoints = ignoreBkpts;
928 if(ignoreBreakpoints)
929 GdbBreakpointsDelete(true);
934 void RunToCursor(CompilerConfig compiler, ProjectConfig config, int bitDepth, char * absoluteFilePath, int lineNumber, bool ignoreBkpts, bool atSameLevel)
936 char relativeFilePath[MAX_LOCATION];
937 DebuggerState oldState = state;
938 ignoreBreakpoints = ignoreBkpts;
939 if(!ide.projectView.project.GetRelativePath(absoluteFilePath, relativeFilePath))
940 strcpy(relativeFilePath, absoluteFilePath);
945 Start(compiler, config, bitDepth);
952 ide.outputView.ShowClearSelectTab(debug);
953 ide.outputView.debugBox.Logf($"Starting debug mode\n");
955 RunToCursorPrepare(absoluteFilePath, relativeFilePath, lineNumber, atSameLevel);
956 sentBreakInsert = true;
957 GdbCommand(false, "-break-insert %s:%d", relativeFilePath, lineNumber);
958 bpRunToCursor.bp = bpItem;
960 bpRunToCursor.inserted = (bpRunToCursor.bp.number != 0);
961 ValidateBreakpoint(bpRunToCursor);
968 if(ignoreBreakpoints)
969 GdbBreakpointsDelete(false);
973 if(ignoreBreakpoints)
974 GdbBreakpointsDelete(false);
975 GdbExecContinue(true);
981 void GetCallStackCursorLine(bool * error, int * lineCursor, int * lineTopFrame)
983 if(activeFrameLevel == -1)
991 *error = signalOn && activeThread == signalThread;
992 *lineCursor = activeFrameLevel - ((frameCount > 192 && activeFrameLevel > 191) ? frameCount - 192 - 1 : 0) + 1;
993 *lineTopFrame = activeFrameLevel ? 1 : 0;
997 int GetMarginIconsLineNumbers(char * fileName, int lines[], bool enabled[], int max, bool * error, int * lineCursor, int * lineTopFrame)
999 char winFilePath[MAX_LOCATION];
1000 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1002 Iterator<Breakpoint> it { ide.workspace.breakpoints };
1003 while(it.Next() && count < max)
1005 Breakpoint bp = it.data;
1008 if(bp.absoluteFilePath && bp.absoluteFilePath[0] && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1010 lines[count] = bp.line;
1011 enabled[count] = bp.enabled;
1016 if(activeFrameLevel == -1)
1024 *error = signalOn && activeThread == signalThread;
1025 if(activeFrame && activeFrame.absoluteFile && !fstrcmp(absoluteFilePath, activeFrame.absoluteFile))
1026 *lineCursor = activeFrame.line;
1029 if(activeFrame && stopItem && stopItem.frame && activeFrame.level == stopItem.frame.level)
1031 else if(stopItem && stopItem.frame && stopItem.frame.absoluteFile && !fstrcmp(absoluteFilePath, stopItem.frame.absoluteFile))
1032 *lineTopFrame = stopItem.frame.line;
1036 if(*lineTopFrame == *lineCursor && *lineTopFrame)
1042 void ChangeWatch(DataRow row, char * expression)
1044 Watch wh = (Watch)row.tag;
1047 delete wh.expression;
1049 wh.expression = CopyString(expression);
1052 Iterator<Watch> it { ide.workspace.watches };
1054 ide.workspace.watches.Delete(it.pointer);
1060 row.tag = (int64)wh;
1061 ide.workspace.watches.Add(wh);
1063 wh.expression = CopyString(expression);
1065 ide.workspace.Save();
1066 //if(expression && state == stopped)
1071 void MoveIcons(char * fileName, int lineNumber, int move, bool start)
1073 char winFilePath[MAX_LOCATION];
1074 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1077 for(bpLink = ide.workspace.breakpoints.first; bpLink; bpLink = next)
1079 Breakpoint bp = (Breakpoint)bpLink.data;
1082 if(bp.type == user && bp.absoluteFilePath && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1084 if(bp.line > lineNumber || (bp.line == lineNumber && start))
1086 if(move < 0 && (bp.line < lineNumber - move))
1087 ide.workspace.RemoveBreakpoint(bp);
1091 ide.breakpointsView.UpdateBreakpoint(bp.row);
1092 ide.workspace.Save();
1098 // moving code cursors is futile, on next step, stop, hit, cursors will be offset anyways
1101 bool SourceDirDialog(char * title, char * startDir, char * test, char * sourceDir)
1105 String srcDir = null;
1107 debuggerFileDialog.text = title;
1108 debuggerFileDialog.currentDirectory = startDir;
1109 debuggerFileDialog.master = ide;
1111 while(debuggerFileDialog.Modal())
1113 strcpy(sourceDir, debuggerFileDialog.filePath);
1114 if(!fstrcmp(ide.workspace.projectDir, sourceDir) &&
1115 MessageBox { type = yesNo, master = ide,
1116 contents = $"This is the project directory.\nWould you like to try again?",
1117 text = $"Invalid Source Directory" }.Modal() == no)
1121 for(dir : ide.workspace.sourceDirs)
1123 if(!fstrcmp(dir, sourceDir))
1131 MessageBox { type = yesNo, master = ide,
1132 contents = $"This source directory is already specified.\nWould you like to try again?",
1133 text = $"Invalid Source Directory" }.Modal() == no)
1139 char file[MAX_LOCATION];
1140 strcpy(file, sourceDir);
1141 PathCat(file, test);
1142 result = FileExists(file);
1144 MessageBox { type = yesNo, master = ide,
1145 contents = $"Unable to locate source file.\nWould you like to try again?",
1146 text = $"Invalid Source Directory" }.Modal() == no)
1160 void AddSourceDir(char * sourceDir)
1162 ide.workspace.sourceDirs.Add(CopyString(sourceDir));
1163 ide.workspace.Save();
1167 DebuggerState oldState = state;
1172 GdbDebugBreak(true);
1175 GdbCommand(false, "-environment-directory \"%s\"", sourceDir);
1178 if(oldState == running)
1179 GdbExecContinue(false);
1183 void ToggleBreakpoint(char * fileName, int lineNumber, Project prj)
1185 char winFilePath[MAX_LOCATION];
1186 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1187 char absolutePath[MAX_LOCATION];
1188 char relativePath[MAX_LOCATION];
1189 char sourceDir[MAX_LOCATION];
1190 Breakpoint bp = null;
1192 strcpy(absolutePath, absoluteFilePath);
1193 for(i : ide.workspace.breakpoints; i.type == user && i.absoluteFilePath && !fstrcmp(i.absoluteFilePath, absolutePath) && i.line == lineNumber)
1202 ide.workspace.RemoveBreakpoint(bp);
1210 // FIXED: This is how it should have been... Source locations are only for files not in project
1211 // if(IsPathInsideOf(absolutePath, ide.workspace.projectDir))
1212 // MakePathRelative(absolutePath, ide.workspace.projectDir, relativePath);
1213 bool result = false;
1215 result = prj.GetRelativePath(absolutePath, relativePath);
1217 result = ide.projectView.project.GetRelativePath(absolutePath, relativePath);
1218 //if(ide.projectView.project.GetRelativePath(absolutePath, relativePath));
1222 char title[MAX_LOCATION];
1223 char directory[MAX_LOCATION];
1224 StripLastDirectory(absolutePath, directory);
1225 snprintf(title, sizeof(title), $"Provide source files location directory for %s", absolutePath);
1226 title[sizeof(title)-1] = 0;
1229 String srcDir = null;
1230 for(dir : ide.workspace.sourceDirs)
1232 if(IsPathInsideOf(absolutePath, dir))
1234 MakePathRelative(absoluteFilePath, dir, relativePath);
1242 if(SourceDirDialog(title, directory, null, sourceDir))
1244 if(IsPathInsideOf(absolutePath, sourceDir))
1246 AddSourceDir(sourceDir);
1247 MakePathRelative(absoluteFilePath, sourceDir, relativePath);
1250 else if(MessageBox { type = yesNo, master = ide,
1251 contents = $"You must provide a valid source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1252 text = $"Invalid Source Directory" }.Modal() == no)
1255 else if(MessageBox { type = yesNo, master = ide,
1256 contents = $"You must provide a source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1257 text = $"No Source Directory Provided" }.Modal() == no)
1261 ide.workspace.bpCount++;
1262 bp = { line = lineNumber, type = user, enabled = true, level = -1 };
1263 ide.workspace.breakpoints.Add(bp);
1264 bp.absoluteFilePath = CopyString(absolutePath);
1265 bp.relativeFilePath = CopyString(relativePath);
1266 ide.breakpointsView.AddBreakpoint(bp);
1271 DebuggerState oldState = state;
1276 GdbDebugBreak(true);
1281 sentBreakInsert = true;
1282 GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, bp.line);
1285 bp.inserted = (bp.bp && bp.bp.number != 0);
1286 ValidateBreakpoint(bp);
1290 if(oldState == running)
1291 GdbExecContinue(false);
1294 ide.workspace.Save();
1297 void UpdateRemovedBreakpoint(Breakpoint bp)
1299 if(targeted && bp.inserted)
1301 DebuggerState oldState = state;
1306 GdbDebugBreak(true);
1310 GdbCommand(false, "-break-delete %d", bp.bp.number);
1313 if(oldState == running)
1314 GdbExecContinue(false);
1320 void ParseFrame(Frame frame, char * string)
1323 Array<char *> frameTokens { minAllocSize = 50 };
1324 Array<char *> argsTokens { minAllocSize = 50 };
1325 Array<char *> argumentTokens { minAllocSize = 50 };
1326 DebugListItem item { };
1329 TokenizeList(string, ',', frameTokens);
1330 for(i = 0; i < frameTokens.count; i++)
1332 if(TokenizeListItem(frameTokens[i], item))
1334 StripQuotes(item.value, item.value);
1335 if(!strcmp(item.name, "level"))
1336 frame.level = atoi(item.value);
1337 else if(!strcmp(item.name, "addr"))
1338 frame.addr = CopyString(item.value);
1339 else if(!strcmp(item.name, "func"))
1340 frame.func = CopyString(item.value);
1341 else if(!strcmp(item.name, "args"))
1343 if(!strcmp(item.value, "[]"))
1344 frame.argsCount = 0;
1347 item.value = StripBrackets(item.value);
1348 TokenizeList(item.value, ',', argsTokens);
1349 for(j = 0; j < argsTokens.count; j++)
1351 argsTokens[j] = StripCurlies(argsTokens[j]);
1352 TokenizeList(argsTokens[j], ',', argumentTokens);
1353 for(k = 0; k < argumentTokens.count; k++)
1356 frame.args.Add(arg);
1357 if(TokenizeListItem(argumentTokens[k], item))
1359 if(!strcmp(item.name, "name"))
1361 StripQuotes(item.value, item.value);
1362 arg.name = CopyString(item.value);
1364 else if(!strcmp(item.name, "value"))
1366 StripQuotes(item.value, item.value);
1367 arg.value = CopyString(item.value);
1370 DebuggerProtocolUnknown("Unknown frame args item name", item.name);
1373 DebuggerProtocolUnknown("Bad frame args item", "");
1375 argumentTokens.RemoveAll();
1377 frame.argsCount = argsTokens.count;
1378 argsTokens.RemoveAll();
1381 else if(!strcmp(item.name, "from"))
1382 frame.from = item.value;
1383 else if(!strcmp(item.name, "file"))
1385 frame.file = item.value;
1386 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1388 else if(!strcmp(item.name, "line"))
1389 frame.line = atoi(item.value);
1390 else if(!strcmp(item.name, "fullname"))
1392 // GDB 6.3 on OS X is giving "fullname" and "dir", all in absolute, but file name only in 'file'
1393 String path = ide.workspace.GetPathWorkspaceRelativeOrAbsolute(item.value);
1394 if(strcmp(frame.file, path))
1397 delete frame.absoluteFile;
1398 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1403 DebuggerProtocolUnknown("Unknown frame member name", item.name);
1406 DebuggerProtocolUnknown("Bad frame", "");
1411 delete argumentTokens;
1415 void ShowDebuggerViews()
1417 ide.outputView.Show();
1418 ide.outputView.SelectTab(debug);
1419 ide.threadsView.Show();
1420 ide.callStackView.Show();
1421 ide.watchesView.Show();
1425 void HideDebuggerViews()
1427 ide.RepositionWindows(true);
1430 void ::GdbCommand(bool focus, char * format, ...)
1434 // TODO: Improve this limit
1435 static char string[MAX_F_STRING*4];
1437 va_start(args, format);
1438 vsnprintf(string, sizeof(string), format, args);
1439 string[sizeof(string)-1] = 0;
1443 ide.debugger.serialSemaphore.TryWait();
1446 #ifdef GDB_DEBUG_CONSOLE
1447 Log(string); Log("\n");
1449 #ifdef GDB_DEBUG_OUTPUT
1450 ide.outputView.gdbBox.Logf("cmd: %s\n", string);
1452 #ifdef GDB_DEBUG_GUI
1454 ide.gdbDialog.AddCommand(string);
1456 strcat(string,"\n");
1457 gdbHandle.Puts(string);
1460 Process_ShowWindows(targetProcessId);
1463 ide.debugger.serialSemaphore.Wait();
1468 bool ValidateBreakpoint(Breakpoint bp)
1470 if(modules && bp.bp)
1472 if(bp.bp.line != bp.line)
1477 ide.outputView.debugBox.Logf("WOULD HAVE -- Invalid breakpoint disabled: %s:%d\n", bp.relativeFilePath, bp.line);
1481 //GdbCommand(false, "-break-delete %d", bp.bp.number);
1482 //bp.inserted = false;
1484 //bp.enabled = false;
1489 ide.outputView.debugBox.Logf("Debugger Error: ValidateBreakpoint error\n");
1490 bp.line = bp.bp.line;
1497 static void GdbInsertInternalBreakpoints()
1501 //if(!breakpointsInserted)
1503 DirExpression objDir = ide.project.GetObjDir(currentCompiler, prjConfig, bitDepth);
1508 if(bp.type == internalMain)
1510 sentBreakInsert = true;
1511 GdbCommand(false, "-break-insert main");
1514 bp.inserted = (bp.bp && bp.bp.number != 0);
1516 #if defined(__WIN32__)
1517 else if(bp.type == internalWinMain)
1519 sentBreakInsert = true;
1520 GdbCommand(false, "-break-insert WinMain");
1523 bp.inserted = (bp.bp && bp.bp.number != 0);
1526 else if(bp.type == internalModulesLoaded)
1528 char path[MAX_LOCATION];
1529 char name[MAX_LOCATION];
1530 char fixedModuleName[MAX_FILENAME];
1533 bool moduleLoadBlock = false;
1535 ReplaceSpaces(fixedModuleName, ide.project.moduleName);
1536 snprintf(name, sizeof(name),"%s.main.ec", fixedModuleName);
1537 name[sizeof(name)-1] = 0;
1538 strcpy(path, ide.workspace.projectDir);
1539 PathCatSlash(path, objDir.dir);
1540 PathCatSlash(path, name);
1541 f = FileOpen(path, read);
1544 for(lineNumber = 1; !f.Eof(); lineNumber++)
1546 if(f.GetLine(line, sizeof(line) - 1))
1548 bool moduleLoadLine;
1549 TrimLSpaces(line, line);
1550 moduleLoadLine = !strncmp(line, "eModule_Load", strlen("eModule_Load"));
1551 if(!moduleLoadBlock && moduleLoadLine)
1552 moduleLoadBlock = true;
1553 else if(moduleLoadBlock && !moduleLoadLine && strlen(line) > 0)
1559 char relative[MAX_LOCATION];
1560 bp.absoluteFilePath = CopyString(path);
1561 MakePathRelative(path, ide.workspace.projectDir, relative);
1562 delete bp.relativeFilePath;
1563 bp.relativeFilePath = CopyString(relative);
1564 bp.line = lineNumber;
1565 sentBreakInsert = true;
1566 GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, lineNumber);
1569 bp.inserted = (bp.bp && bp.bp.number != 0);
1574 else if(bp.type == internalModuleLoad && modules)
1576 Project ecerePrj = null;
1577 for(p : ide.workspace.projects)
1579 if(!strcmp(p.topNode.name, "ecere.epj"))
1587 ProjectNode node = ecerePrj.topNode.Find("instance.c", false);
1590 char path[MAX_LOCATION];
1591 char relative[MAX_LOCATION];
1592 node.GetFullFilePath(path);
1593 bp.absoluteFilePath = CopyString(path);
1594 MakePathRelative(path, ecerePrj.topNode.path, relative);
1595 delete bp.relativeFilePath;
1596 bp.relativeFilePath = CopyString(relative);
1597 sentBreakInsert = true;
1598 GdbCommand(false, "-break-insert %s:InternalModuleLoadBreakpoint", bp.relativeFilePath);
1601 bp.inserted = (bp.bp && bp.bp.number != 0);
1612 void GdbBreakpointsInsert()
1616 //if(!breakpointsInserted)
1618 //if(!ignoreBreakpoints)
1619 //breakpointsInserted = true;
1620 for(bp : ide.workspace.breakpoints)
1622 if(!bp.inserted && bp.type == user)
1624 if(!ignoreBreakpoints && bp.enabled)
1626 sentBreakInsert = true;
1627 breakpointError = false;
1628 GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, bp.line);
1629 // Improve, GdbCommand should return a success value?
1632 char fileName[MAX_FILENAME];
1633 breakpointError = false;
1634 GetLastDirectory(bp.relativeFilePath, fileName);
1635 sentBreakInsert = true;
1636 GdbCommand(false, "-break-insert %s:%d", fileName, bp.line);
1640 bp.inserted = (bp.bp && bp.bp.number != 0);
1643 ValidateBreakpoint(bp);
1649 printf("problem\n");
1651 bp.bp = GdbDataBreakpoint { };
1655 if(bpRunToCursor && !bpRunToCursor.inserted)
1657 sentBreakInsert = true;
1658 GdbCommand(false, "-break-insert %s:%d", bpRunToCursor.relativeFilePath, bpRunToCursor.line);
1659 bpRunToCursor.bp = bpItem;
1661 bpRunToCursor.inserted = (bpRunToCursor.bp && bpRunToCursor.bp.number != 0);
1662 ValidateBreakpoint(bpRunToCursor);
1668 void GdbBreakpointsDelete(bool deleteRunToCursor)
1670 //breakpointsInserted = false;
1676 GdbCommand(false, "-break-delete %d", bp.bp.number);
1677 bp.inserted = false;
1679 //check here (reply form -break-delete, returns bpitem?)
1682 for(bp : ide.workspace.breakpoints)
1685 GdbCommand(false, "-break-delete %d", bp.bp.number);
1686 bp.inserted = false;
1688 //check here (reply form -break-delete, returns bpitem?)
1691 if(deleteRunToCursor && bpRunToCursor)
1693 GdbCommand(false, "-break-delete %d", bpRunToCursor.bp.number);
1694 bpRunToCursor.inserted = false;
1695 bpRunToCursor.bp = bpItem;
1696 //check here (reply form -break-delete, returns bpitem?)
1705 stackFrames.Free(Frame::Free);
1706 GdbCommand(false, "-stack-info-depth");
1708 GdbCommand(false, "-stack-info-depth 192");
1709 if(frameCount && frameCount <= 192)
1710 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 191));
1713 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 95));
1714 GdbCommand(false, "-stack-list-frames %d %d", Max(frameCount - 96, 96), frameCount - 1);
1716 GdbCommand(false, "");
1723 char escaped[MAX_LOCATION];
1724 strescpy(escaped, targetFile);
1725 GdbCommand(false, "file \"%s\"", escaped); //GDB/MI Missing Implementation -symbol-file, -target-attach
1730 for(prj : ide.workspace.projects)
1732 if(prj == ide.workspace.projects.firstIterator.data)
1734 GdbCommand(false, "-environment-directory \"%s\"", prj.topNode.path);
1737 for(dir : ide.workspace.sourceDirs)
1739 GdbCommand(false, "-environment-directory \"%s\"", dir);
1741 GdbInsertInternalBreakpoints();
1747 void GdbTargetRelease()
1751 GdbBreakpointsDelete(true);
1752 GdbCommand(false, "file"); //GDB/MI Missing Implementation -target-detach
1758 void GdbDebugBreak(bool internal)
1763 breakType = DebuggerAction::internal;
1765 if(ide) ide.Update(null);
1767 if(Process_Break(targetProcessId)) //GdbCommand(false, "-exec-interrupt");
1768 serialSemaphore.Wait();
1771 ChangeState(loaded);
1772 targetProcessId = 0;
1777 ide.outputView.debugBox.Logf("Debugger Error: GdbDebugBreak with not target id should never happen\n");
1784 ShowDebuggerViews();
1785 GdbCommand(true, "-exec-run");
1788 void GdbExecContinue(bool focus)
1791 GdbCommand(focus, "-exec-continue");
1797 GdbCommand(true, "-exec-next");
1803 GdbCommand(true, "-exec-step");
1806 void GdbExecFinish()
1809 GdbCommand(true, "-exec-finish");
1812 void GdbExecCommon()
1814 ClearBreakDisplay();
1815 GdbInsertInternalBreakpoints();
1816 GdbBreakpointsInsert();
1819 #ifdef GDB_DEBUG_GUI
1820 void SendGDBCommand(char * command)
1822 DebuggerState oldState = state;
1827 GdbDebugBreak(true);
1830 GdbCommand(false, command);
1833 if(oldState == running)
1834 GdbExecContinue(false);
1838 void ClearBreakDisplay()
1841 activeFrameLevel = -1;
1852 stackFrames.Free(Frame::Free);
1853 WatchesCodeEditorLinkRelease();
1854 ide.callStackView.Clear();
1855 ide.threadsView.Clear();
1862 GdbCommand(false, "-interpreter-exec console \"kill\""); // should use -exec-abort -- GDB/MI implementation incomplete
1866 bool GdbInit(CompilerConfig compiler, ProjectConfig config, int bitDepth)
1869 char oldDirectory[MAX_LOCATION];
1870 char tempPath[MAX_LOCATION];
1871 char command[MAX_LOCATION];
1872 Project project = ide.project;
1873 DirExpression targetDirExp = project.GetTargetDir(compiler, config, bitDepth);
1874 PathBackup pathBackup { };
1876 if(currentCompiler != compiler)
1878 delete currentCompiler;
1879 currentCompiler = compiler;
1880 incref currentCompiler;
1883 this.bitDepth = bitDepth;
1885 ChangeState(loaded);
1887 sentBreakInsert = false;
1888 breakpointError = false;
1892 //breakpointsInserted = false;
1894 ide.outputView.ShowClearSelectTab(debug);
1895 ide.outputView.debugBox.Logf($"Starting debug mode\n");
1897 #ifdef GDB_DEBUG_CONSOLE
1898 Log("Starting GDB"); Log("\n");
1900 #ifdef GDB_DEBUG_OUTPUT
1901 ide.outputView.gdbBox.Logf("run: Starting GDB\n");
1904 strcpy(tempPath, ide.workspace.projectDir);
1905 PathCatSlash(tempPath, targetDirExp.dir);
1907 targetDir = CopyString(tempPath);
1908 project.CatTargetFileName(tempPath, compiler, config);
1910 targetFile = CopyString(tempPath);
1912 GetWorkingDir(oldDirectory, MAX_LOCATION);
1913 if(ide.workspace.debugDir && ide.workspace.debugDir[0])
1915 char temp[MAX_LOCATION];
1916 strcpy(temp, ide.workspace.projectDir);
1917 PathCatSlash(temp, ide.workspace.debugDir);
1918 ChangeWorkingDir(temp);
1921 ChangeWorkingDir(ide.workspace.projectDir);
1923 ide.SetPath(true, compiler, config, bitDepth);
1925 // TODO: This pollutes the environment, but at least it works
1926 // It shouldn't really affect the IDE as the PATH gets restored and other variables set for testing will unlikely cause problems
1927 // What is the proper solution for this? DualPipeOpenEnv?
1928 // gdb set environment commands don't seem to take effect
1929 for(e : ide.workspace.environmentVars)
1931 SetEnvironment(e.name, e.string);
1935 (compiler.targetPlatform == win32 && bitDepth == 64) ? "x86_64-w64-mingw32-gdb" :
1936 (compiler.targetPlatform == win32 && bitDepth == 32) ? "i686-w64-mingw32-gdb" :
1938 strcat(command, " -n -silent --interpreter=mi2"); //-async //\"%s\"
1940 gdbHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
1943 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start GDB\n");
1951 gdbProcessId = gdbHandle.GetProcessID();
1954 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get GDB process ID\n");
1960 serialSemaphore.Wait();
1965 //ChangeState(terminated);
1971 #if defined(__unix__)
1973 CreateTemporaryDir(progFifoDir, "ecereide");
1974 strcpy(progFifoPath, progFifoDir);
1975 PathCat(progFifoPath, "ideprogfifo");
1976 if(!mkfifo(progFifoPath, 0600))
1978 //fileCreated = true;
1983 ide.outputView.debugBox.Logf(createFIFOMsg, progFifoPath);
1988 progThread.terminate = false;
1989 progThread.Create();
1992 #if defined(__WIN32__)
1993 GdbCommand(false, "-gdb-set new-console on");
1996 GdbCommand(false, "-gdb-set verbose off");
1997 //GdbCommand(false, "-gdb-set exec-done-display on");
1998 GdbCommand(false, "-gdb-set step-mode off");
1999 GdbCommand(false, "-gdb-set unwindonsignal on");
2000 //GdbCommand(false, "-gdb-set shell on");
2001 GdbCommand(false, "set print elements 992");
2002 GdbCommand(false, "-gdb-set backtrace limit 100000");
2004 #if defined(__unix__)
2005 GdbCommand(false, "-inferior-tty-set %s", progFifoPath);
2008 GdbCommand(false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
2010 for(e : ide.workspace.environmentVars)
2012 GdbCommand(false, "set environment %s=%s", e.name, e.string);
2019 ChangeWorkingDir(oldDirectory);
2025 delete targetDirExp;
2031 if(gdbHandle && gdbProcessId)
2033 GdbCommand(false, "-gdb-exit");
2048 ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
2052 for(bp : ide.workspace.breakpoints)
2053 bp.inserted = false;
2055 bp.inserted = false;
2057 bpRunToCursor.inserted = false;
2059 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2060 ClearBreakDisplay();
2063 #if defined(__unix__)
2064 if(FileExists(progFifoPath)) //fileCreated)
2066 progThread.terminate = true;
2069 fifoFile.CloseInput();
2075 DeleteFile(progFifoPath);
2076 progFifoPath[0] = '\0';
2082 void WatchesCodeEditorLinkInit()
2085 char tempPath[MAX_LOCATION];
2086 char path[MAX_LOCATION];
2088 //void MakeFilePathProjectRelative(char * path, char * relativePath)
2089 if(!ide.projectView.project.GetRelativePath(activeFrame.file, tempPath))
2090 strcpy(tempPath, activeFrame.file);
2092 strcpy(path, ide.workspace.projectDir);
2093 PathCat(path, tempPath);
2094 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2097 for(srcDir : ide.workspace.sourceDirs)
2099 strcpy(path, srcDir);
2100 PathCat(path, tempPath);
2101 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2102 if(codeEditor) break;
2107 if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2108 activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);
2109 if(!activeFrame || !activeFrame.absoluteFile)
2112 codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal, false);
2115 codeEditor.inUseDebug = true;
2118 //watchesInit = true;
2121 void WatchesCodeEditorLinkRelease()
2127 codeEditor.inUseDebug = false;
2128 if(!codeEditor.visible)
2129 codeEditor.Destroy(0);
2135 bool ResolveWatch(Watch wh)
2137 bool result = false;
2150 char watchmsg[MAX_F_STRING];
2151 if(state == stopped && !codeEditor)
2152 wh.value = CopyString($"No source file found for selected frame");
2153 //if(codeEditor && state == stopped || state != stopped)
2156 Module backupPrivateModule;
2157 Context backupContext;
2158 Class backupThisClass;
2162 backupPrivateModule = GetPrivateModule();
2163 backupContext = GetCurrentContext();
2164 backupThisClass = GetThisClass();
2167 SetPrivateModule(codeEditor.privateModule);
2168 SetCurrentContext(codeEditor.globalContext);
2169 SetTopContext(codeEditor.globalContext);
2170 SetGlobalContext(codeEditor.globalContext);
2171 SetGlobalData(&codeEditor.globalData);
2174 exp = ParseExpressionString(wh.expression);
2176 if(exp && !parseError)
2178 char expString[4096];
2180 PrintExpression(exp, expString);
2182 if(GetPrivateModule())
2185 DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2186 ProcessExpressionType(exp);
2188 wh.type = exp.expType;
2191 DebugComputeExpression(exp);
2192 if(ExpressionIsError(exp))
2194 GDBFallBack(exp, expString);
2197 /*if(exp.hasAddress)
2199 char temp[MAX_F_STRING];
2200 sprintf(temp, "0x%x", exp.address);
2201 wh.address = CopyString(temp);
2202 // wh.address = CopyStringf("0x%x", exp.address);
2207 Type dataType = exp.expType;
2210 char temp[MAX_F_STRING];
2211 switch(dataType.kind)
2214 sprintf(temp, "%i", exp.val.c);
2217 sprintf(temp, "%i", exp.val.s);
2222 sprintf(temp, "%i", exp.val.i);
2225 sprintf(temp, "%i", exp.val.i64);
2228 sprintf(temp, "%i", exp.val.p);
2233 long v = (long)exp.val.f;
2234 sprintf(temp, "%i", v);
2239 long v = (long)exp.val.d;
2240 sprintf(temp, "%i", v);
2245 wh.intVal = CopyString(temp);
2246 switch(dataType.kind)
2249 sprintf(temp, "0x%x", exp.val.c);
2252 sprintf(temp, "0x%x", exp.val.s);
2256 sprintf(temp, "0x%x", exp.val.i);
2259 sprintf(temp, "0x%x", exp.val.i64);
2262 sprintf(temp, "0x%x", exp.val.i64);
2265 sprintf(temp, "0x%x", exp.val.p);
2270 long v = (long)exp.val.f;
2271 sprintf(temp, "0x%x", v);
2276 long v = (long)exp.val.d;
2277 sprintf(temp, "0x%x", v);
2282 wh.hexVal = CopyString(temp);
2283 switch(dataType.kind)
2286 sprintf(temp, "0o%o", exp.val.c);
2289 sprintf(temp, "0o%o", exp.val.s);
2293 sprintf(temp, "0o%o", exp.val.i);
2296 sprintf(temp, "0o%o", exp.val.i64);
2299 sprintf(temp, "0o%o", exp.val.i64);
2302 sprintf(temp, "0o%o", exp.val.p);
2307 long v = (long)exp.val.f;
2308 sprintf(temp, "0o%o", v);
2313 long v = (long)exp.val.d;
2314 sprintf(temp, "0o%o", v);
2319 wh.octVal = CopyString(temp);
2322 // WHATS THIS HERE ?
2323 if(exp.type == constantExp && exp.constant)
2324 wh.constant = CopyString(exp.constant);
2330 case symbolErrorExp:
2331 snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2333 case structMemberSymbolErrorExp:
2334 // todo get info as in next case (ExpClassMemberSymbolError)
2335 snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2337 case classMemberSymbolErrorExp:
2340 Expression memberExp = exp.member.exp;
2341 Identifier memberID = exp.member.member;
2342 Type type = memberExp.expType;
2345 _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2348 char string[256] = "";
2350 PrintTypeNoConst(type, string, false, true);
2351 classSym = FindClass(string);
2352 _class = classSym ? classSym.registered : null;
2355 snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2357 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2360 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2363 case memoryErrorExp:
2364 // Need to ensure when set to memoryErrorExp, constant is set
2365 snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2367 case dereferenceErrorExp:
2368 snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2370 case unknownErrorExp:
2371 snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2373 case noDebuggerErrorExp:
2374 snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2376 case debugStateErrorExp:
2377 snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2380 snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2384 // Temporary Code for displaying Strings
2385 if((exp.expType && ((exp.expType.kind == pointerType ||
2386 exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) ||
2387 (wh.type && wh.type.kind == classType && wh.type._class &&
2388 wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2389 !strcmp(wh.type._class.registered.name, "String")))
2392 if(exp.expType.kind != arrayType || exp.hasAddress)
2398 //char temp[MAX_F_STRING * 32];
2400 ExpressionType evalError = dummyExp;
2401 /*if(exp.expType.kind == arrayType)
2402 sprintf(temp, "(char*)0x%x", exp.address);
2404 sprintf(temp, "(char*)%s", exp.constant);*/
2406 //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2407 // address = strtoul(exp.constant, null, 0);
2408 address = _strtoui64(exp.constant, null, 0);
2409 //printf("%x\n", address);
2410 // snprintf(value, sizeof(value), "0x%08x ", address);
2412 if(address > 0xFFFFFFFFLL)
2413 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2415 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2416 value[sizeof(value)-1] = 0;
2419 strcat(value, $"Null string");
2423 len = strlen(value);
2425 while(!string && size > 2)
2427 string = GdbReadMemory(address, size);
2430 if(string && string[0])
2433 if(UTF8Validate(string))
2438 for(c = 0; (ch = string[c]) && c<4096; c++)
2441 value[len++] = '\0';
2446 ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2447 strcat(value, ") (ISO8859-1)");
2454 strcat(value, $"Empty string");
2458 strcat(value, $"Couldn't read memory");
2460 wh.value = CopyString(value);
2463 else if(wh.type && wh.type.kind == classType && wh.type._class &&
2464 wh.type._class.registered && wh.type._class.registered.type == enumClass)
2466 uint64 value = strtoul(exp.constant, null, 0);
2467 Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2468 EnumClassData enumeration = (EnumClassData)enumClass.data;
2470 for(item = enumeration.values.first; item; item = item.next)
2471 if((int)item.data == value)
2474 wh.value = CopyString(item.name);
2476 wh.value = CopyString($"Invalid Enum Value");
2477 result = (bool)atoi(exp.constant);
2479 else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class &&
2480 wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2487 if(exp.constant[0] == '\'')
2489 if((int)((byte *)exp.constant)[1] > 127)
2492 value = UTF8GetChar(exp.constant + 1, &nb);
2493 if(nb < 2) value = exp.constant[1];
2494 signedValue = value;
2498 signedValue = exp.constant[1];
2500 // Precomp Syntax error with boot strap here:
2501 byte b = (byte)(char)signedValue;
2502 value = (unichar) b;
2508 if(wh.type.kind == charType && wh.type.isSigned)
2510 signedValue = (int)(char)strtol(exp.constant, null, 0);
2512 // Precomp Syntax error with boot strap here:
2513 byte b = (byte)(char)signedValue;
2514 value = (unichar) b;
2519 value = (uint)strtoul(exp.constant, null, 0);
2520 signedValue = (int)value;
2524 UTF32toUTF8Len(&value, 1, charString, 5);
2526 snprintf(string, sizeof(string), "\'\\0' (0)");
2527 else if(value == '\t')
2528 snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2529 else if(value == '\n')
2530 snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2531 else if(value == '\r')
2532 snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2533 else if(wh.type.kind == charType && wh.type.isSigned)
2534 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2535 else if(value > 256 || wh.type.kind != charType)
2537 if(value > 0x10FFFF || !GetCharCategory(value))
2538 snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2540 snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2543 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2544 string[sizeof(string)-1] = 0;
2546 wh.value = CopyString(string);
2551 wh.value = CopyString(exp.constant);
2552 result = (bool)atoi(exp.constant);
2558 wh.value = PrintHexUInt64(exp.address);
2559 result = (bool)exp.address;
2563 char tempString[256];
2564 if(exp.member.memberType == propertyMember)
2565 snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2567 snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression,
2568 exp.type.OnGetString(tempString, null, null));
2574 snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2575 if(exp) FreeExpression(exp);
2578 SetPrivateModule(backupPrivateModule);
2579 SetCurrentContext(backupContext);
2580 SetTopContext(backupContext);
2581 SetGlobalContext(backupContext);
2582 SetThisClass(backupThisClass);
2585 // wh.value = CopyString("No source file found for selected frame");
2587 watchmsg[sizeof(watchmsg)-1] = 0;
2589 wh.value = CopyString(watchmsg);
2591 ide.watchesView.UpdateWatch(wh);
2595 void EvaluateWatches()
2597 for(wh : ide.workspace.watches)
2601 char * ::GdbEvaluateExpression(char * expression)
2605 GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2607 ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2611 // to be removed... use GdbReadMemory that returns a byte array instead
2612 char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
2618 printf("GdbReadMemoryString called with size = 0!\n");
2620 // GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
2621 if(GetRuntimePlatform() == win32)
2622 GdbCommand(false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
2624 GdbCommand(false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
2626 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
2630 byte * ::GdbReadMemory(uint64 address, int bytes)
2634 //GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
2635 if(GetRuntimePlatform() == win32)
2636 GdbCommand(false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
2638 GdbCommand(false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
2641 printf("GdbReadMemory called with bytes = 0!\n");
2644 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
2645 else if(eval.result && strcmp(eval.result, "N/A"))
2647 byte * result = new byte[bytes];
2648 byte * string = eval.result;
2652 result[c++] = (byte)strtol(string, &string, 10);
2668 void EventHit(GdbDataStop stopItem)
2670 bool conditionMet = true;
2671 Breakpoint bp = bpHit;
2673 if(!bp && bpRunToCursor)
2678 if(bp.type == user && stopItem.frame.line && bp.line != stopItem.frame.line)
2680 bp.line = stopItem.frame.line;
2681 ide.breakpointsView.UpdateBreakpoint(bp.row);
2682 ide.workspace.Save();
2688 case internalWinMain:
2689 GdbBreakpointsInsert();
2690 if(userBreakOnInternBreak)
2692 userBreakOnInternBreak = false;
2693 // Why was SelectFrame missing here?
2694 SelectFrame(activeFrameLevel);
2695 GoToStackFrameLine(activeFrameLevel, true);
2696 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
2700 GdbExecContinue(false);
2702 case internalModulesLoaded:
2704 GdbInsertInternalBreakpoints();
2705 GdbBreakpointsInsert();
2706 GdbExecContinue(false);
2708 case internalModuleLoad:
2709 GdbBreakpointsInsert();
2710 GdbExecContinue(false);
2715 conditionMet = ResolveWatch(bp.condition);
2717 if((bp.level == -1 || bp.level == frameCount-1) && conditionMet)
2722 ignoreBreakpoints = false;
2723 // Why was SelectFrame missing here?
2724 SelectFrame(activeFrameLevel);
2725 GoToStackFrameLine(activeFrameLevel, true);
2726 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
2732 GdbExecContinue(false);
2736 GdbExecContinue(false);
2737 ide.breakpointsView.UpdateBreakpoint(bp.row);
2742 ide.outputView.debugBox.Logf("Debugger Error: Breakpoint hit could not match breakpoint instance\n");
2746 if(symbols && bpRunToCursor.inserted)
2747 GdbCommand(false, "-break-delete %d", bpRunToCursor.bp.number);
2748 delete bpRunToCursor;
2752 void GdbThreadExit()
2754 if(state != terminated)
2756 ChangeState(terminated);
2757 targetProcessId = 0;
2758 ClearBreakDisplay();
2762 serialSemaphore.Release();
2767 ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
2768 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2770 HideDebuggerViews();
2772 //ChangeState(terminated);
2776 void GdbThreadMain(char * output)
2779 Array<char *> outTokens { minAllocSize = 50 };
2780 Array<char *> subTokens { minAllocSize = 50 };
2781 DebugListItem item { };
2782 DebugListItem item2 { };
2783 bool setWaitingForPID = false;
2785 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
2786 #ifdef GDB_DEBUG_CONSOLE
2787 Log(output); Log("\n");
2789 #ifdef GDB_DEBUG_OUTPUT
2791 int len = strlen(output);
2799 for(c = 0; c < len / 1024; c++)
2801 strncpy(tmp, start, 1024);
2802 ide.outputView.gdbBox.Logf("out: %s\n", tmp);
2805 ide.outputView.gdbBox.Logf("out: %s\n", start);
2809 ide.outputView.gdbBox.Logf("out: %s\n", output);
2813 #ifdef GDB_DEBUG_CONSOLE
2814 strcpy(lastGdbOutput, output);
2816 #ifdef GDB_DEBUG_GUI
2817 if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
2824 if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
2827 ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
2833 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
2835 //if(outTokens.count == 1)
2840 ChangeState(loaded);
2841 targetProcessId = 0;
2842 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2844 if(!strcmp(item.name, "reason"))
2846 char * reason = item.value;
2847 StripQuotes(reason, reason);
2848 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
2851 if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
2853 StripQuotes(item2.value, item2.value);
2854 if(!strcmp(item2.name, "exit-code"))
2855 exitCode = item2.value;
2861 HandleExit(reason, exitCode);
2865 DebuggerProtocolUnknown("Unknown kill reply", item.name);
2868 HandleExit(null, null);
2871 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2873 if(!strcmp(item.name, "bkpt"))
2875 sentBreakInsert = false;
2878 printf("problem\n");
2880 bpItem = GdbDataBreakpoint { };
2881 item.value = StripCurlies(item.value);
2882 TokenizeList(item.value, ',', subTokens);
2883 for(i = 0; i < subTokens.count; i++)
2885 if(TokenizeListItem(subTokens[i], item))
2887 StripQuotes(item.value, item.value);
2888 if(!strcmp(item.name, "number"))
2889 bpItem.number = atoi(item.value);
2890 else if(!strcmp(item.name, "type"))
2891 bpItem.type = CopyString(item.value);
2892 else if(!strcmp(item.name, "disp"))
2893 bpItem.disp = CopyString(item.value);
2894 else if(!strcmp(item.name, "enabled"))
2895 bpItem.enabled = (!strcmpi(item.value, "y"));
2896 else if(!strcmp(item.name, "addr"))
2897 bpItem.addr = CopyString(item.value);
2898 else if(!strcmp(item.name, "func"))
2899 bpItem.func = CopyString(item.value);
2900 else if(!strcmp(item.name, "file"))
2901 bpItem.file = item.value;
2902 else if(!strcmp(item.name, "line"))
2903 bpItem.line = atoi(item.value);
2904 else if(!strcmp(item.name, "at"))
2905 bpItem.at = CopyString(item.value);
2906 else if(!strcmp(item.name, "times"))
2907 bpItem.times = atoi(item.value);
2910 //breakType = bpValidation;
2911 //app.SignalEvent();
2912 subTokens.RemoveAll();
2914 else if(!strcmp(item.name, "BreakpointTable"))
2915 ide.outputView.debugBox.Logf("Debugger Error: Command reply BreakpointTable not handled\n");
2916 else if(!strcmp(item.name, "depth"))
2918 StripQuotes(item.value, item.value);
2919 frameCount = atoi(item.value);
2921 stackFrames.Free(Frame::Free);
2923 else if(!strcmp(item.name, "stack"))
2926 if(stackFrames.count)
2927 ide.callStackView.Logf("...\n");
2930 item.value = StripBrackets(item.value);
2931 TokenizeList(item.value, ',', subTokens);
2932 for(i = 0; i < subTokens.count; i++)
2934 if(TokenizeListItem(subTokens[i], item))
2936 if(!strcmp(item.name, "frame"))
2939 stackFrames.Add(frame);
2940 item.value = StripCurlies(item.value);
2941 ParseFrame(frame, item.value);
2942 if(frame.file && frame.from)
2943 DebuggerProtocolUnknown("Unexpected frame file and from members present", "");
2947 if(activeFrameLevel == -1)
2949 if(ide.projectView.IsModuleInProject(frame.file));
2951 if(frame.level != 0)
2953 //stopItem.frame = frame;
2954 breakType = selectFrame;
2957 activeFrame = frame;
2958 activeFrameLevel = frame.level;
2961 ide.callStackView.Logf("%3d ", frame.level);
2962 if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
2963 ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
2964 else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
2965 ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
2966 else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
2967 ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
2968 else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
2969 ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
2971 ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
2976 ide.callStackView.Logf("%3d ", frame.level);
2981 ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
2985 ide.callStackView.Logf("%s\n", frame.func);
2987 ide.callStackView.Logf($"unknown source\n");
2991 DebuggerProtocolUnknown("Unknown stack content", item.name);
2994 if(activeFrameLevel == -1)
2996 activeFrameLevel = 0;
2997 activeFrame = stackFrames.first;
2999 ide.callStackView.Home();
3001 subTokens.RemoveAll();
3003 /*else if(!strcmp(item.name, "frame"))
3006 item.value = StripCurlies(item.value);
3007 ParseFrame(&frame, item.value);
3009 else if(!strcmp(item.name, "thread-ids"))
3011 ide.threadsView.Clear();
3012 item.value = StripCurlies(item.value);
3013 TokenizeList(item.value, ',', subTokens);
3014 for(i = subTokens.count - 1; ; i--)
3016 if(TokenizeListItem(subTokens[i], item))
3018 if(!strcmp(item.name, "thread-id"))
3021 StripQuotes(item.value, item.value);
3022 value = atoi(item.value);
3023 ide.threadsView.Logf("%3d \n", value);
3026 DebuggerProtocolUnknown("Unknown threads content", item.name);
3031 ide.threadsView.Home();
3033 subTokens.RemoveAll();
3034 //if(!strcmp(outTokens[2], "number-of-threads"))
3036 else if(!strcmp(item.name, "new-thread-id"))
3038 StripQuotes(item.value, item.value);
3039 activeThread = atoi(item.value);
3041 else if(!strcmp(item.name, "value"))
3043 StripQuotes(item.value, item.value);
3044 eval.result = CopyString(item.value);
3045 eval.active = false;
3047 else if(!strcmp(item.name, "addr"))
3049 for(i = 2; i < outTokens.count; i++)
3051 if(TokenizeListItem(outTokens[i], item))
3053 if(!strcmp(item.name, "total-bytes"))
3055 StripQuotes(item.value, item.value);
3056 eval.bytes = atoi(item.value);
3058 else if(!strcmp(item.name, "next-row"))
3060 StripQuotes(item.value, item.value);
3061 eval.nextBlockAddress = _strtoui64(item.value, null, 0);
3063 else if(!strcmp(item.name, "memory"))
3067 //StripQuotes(item.value, item.value);
3068 item.value = StripBrackets(item.value);
3069 // this should be treated as a list...
3070 item.value = StripCurlies(item.value);
3071 TokenizeList(item.value, ',', subTokens);
3072 for(j = 0; j < subTokens.count; j++)
3074 if(TokenizeListItem(subTokens[j], item))
3076 if(!strcmp(item.name, "data"))
3078 item.value = StripBrackets(item.value);
3079 StripQuotes2(item.value, item.value);
3080 eval.result = CopyString(item.value);
3081 eval.active = false;
3085 subTokens.RemoveAll();
3090 else if(!strcmp(item.name, "source-path"))
3094 DebuggerProtocolUnknown("Unknown command reply", item.name);
3097 else if(!strcmp(outTokens[0], "^running"))
3099 waitingForPID = true;
3100 setWaitingForPID = true;
3102 else if(!strcmp(outTokens[0], "^exit"))
3104 ChangeState(terminated);
3105 // ide.outputView.debugBox.Logf("Exit\n");
3106 // ide.Update(null);
3108 serialSemaphore.Release();
3110 else if(!strcmp(outTokens[0], "^error"))
3114 sentBreakInsert = false;
3115 breakpointError = true;
3118 printf("problem\n");
3120 bpItem = GdbDataBreakpoint { };
3123 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3125 if(!strcmp(item.name, "msg"))
3127 StripQuotes(item.value, item.value);
3130 eval.active = false;
3132 if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3133 eval.error = symbolNotFound;
3134 else if(strstr(item.value, "Cannot access memory at address"))
3135 eval.error = memoryCantBeRead;
3137 eval.error = unknown;
3139 else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3142 else if(!strncmp(item.value, "Cannot access memory at address", 31))
3145 else if(!strcmp(item.value, "Cannot find bounds of current function"))
3147 ChangeState(stopped);
3148 gdbHandle.Printf("-exec-continue\n");
3150 else if(!strcmp(item.value, "ptrace: No such process."))
3152 ChangeState(loaded);
3153 targetProcessId = 0;
3155 else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3158 else if(!strcmp(item.value, "You can't do that without a process to debug."))
3160 ChangeState(loaded);
3161 targetProcessId = 0;
3163 else if(strstr(item.value, "No such file or directory."))
3165 ChangeState(loaded);
3166 targetProcessId = 0;
3168 else if(strstr(item.value, "During startup program exited with code "))
3170 ChangeState(loaded);
3171 targetProcessId = 0;
3176 if(strlen(item.value) < MAX_F_STRING)
3179 ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3183 ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3189 DebuggerProtocolUnknown("Unknown error content", item.name);
3192 DebuggerProtocolUnknown("Unknown result-record", outTokens[0]);
3194 outTokens.RemoveAll();
3197 DebuggerProtocolUnknown("Unknown status-async-output", outTokens[0]);
3200 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "=thread-group-created")) //=thread-group-created,id="7611"
3202 else if(!strcmp(outTokens[0], "=thread-created")) //=thread-created,id="1",group-id="7611"
3204 else if(!strcmp(outTokens[0], "=library-loaded")) //=library-loaded,id="/lib/ld-linux.so.2",target-name="/lib/ld-linux.so.2",host-name="/lib/ld-linux.so.2",symbols-loaded="0"
3205 FGODetectLoadedLibraryForAddedProjectIssues(outTokens);
3207 DebuggerProtocolUnknown("Unknown notify-async-output", outTokens[0]);
3208 outTokens.RemoveAll();
3212 if(TokenizeList(output, ',', outTokens))
3214 if(!strcmp(outTokens[0],"*running"))
3216 waitingForPID = true;
3217 setWaitingForPID = true;
3219 else if(!strcmp(outTokens[0], "*stopped"))
3222 ChangeState(stopped);
3224 for(tk = 1; tk < outTokens.count; tk++)
3226 if(TokenizeListItem(outTokens[tk], item))
3228 if(!strcmp(item.name, "reason"))
3230 char * reason = item.value;
3231 StripQuotes(reason, reason);
3232 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3235 if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3238 StripQuotes(item2.value, item2.value);
3239 if(!strcmp(item2.name, "exit-code"))
3240 exitCode = item2.value;
3246 HandleExit(reason, exitCode);
3248 else if(!strcmp(reason, "breakpoint-hit"))
3252 printf("problem\n");
3254 stopItem = GdbDataStop { };
3256 for(i = tk+1; i < outTokens.count; i++)
3258 TokenizeListItem(outTokens[i], item);
3259 StripQuotes(item.value, item.value);
3260 if(!strcmp(item.name, "bkptno"))
3261 stopItem.bkptno = atoi(item.value);
3262 else if(!strcmp(item.name, "thread-id"))
3263 stopItem.threadid = atoi(item.value);
3264 else if(!strcmp(item.name, "frame"))
3266 item.value = StripCurlies(item.value);
3267 ParseFrame(stopItem.frame, item.value);
3270 DebuggerProtocolUnknown("Unknown breakpoint hit item name", item.name);
3275 else if(!strcmp(reason, "end-stepping-range"))
3279 printf("problem\n");
3281 stopItem = GdbDataStop { };
3283 for(i = tk+1; i < outTokens.count; i++)
3285 TokenizeListItem(outTokens[i], item);
3286 StripQuotes(item.value, item.value);
3287 if(!strcmp(item.name, "thread-id"))
3288 stopItem.threadid = atoi(item.value);
3289 else if(!strcmp(item.name, "frame"))
3291 item.value = StripCurlies(item.value);
3292 ParseFrame(stopItem.frame, item.value);
3294 else if(!strcmp(item.name, "reason"))
3296 else if(!strcmp(item.name, "bkptno"))
3299 DebuggerProtocolUnknown("Unknown end of stepping range item name", item.name);
3305 else if(!strcmp(reason, "function-finished"))
3309 printf("problem\n");
3311 stopItem = GdbDataStop { };
3312 stopItem.reason = CopyString(reason);
3314 for(i = tk+1; i < outTokens.count; i++)
3316 TokenizeListItem(outTokens[i], item);
3317 StripQuotes(item.value, item.value);
3318 if(!strcmp(item.name, "thread-id"))
3319 stopItem.threadid = atoi(item.value);
3320 else if(!strcmp(item.name, "frame"))
3322 item.value = StripCurlies(item.value);
3323 ParseFrame(stopItem.frame, item.value);
3325 else if(!strcmp(item.name, "gdb-result-var"))
3326 stopItem.gdbResultVar = CopyString(item.value);
3327 else if(!strcmp(item.name, "return-value"))
3328 stopItem.returnValue = CopyString(item.value);
3330 DebuggerProtocolUnknown("Unknown function finished item name", item.name);
3333 event = functionEnd;
3336 else if(!strcmp(reason, "signal-received"))
3340 printf("problem\n");
3342 stopItem = GdbDataStop { };
3343 stopItem.reason = CopyString(reason);
3345 for(i = tk+1; i < outTokens.count; i++)
3347 TokenizeListItem(outTokens[i], item);
3348 StripQuotes(item.value, item.value);
3349 if(!strcmp(item.name, "signal-name"))
3350 stopItem.name = CopyString(item.value);
3351 else if(!strcmp(item.name, "signal-meaning"))
3352 stopItem.meaning = CopyString(item.value);
3353 else if(!strcmp(item.name, "thread-id"))
3354 stopItem.threadid = atoi(item.value);
3355 else if(!strcmp(item.name, "frame"))
3357 item.value = StripCurlies(item.value);
3358 ParseFrame(stopItem.frame, item.value);
3361 DebuggerProtocolUnknown("Unknown signal reveived item name", item.name);
3363 if(!strcmp(stopItem.name, "SIGTRAP"))
3382 else if(!strcmp(reason, "watchpoint-trigger"))
3383 DebuggerProtocolUnknown("Reason watchpoint trigger not handled", "");
3384 else if(!strcmp(reason, "read-watchpoint-trigger"))
3385 DebuggerProtocolUnknown("Reason read watchpoint trigger not handled", "");
3386 else if(!strcmp(reason, "access-watchpoint-trigger"))
3387 DebuggerProtocolUnknown("Reason access watchpoint trigger not handled", "");
3388 else if(!strcmp(reason, "watchpoint-scope"))
3389 DebuggerProtocolUnknown("Reason watchpoint scope not handled", "");
3390 else if(!strcmp(reason, "location-reached"))
3391 DebuggerProtocolUnknown("Reason location reached not handled", "");
3393 DebuggerProtocolUnknown("Unknown reason", reason);
3401 DebuggerProtocolUnknown("Unknown exec-async-output", outTokens[0]);
3402 outTokens.RemoveAll();
3405 if(!strcmpi(output, "(gdb) "))
3409 char exeFile[MAX_LOCATION];
3410 int oldProcessID = targetProcessId;
3411 GetLastDirectory(targetFile, exeFile);
3415 targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3416 if(targetProcessId || gdbHandle.Peek()) break;
3421 ChangeState(running);
3422 else if(!oldProcessID)
3424 ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3425 // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3426 gdbHandle.Printf("-gdb-exit\n");
3428 ChangeState(terminated); //loaded;
3433 for(bp : ide.workspace.breakpoints)
3434 bp.inserted = false;
3437 bp.inserted = false;
3439 bpRunToCursor.inserted = false;
3441 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3442 ClearBreakDisplay();
3444 #if defined(__unix__)
3445 if(FileExists(progFifoPath)) //fileCreated)
3447 progThread.terminate = true;
3450 fifoFile.CloseInput();
3457 DeleteFile(progFifoPath);
3458 progFifoPath[0] = '\0';
3465 serialSemaphore.Release();
3468 DebuggerProtocolUnknown($"Unknown prompt", output);
3472 if(!strncmp(output, "&\"warning:", 10))
3475 content = strstr(output, "\"");
3476 StripQuotes(content, content);
3477 content = strstr(content, ":");
3483 ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3490 DebuggerProtocolUnknown($"Unknown output", output);
3492 if(!setWaitingForPID)
3493 waitingForPID = false;
3494 setWaitingForPID = false;
3502 // From GDB Output functions
3503 void FGODetectLoadedLibraryForAddedProjectIssues(Array<char *> outTokens)
3505 char path[MAX_LOCATION] = "";
3506 char file[MAX_FILENAME] = "";
3508 DebugListItem item { };
3509 for(token : outTokens)
3511 if(TokenizeListItem(token, item))
3513 if(!strcmp(item.name, "target-name"))
3515 StripQuotes(item.value, path);
3516 MakeSystemPath(path);
3517 GetLastDirectory(path, file);
3519 else if(!strcmp(item.name, "symbols-loaded"))
3521 symbolsLoaded = (atoi(item.value) == 1);
3526 if(path[0] && file[0])
3528 for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
3532 char prjTargetPath[MAX_LOCATION];
3533 char prjTargetFile[MAX_FILENAME];
3534 DirExpression targetDirExp = prj.GetTargetDir(currentCompiler, prj.config, bitDepth);
3535 strcpy(prjTargetPath, prj.topNode.path);
3536 PathCat(prjTargetPath, targetDirExp.dir);
3537 prjTargetFile[0] = '\0';
3538 prj.CatTargetFileName(prjTargetFile, currentCompiler, prj.config);
3539 PathCat(prjTargetPath, prjTargetFile);
3540 MakeSystemPath(prjTargetPath);
3542 match = !fstrcmp(prjTargetFile, file);
3543 if(!match && (dot = strstr(prjTargetFile, ".so.")))
3545 char * dot3 = strstr(dot+4, ".");
3549 match = !fstrcmp(prjTargetFile, file);
3554 match = !fstrcmp(prjTargetFile, file);
3559 // TODO: nice visual feedback to better warn user. use some ide notification system or other means.
3560 /* -- 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)
3562 ide.outputView.debugBox.Logf($"Attention! No symbols for loaded library %s matched to the %s added project.\n", path, prj.topNode.name);
3564 match = !fstrcmp(prjTargetPath, path);
3565 if(!match && (dot = strstr(prjTargetPath, ".so.")))
3567 char * dot3 = strstr(dot+4, ".");
3571 match = !fstrcmp(prjTargetPath, path);
3576 match = !fstrcmp(prjTargetPath, path);
3580 ide.outputView.debugBox.Logf($"Loaded library %s doesn't match the %s target of the %s added project.\n", path, prjTargetPath, prj.topNode.name);
3587 void RunToCursorPrepare(char * absoluteFilePath, char * relativeFilePath, int lineNumber, bool atSameLevel)
3591 //bpRunToCursor.Free();
3592 bpRunToCursor = Breakpoint { };
3595 bpRunToCursor = Breakpoint { };
3597 if(absoluteFilePath)
3598 bpRunToCursor.absoluteFilePath = CopyString(absoluteFilePath);
3599 if(relativeFilePath)
3600 bpRunToCursor.relativeFilePath = CopyString(relativeFilePath);
3601 bpRunToCursor.line = lineNumber;
3602 bpRunToCursor.type = runToCursor;
3603 bpRunToCursor.enabled = true;
3604 bpRunToCursor.condition = null;
3605 bpRunToCursor.ignore = 0;
3606 bpRunToCursor.level = atSameLevel ? frameCount - activeFrameLevel -1 : -1;
3609 ExpressionType ::DebugEvalExpTypeError(char * result)
3615 case symbolNotFound:
3616 return symbolErrorExp;
3617 case memoryCantBeRead:
3618 return memoryErrorExp;
3620 return unknownErrorExp;
3623 char * ::EvaluateExpression(char * expression, ExpressionType * error)
3626 if(ide.projectView && ide.debugger.state == stopped)
3628 result = GdbEvaluateExpression(expression);
3629 *error = DebugEvalExpTypeError(result);
3634 *error = noDebuggerErrorExp;
3639 char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
3642 char * result = GdbReadMemoryString(address, size, format, 1, 1);
3643 if(!result || !strcmp(result, "N/A"))
3644 *error = memoryErrorExp;
3646 *error = DebugEvalExpTypeError(result);
3651 class GdbThread : Thread
3657 static char output[4096];
3658 Array<char> dynamicBuffer { minAllocSize = 4096 };
3659 DualPipe oldGdbHandle = gdbHandle;
3660 incref oldGdbHandle;
3663 while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
3667 result = gdbHandle.Read(output, 1, sizeof(output));
3669 if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
3676 for(c = 0; c<result; c++)
3678 if(output[c] == '\n')
3680 int pos = dynamicBuffer.size;
3681 dynamicBuffer.size += c - start;
3682 memcpy(&dynamicBuffer[pos], output + start, c - start);
3683 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3684 // COMMENTED OUT DUE TO ISSUE #135, FIXED
3685 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
3686 dynamicBuffer.size++;
3687 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
3689 // printf("%s\n", dynamicBuffer.array);
3691 debugger.GdbThreadMain(&dynamicBuffer[0]);
3692 dynamicBuffer.size = 0;
3698 int pos = dynamicBuffer.size;
3699 dynamicBuffer.size += c - start;
3700 memcpy(&dynamicBuffer[pos], output + start, c - start);
3706 printf("Got end of file from GDB!\n");
3710 delete dynamicBuffer;
3711 //if(oldGdbHandle == gdbHandle)
3712 debugger.GdbThreadExit();
3713 delete oldGdbHandle;
3719 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
3720 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
3722 #if defined(__unix__)
3727 #include <sys/types.h>
3732 class ProgramThread : Thread
3738 bool fileCreated = false;
3740 static char output[1000];
3743 /*if(!mkfifo(progFifoPath, mask))
3750 ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
3754 if(FileExists(progFifoPath)) //fileCreated)
3756 fifoFile = FileOpen(progFifoPath, read);
3760 ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
3765 fd = fileno((FILE *)fifoFile.input);
3766 //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
3770 while(!terminate && fifoFile && !fifoFile.Eof())
3773 struct timeval time;
3781 selectResult = select(fd + 1, &rs, null, null, &time);
3782 if(FD_ISSET(fd, &rs))
3784 int result = (int)read(fd, output, sizeof(output)-1);
3785 if(!result || (result < 0 && errno != EAGAIN))
3789 output[result] = '\0';
3790 if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
3793 ide.outputView.debugBox.Log(output);
3802 //fifoFile.CloseInput();
3805 ide.outputView.debugBox.Log("\n");
3809 if(FileExists(progFifoPath)) //fileCreated)
3811 DeleteFile(progFifoPath);
3812 progFifoPath[0] = '\0';
3820 class Argument : struct
3822 Argument prev, next;
3838 class Frame : struct
3847 property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
3849 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3850 char * absoluteFile;
3859 delete absoluteFile;
3860 args.Free(Argument::Free);
3869 class GdbDataStop : struct
3886 char * gdbResultVar;
3896 if(!strcmp(reason, "signal-received"))
3901 else if(!strcmp(reason, "function-finished"))
3903 delete gdbResultVar;
3908 if(frame) frame.Free();
3917 class GdbDataBreakpoint : struct
3926 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3941 ~GdbDataBreakpoint()
3947 class Breakpoint : struct
3951 char * relativeFilePath;
3952 char * absoluteFilePath;
3961 BreakpointType type;
3964 GdbDataBreakpoint bp;
3966 char * LocationToString()
3968 char location[MAX_LOCATION+20];
3969 snprintf(location, sizeof(location), "%s:%d", relativeFilePath, line);
3970 location[sizeof(location)-1] = 0;
3971 #if defined(__WIN32__)
3972 ChangeCh(location, '/', '\\');
3974 return CopyString(location);
3979 if(relativeFilePath && relativeFilePath[0])
3981 f.Printf(" * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
3983 f.Printf(" ~ %s\n", condition.expression);
3992 delete relativeFilePath;
3993 delete absoluteFilePath;
4003 class Watch : struct
4014 f.Printf(" ~ %s\n", expression);
4038 class DebugListItem : struct
4044 struct DebugEvaluationData
4049 uint64 nextBlockAddress;
4051 DebuggerEvaluationError error;
4054 class CodeLocation : struct
4057 char * absoluteFile;
4060 CodeLocation ::ParseCodeLocation(char * location)
4064 char * colon = null;
4066 char loc[MAX_LOCATION];
4067 strcpy(loc, location);
4068 for(temp = loc; temp = strstr(temp, ":"); temp++)
4076 int line = atoi(colon);
4079 CodeLocation codloc { line = line };
4080 codloc.file = CopyString(loc);
4081 codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
4093 delete absoluteFile;
4103 void GDBFallBack(Expression exp, String expString)
4106 ExpressionType evalError = dummyExp;
4107 result = Debugger::EvaluateExpression(expString, &evalError);
4110 exp.constant = result;
4111 exp.type = constantExp;