7 #define GDB_DEBUG_CONSOLE
10 extern char * strrchr(const char * s, int c);
22 #include <sys/time.h> // Required on Apple...
27 public char * StripQuotes2(char * string, char * output)
31 bool quoted = false, escaped = false;
33 for(c = 0; ch = string[c]; c++)
37 if(escaped || ch != '\"')
40 escaped = !escaped && ch == '\\';
55 static void strescpy(char * d, char * s)
108 static char * CopyUnescapedSystemPath(char * p)
110 char * d = new char[strlen(p) + 1];
112 #if defined(__WIN32__)
113 ChangeCh(d, '/', '\\');
118 static char * CopyUnescapedUnixPath(char * p)
120 char * d = new char[strlen(p) + 1];
122 #if defined(__WIN32__)
123 ChangeCh(d, '\\', '/');
128 static char * CopyUnescapedString(char * s)
130 char * d = new char[strlen(s) + 1];
135 // String Unescape Copy
137 // TOFIX: THIS DOESN'T HANDLE NUMERIC ESCAPE CODES (OCTAL/HEXADECIMAL...)?
138 // Seems very similar to ReadString in pass15.ec (which also misses numeric escape codes :) )
140 static void struscpy(char * d, char * s)
192 static char * StripBrackets(char * string)
194 int length = strlen(string);
195 if(length > 1 && *string == '[' && string[length - 1] == ']')
198 string[length - 1] = '\0';
205 static char * StripCurlies(char * string)
207 int length = strlen(string);
208 if(length > 1 && *string == '{' && string[length - 1] == '}')
211 string[length - 1] = '\0';
218 static int StringGetInt(char * string, int start)
221 int i, len = strlen(string);
223 for(i = start; i < len && i < start + 8; i++)
225 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')
226 strncat(number, &string[i], 1);
233 static int TokenizeList(char * string, const char seperator, Array<char *> tokens)
237 bool quoted = false, escaped = false;
238 char * start = string, ch;
240 for(; (ch = *string); string++)
247 if(escaped || ch != '\"')
248 escaped = !escaped && ch == '\\';
254 else if(ch == '{' || ch == '[' || ch == '(' || ch == '<')
256 else if(ch == '}' || ch == ']' || ch == ')' || ch == '>')
258 else if(ch == seperator && !level)
267 //tokens[count] = start;
268 //tokens[count++] = start;
275 static bool TokenizeListItem(char * string, DebugListItem item)
277 char * equal = strstr(string, "=");
291 static void DebuggerProtocolUnknown(char * message, char * gdbOutput)
293 #ifdef _DEBUG_GDB_PROTOCOL
294 ide.outputView.debugBox.Logf("Debugger Protocol Error: %s (%s)\n", message, gdbOutput);
298 // define GdbGetLineSize = 1638400;
299 define GdbGetLineSize = 5638400;
300 #if defined(__unix__)
301 char progFifoPath[MAX_LOCATION];
302 char progFifoDir[MAX_LOCATION];
305 enum DebuggerState { none, prompt, loaded, running, stopped, terminated };
306 enum DebuggerEvent { none, hit, breakEvent, signal, stepEnd, functionEnd, exit };
307 enum DebuggerAction { none, internal, restart, stop, selectFrame }; //, bpValidation
308 enum BreakpointType { none, internalMain, internalWinMain, internalModulesLoaded, user, runToCursor };
309 enum DebuggerEvaluationError { none, symbolNotFound, memoryCantBeRead, unknown };
311 FileDialog debuggerFileDialog { type = selectDir };
313 static DualPipe gdbHandle;
314 static DebugEvaluationData eval { };
316 static int targetProcessId;
318 static bool gdbReady;
319 static bool breakpointError;
323 Semaphore serialSemaphore { };
328 //bool breakpointsInserted;
330 bool sentBreakInsert;
331 bool ignoreBreakpoints;
332 bool userBreakOnInternBreak;
339 int activeFrameLevel;
350 DebuggerAction breakType;
351 //DebuggerCommand lastCommand; // THE COMPILER COMPILES STUFF THAT DOES NOT EXIST???
353 GdbDataStop stopItem;
354 GdbDataBreakpoint bpItem;
357 List<Breakpoint> sysBPs { };
358 Breakpoint bpRunToCursor;
364 CompilerConfig currentCompiler;
365 ProjectConfig prjConfig;
367 CodeEditor codeEditor;
369 GdbThread gdbThread { debugger = this };
372 delay = 0.0, userData = this;
376 bool monitor = false;
377 DebuggerEvent curEvent = event;
378 GdbDataStop stopItem = this.stopItem;
384 this.stopItem = null;
387 if(curEvent && curEvent != exit)
390 printf("No stop item\n");
398 Restart(currentCompiler, prjConfig);
407 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
408 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
409 if(activeFrame.level == activeFrameLevel)
415 // GdbCommand(false, "-break-info %d", bpItem.number);
425 activeThread = stopItem.threadid;
426 GdbCommand(false, "-thread-list-ids");
431 Breakpoint bp = null;
433 for(i : ide.workspace.breakpoints; i.bp && i.bp.number == stopItem.bkptno)
440 for(i : sysBPs; i.bp && i.bp.number == stopItem.bkptno)
446 if(bp && bp.type != user && stopItem && stopItem.frame)
448 // In case the user put a breakpoint where an internal breakpoint is, avoid the confusion...
449 for(i : ide.workspace.breakpoints)
451 if(i.bp && i.line == stopItem.frame.line && !fstrcmp(i.absoluteFilePath, stopItem.frame.absoluteFile))
458 if(!(!userBreakOnInternBreak &&
459 bp && (bp.type == internalMain || bp.type == internalWinMain || bp.type == internalModulesLoaded)))
461 hitThread = stopItem.threadid;
465 signalThread = stopItem.threadid;
477 activeThread = stopItem.threadid;
478 GdbCommand(false, "-thread-list-ids");
480 if(activeFrameLevel > 0)
481 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
483 WatchesCodeEditorLinkInit();
493 ide.outputView.debugBox.Logf($"Signal received: %s - %s\n", stopItem.name, stopItem.meaning);
494 ide.outputView.debugBox.Logf(" %s:%d\n", (s = CopySystemPath(stopItem.frame.file)), stopItem.frame.line);
500 // Why was SelectFrame missing here?
501 SelectFrame(activeFrameLevel);
502 GoToStackFrameLine(activeFrameLevel, curEvent == signal || curEvent == stepEnd /*false*/);
503 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
505 if(curEvent == signal)
506 ide.outputView.Show();
507 if(curEvent == signal || curEvent == breakEvent)
509 if(curEvent == breakEvent)
510 ide.threadsView.Show();
511 ide.callStackView.Show();
513 ide.ShowCodeEditor();
514 if(curEvent == breakEvent)
515 ide.callStackView.Activate();
523 ignoreBreakpoints = false;
534 #ifdef GDB_DEBUG_CONSOLE
535 char lastGdbOutput[GdbGetLineSize];
537 #if defined(__unix__)
538 ProgramThread progThread { };
541 void ChangeState(DebuggerState value)
543 bool same = value == state;
544 // if(same) PrintLn("Debugger::ChangeState -- changing to same state");
546 if(!same && ide) ide.AdjustDebugMenus();
551 // Stop(); // Don't need to call Stop here, because ~ProjectView() will call it explicitly.
553 stackFrames.Free(Frame::Free);
563 waitingForPID = false;
568 sentBreakInsert = false;
569 ignoreBreakpoints = false;
570 userBreakOnInternBreak = false;
573 activeFrameLevel = 0;
590 bpRunToCursor = null;
593 delete currentCompiler;
597 /*GdbThread gdbThread
603 ideProcessId = Process_GetCurrentProcessId();
605 sysBPs.Add(Breakpoint { type = internalMain, enabled = true, level = -1 });
606 #if defined(__WIN32__)
607 sysBPs.Add(Breakpoint { type = internalWinMain, enabled = true, level = -1 });
609 sysBPs.Add(Breakpoint { type = internalModulesLoaded, enabled = true, level = -1 });
622 property bool isActive { get { return state == running || state == stopped; } }
623 property bool isPrepared { get { return state == loaded || state == running || state == stopped; } }
627 GdbExecContinue(true);
635 GdbDebugBreak(false);
647 GdbDebugBreak(false);
658 void Restart(CompilerConfig compiler, ProjectConfig config)
666 GdbDebugBreak(false);
673 if(!GdbInit(compiler, config))
681 bool GoToCodeLine(char * location)
684 codloc = CodeLocation::ParseCodeLocation(location);
687 CodeEditor editor = (CodeEditor)ide.OpenFile(codloc.absoluteFile, normal, true, null, no, normal);
690 EditBox editBox = editor.editBox;
691 editBox.GoToLineNum(codloc.line - 1);
692 editBox.GoToPosition(editBox.line, codloc.line - 1, 0);
699 bool GoToStackFrameLine(int stackLevel, bool askForLocation)
703 char filePath[MAX_LOCATION];
704 char sourceDir[MAX_LOCATION];
706 CodeEditor editor = null;
707 if(stackLevel == -1) // this (the two lines) is part of that fix that I would not put in for some time
709 for(frame = stackFrames.first; frame; frame = frame.next)
710 if(frame.level == stackLevel)
714 ide.callStackView.Show();
716 if(!frame.absoluteFile && frame.file)
717 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
718 if(!frame.absoluteFile && askForLocation && frame.file)
721 char title[MAX_LOCATION];
722 snprintf(title, sizeof(title), $"Provide source file location for %s", (s = CopySystemPath(frame.file)));
723 title[sizeof(title)-1] = 0;
725 if(SourceDirDialog(title, ide.workspace.projectDir, frame.file, sourceDir))
727 AddSourceDir(sourceDir);
728 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
731 if(frame.absoluteFile)
732 editor = (CodeEditor)ide.OpenFile(frame.absoluteFile, normal, true, null, no, normal);
734 if(editor && frame.line)
736 EditBox editBox = editor.editBox;
737 editBox.GoToLineNum(frame.line - 1);
738 editBox.GoToPosition(editBox.line, frame.line - 1, 0);
746 void SelectThread(int thread)
750 if(thread != activeThread)
752 activeFrameLevel = -1;
753 ide.callStackView.Clear();
754 GdbCommand(false, "-thread-select %d", thread);
756 // Why was SelectFrame missing here?
757 SelectFrame(activeFrameLevel);
758 GoToStackFrameLine(activeFrameLevel, true);
759 WatchesCodeEditorLinkRelease();
760 WatchesCodeEditorLinkInit();
764 ide.callStackView.Show();
768 void SelectFrame(int frame)
772 if(frame != activeFrameLevel || !codeEditor || !codeEditor.visible)
774 activeFrameLevel = frame; // there is no active frame number in the gdb reply
775 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
776 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
777 if(activeFrame.level == activeFrameLevel)
780 WatchesCodeEditorLinkRelease();
781 WatchesCodeEditorLinkInit();
788 void HandleExit(char * reason, char * code)
790 bool returnedExitCode = false;
791 char verboseExitCode[128];
793 ChangeState(loaded); // this state change seems to be superfluous, might be in case of gdb crash
798 snprintf(verboseExitCode, sizeof(verboseExitCode), $" with exit code %s", code);
799 verboseExitCode[sizeof(verboseExitCode)-1] = 0;
802 verboseExitCode[0] = '\0';
806 // ClearBreakDisplay();
810 for(wh : ide.workspace.watches)
812 if(wh.type) FreeType(wh.type);
815 ide.watchesView.UpdateWatch(wh);
819 #if defined(__unix__)
820 progThread.terminate = true;
823 fifoFile.CloseInput();
832 char program[MAX_LOCATION];
833 GetSystemPathBuffer(program, targetFile);
835 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
836 else if(!strcmp(reason, "exited-normally"))
837 ide.outputView.debugBox.Logf($"The program %s has exited normally%s.\n", program, verboseExitCode);
838 else if(!strcmp(reason, "exited"))
839 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
840 else if(!strcmp(reason, "exited-signalled"))
841 ide.outputView.debugBox.Logf($"The program %s has exited with a signal%s.\n", program, verboseExitCode);
843 ide.outputView.debugBox.Logf($"The program %s has exited (gdb provided an unknown reason)%s.\n", program, verboseExitCode);
848 void Start(CompilerConfig compiler, ProjectConfig config)
850 ide.outputView.debugBox.Clear();
855 if(!GdbInit(compiler, config))
863 void StepInto(CompilerConfig compiler, ProjectConfig config)
869 if(!GdbInit(compiler, config))
872 ide.outputView.ShowClearSelectTab(debug);
873 ide.outputView.debugBox.Logf($"Starting debug mode\n");
874 userBreakOnInternBreak = true;
883 void StepOver(CompilerConfig compiler, ProjectConfig config, bool ignoreBkpts)
889 if(!GdbInit(compiler, config))
892 ide.outputView.ShowClearSelectTab(debug);
893 ide.outputView.debugBox.Logf($"Starting debug mode\n");
894 ignoreBreakpoints = ignoreBkpts;
895 userBreakOnInternBreak = true;
899 ignoreBreakpoints = ignoreBkpts;
900 if(ignoreBreakpoints)
901 GdbBreakpointsDelete(true);
907 void StepOut(bool ignoreBkpts)
911 ignoreBreakpoints = ignoreBkpts;
912 if(ignoreBreakpoints)
913 GdbBreakpointsDelete(true);
918 void RunToCursor(CompilerConfig compiler, ProjectConfig config, char * absoluteFilePath, int lineNumber, bool ignoreBkpts)
920 char relativeFilePath[MAX_LOCATION];
921 DebuggerState oldState = state;
922 ignoreBreakpoints = ignoreBkpts;
923 if(!ide.projectView.GetRelativePath(absoluteFilePath, relativeFilePath))
924 strcpy(relativeFilePath, absoluteFilePath);
929 Start(compiler, config);
936 ide.outputView.ShowClearSelectTab(debug);
937 ide.outputView.debugBox.Logf($"Starting debug mode\n");
939 RunToCursorPrepare(absoluteFilePath, relativeFilePath, lineNumber);
940 sentBreakInsert = true;
941 GdbCommand(false, "-break-insert %s:%d", relativeFilePath, lineNumber);
942 bpRunToCursor.bp = bpItem;
944 bpRunToCursor.inserted = (bpRunToCursor.bp.number != 0);
945 ValidateBreakpoint(bpRunToCursor);
952 if(ignoreBreakpoints)
953 GdbBreakpointsDelete(false);
957 if(ignoreBreakpoints)
958 GdbBreakpointsDelete(false);
959 GdbExecContinue(true);
965 void GetCallStackCursorLine(bool * error, int * lineCursor, int * lineTopFrame)
967 if(activeFrameLevel == -1)
975 *error = signalOn && activeThread == signalThread;
976 *lineCursor = activeFrameLevel + 1;
977 *lineTopFrame = activeFrameLevel ? 1 : 0;
981 int GetMarginIconsLineNumbers(char * fileName, int lines[], bool enabled[], int max, bool * error, int * lineCursor, int * lineTopFrame)
983 char winFilePath[MAX_LOCATION];
984 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
986 Iterator<Breakpoint> it { ide.workspace.breakpoints };
987 while(it.Next() && count < max)
989 Breakpoint bp = it.data;
992 if(bp.absoluteFilePath && bp.absoluteFilePath[0] && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
994 lines[count] = bp.line;
995 enabled[count] = bp.enabled;
1000 if(activeFrameLevel == -1)
1008 *error = signalOn && activeThread == signalThread;
1009 if(activeFrame && activeFrame.absoluteFile && !fstrcmp(absoluteFilePath, activeFrame.absoluteFile))
1010 *lineCursor = activeFrame.line;
1013 if(activeFrame && stopItem && stopItem.frame && activeFrame.level == stopItem.frame.level)
1015 else if(stopItem && stopItem.frame && stopItem.frame.absoluteFile && !fstrcmp(absoluteFilePath, stopItem.frame.absoluteFile))
1016 *lineTopFrame = stopItem.frame.line;
1020 if(*lineTopFrame == *lineCursor && *lineTopFrame)
1026 void ChangeWatch(DataRow row, char * expression)
1028 Watch wh = (Watch)row.tag;
1031 delete wh.expression;
1033 wh.expression = CopyString(expression);
1036 Iterator<Watch> it { ide.workspace.watches };
1038 ide.workspace.watches.Delete(it.pointer);
1044 row.tag = (int64)wh;
1045 ide.workspace.watches.Add(wh);
1047 wh.expression = CopyString(expression);
1049 ide.workspace.Save();
1050 //if(expression && state == stopped)
1055 void MoveIcons(char * fileName, int lineNumber, int move, bool start)
1057 char winFilePath[MAX_LOCATION];
1058 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1061 for(bpLink = ide.workspace.breakpoints.first; bpLink; bpLink = next)
1063 Breakpoint bp = (Breakpoint)bpLink.data;
1066 if(bp.type == user && bp.absoluteFilePath && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1068 if(bp.line > lineNumber || (bp.line == lineNumber && start))
1070 if(move < 0 && (bp.line < lineNumber - move))
1071 ide.workspace.RemoveBreakpoint(bp);
1075 ide.breakpointsView.UpdateBreakpoint(bp.row);
1076 ide.workspace.Save();
1082 // moving code cursors is futile, on next step, stop, hit, cursors will be offset anyways
1085 bool SourceDirDialog(char * title, char * startDir, char * test, char * sourceDir)
1089 String srcDir = null;
1091 debuggerFileDialog.text = title;
1092 debuggerFileDialog.currentDirectory = startDir;
1093 debuggerFileDialog.master = ide;
1095 while(debuggerFileDialog.Modal())
1097 strcpy(sourceDir, debuggerFileDialog.filePath);
1098 if(!fstrcmp(ide.workspace.projectDir, sourceDir) &&
1099 MessageBox { type = yesNo, master = ide,
1100 contents = $"This is the project directory.\nWould you like to try again?",
1101 text = $"Invalid Source Directory" }.Modal() == no)
1105 for(dir : ide.workspace.sourceDirs)
1107 if(!fstrcmp(dir, sourceDir))
1115 MessageBox { type = yesNo, master = ide,
1116 contents = $"This source directory is already specified.\nWould you like to try again?",
1117 text = $"Invalid Source Directory" }.Modal() == no)
1123 char file[MAX_LOCATION];
1124 strcpy(file, sourceDir);
1125 PathCat(file, test);
1126 result = FileExists(file);
1128 MessageBox { type = yesNo, master = ide,
1129 contents = $"Unable to locate source file.\nWould you like to try again?",
1130 text = $"Invalid Source Directory" }.Modal() == no)
1144 void AddSourceDir(char * sourceDir)
1146 ide.workspace.sourceDirs.Add(CopyString(sourceDir));
1147 ide.workspace.Save();
1151 DebuggerState oldState = state;
1156 GdbDebugBreak(true);
1159 GdbCommand(false, "-environment-directory \"%s\"", sourceDir);
1162 if(oldState == running)
1163 GdbExecContinue(false);
1167 void ToggleBreakpoint(char * fileName, int lineNumber, Project prj)
1169 char winFilePath[MAX_LOCATION];
1170 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1171 char absolutePath[MAX_LOCATION];
1172 char relativePath[MAX_LOCATION];
1173 char sourceDir[MAX_LOCATION];
1174 Breakpoint bp = null;
1176 strcpy(absolutePath, absoluteFilePath);
1177 for(i : ide.workspace.breakpoints; i.type == user && i.absoluteFilePath && !fstrcmp(i.absoluteFilePath, absolutePath) && i.line == lineNumber)
1186 ide.workspace.RemoveBreakpoint(bp);
1194 // FIXED: This is how it should have been... Source locations are only for files not in project
1195 // if(IsPathInsideOf(absolutePath, ide.workspace.projectDir))
1196 // MakePathRelative(absolutePath, ide.workspace.projectDir, relativePath);
1197 bool result = false;
1199 result = prj.GetRelativePath(absolutePath, relativePath);
1201 ide.projectView.GetRelativePath(absolutePath, relativePath);
1202 //if(ide.projectView.GetRelativePath(absolutePath, relativePath));
1206 char title[MAX_LOCATION];
1207 char directory[MAX_LOCATION];
1208 StripLastDirectory(absolutePath, directory);
1209 snprintf(title, sizeof(title), $"Provide source files location directory for %s", absolutePath);
1210 title[sizeof(title)-1] = 0;
1213 String srcDir = null;
1214 for(dir : ide.workspace.sourceDirs)
1216 if(IsPathInsideOf(absolutePath, dir))
1218 MakePathRelative(absoluteFilePath, dir, relativePath);
1226 if(SourceDirDialog(title, directory, null, sourceDir))
1228 if(IsPathInsideOf(absolutePath, sourceDir))
1230 AddSourceDir(sourceDir);
1231 MakePathRelative(absoluteFilePath, sourceDir, relativePath);
1234 else if(MessageBox { type = yesNo, master = ide,
1235 contents = $"You must provide a valid source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1236 text = $"Invalid Source Directory" }.Modal() == no)
1239 else if(MessageBox { type = yesNo, master = ide,
1240 contents = $"You must provide a source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1241 text = $"No Source Directory Provided" }.Modal() == no)
1245 ide.workspace.bpCount++;
1246 bp = { line = lineNumber, type = user, enabled = true, level = -1 };
1247 ide.workspace.breakpoints.Add(bp);
1248 bp.absoluteFilePath = CopyString(absolutePath);
1249 bp.relativeFilePath = CopyString(relativePath);
1250 ide.breakpointsView.AddBreakpoint(bp);
1255 DebuggerState oldState = state;
1260 GdbDebugBreak(true);
1265 sentBreakInsert = true;
1266 GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, bp.line);
1269 bp.inserted = (bp.bp && bp.bp.number != 0);
1270 ValidateBreakpoint(bp);
1274 if(oldState == running)
1275 GdbExecContinue(false);
1278 ide.workspace.Save();
1281 void UpdateRemovedBreakpoint(Breakpoint bp)
1283 if(targeted && bp.inserted)
1285 DebuggerState oldState = state;
1290 GdbDebugBreak(true);
1294 GdbCommand(false, "-break-delete %d", bp.bp.number);
1297 if(oldState == running)
1298 GdbExecContinue(false);
1304 void ParseFrame(Frame frame, char * string)
1307 Array<char *> frameTokens { minAllocSize = 50 };
1308 Array<char *> argsTokens { minAllocSize = 50 };
1309 Array<char *> argumentTokens { minAllocSize = 50 };
1310 DebugListItem item { };
1313 TokenizeList(string, ',', frameTokens);
1314 for(i = 0; i < frameTokens.count; i++)
1316 if(TokenizeListItem(frameTokens[i], item))
1318 StripQuotes(item.value, item.value);
1319 if(!strcmp(item.name, "level"))
1320 frame.level = atoi(item.value);
1321 else if(!strcmp(item.name, "addr"))
1322 frame.addr = CopyString(item.value);
1323 else if(!strcmp(item.name, "func"))
1324 frame.func = CopyString(item.value);
1325 else if(!strcmp(item.name, "args"))
1327 if(!strcmp(item.value, "[]"))
1328 frame.argsCount = 0;
1331 item.value = StripBrackets(item.value);
1332 TokenizeList(item.value, ',', argsTokens);
1333 for(j = 0; j < argsTokens.count; j++)
1335 argsTokens[j] = StripCurlies(argsTokens[j]);
1336 TokenizeList(argsTokens[j], ',', argumentTokens);
1337 for(k = 0; k < argumentTokens.count; k++)
1340 frame.args.Add(arg);
1341 if(TokenizeListItem(argumentTokens[k], item))
1343 if(!strcmp(item.name, "name"))
1345 StripQuotes(item.value, item.value);
1346 arg.name = CopyString(item.value);
1348 else if(!strcmp(item.name, "value"))
1350 StripQuotes(item.value, item.value);
1351 arg.value = CopyString(item.value);
1354 DebuggerProtocolUnknown("Unknown frame args item name", item.name);
1357 DebuggerProtocolUnknown("Bad frame args item", "");
1359 argumentTokens.RemoveAll();
1361 frame.argsCount = argsTokens.count;
1362 argsTokens.RemoveAll();
1365 else if(!strcmp(item.name, "from"))
1366 frame.from = item.value;
1367 else if(!strcmp(item.name, "file"))
1369 frame.file = item.value;
1370 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1372 else if(!strcmp(item.name, "line"))
1373 frame.line = atoi(item.value);
1374 else if(!strcmp(item.name, "fullname"))
1376 // GDB 6.3 on OS X is giving "fullname" and "dir", all in absolute, but file name only in 'file'
1377 String path = ide.workspace.GetPathWorkspaceRelativeOrAbsolute(item.value);
1378 if(strcmp(frame.file, path))
1381 delete frame.absoluteFile;
1382 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1387 DebuggerProtocolUnknown("Unknown frame member name", item.name);
1390 DebuggerProtocolUnknown("Bad frame", "");
1395 delete argumentTokens;
1399 void ShowDebuggerViews()
1401 ide.outputView.Show();
1402 ide.outputView.SelectTab(debug);
1403 ide.threadsView.Show();
1404 ide.callStackView.Show();
1405 ide.watchesView.Show();
1409 void HideDebuggerViews()
1411 ide.RepositionWindows(true);
1414 void ::GdbCommand(bool focus, char * format, ...)
1418 // TODO: Improve this limit
1419 static char string[MAX_F_STRING*3];
1421 va_start(args, format);
1422 vsnprintf(string, sizeof(string), format, args);
1423 string[sizeof(string)-1] = 0;
1427 ide.debugger.serialSemaphore.TryWait();
1430 #ifdef GDB_DEBUG_CONSOLE
1431 Log(string); Log("\n");
1433 #ifdef GDB_DEBUG_OUTPUT
1434 ide.outputView.gdbBox.Logf("cmd: %s\n", string);
1436 #ifdef GDB_DEBUG_GUI
1438 ide.gdbDialog.AddCommand(string);
1440 strcat(string,"\n");
1441 gdbHandle.Puts(string);
1444 Process_ShowWindows(targetProcessId);
1447 ide.debugger.serialSemaphore.Wait();
1452 bool ValidateBreakpoint(Breakpoint bp)
1454 if(modules && bp.bp)
1456 if(bp.bp.line != bp.line)
1461 ide.outputView.debugBox.Logf("WOULD HAVE -- Invalid breakpoint disabled: %s:%d\n", bp.relativeFilePath, bp.line);
1465 //GdbCommand(false, "-break-delete %d", bp.bp.number);
1466 //bp.inserted = false;
1468 //bp.enabled = false;
1473 ide.outputView.debugBox.Logf("Debugger Error: ValidateBreakpoint error\n");
1474 bp.line = bp.bp.line;
1481 static void GdbInsertInternalBreakpoint()
1485 //if(!breakpointsInserted)
1487 DirExpression objDir = ide.project.GetObjDir(currentCompiler, prjConfig);
1492 if(bp.type == internalMain)
1494 sentBreakInsert = true;
1495 GdbCommand(false, "-break-insert main");
1498 bp.inserted = (bp.bp && bp.bp.number != 0);
1500 #if defined(__WIN32__)
1501 else if(bp.type == internalWinMain)
1503 sentBreakInsert = true;
1504 GdbCommand(false, "-break-insert WinMain");
1507 bp.inserted = (bp.bp && bp.bp.number != 0);
1510 else if(bp.type == internalModulesLoaded)
1512 char path[MAX_LOCATION];
1513 char name[MAX_LOCATION];
1514 char fixedModuleName[MAX_FILENAME];
1517 bool moduleLoadBlock = false;
1519 ReplaceSpaces(fixedModuleName, ide.project.moduleName);
1520 snprintf(name, sizeof(name),"%s.main.ec", fixedModuleName);
1521 name[sizeof(name)-1] = 0;
1522 strcpy(path, ide.workspace.projectDir);
1523 PathCatSlash(path, objDir.dir);
1524 PathCatSlash(path, name);
1525 f = FileOpen(path, read);
1528 for(lineNumber = 1; !f.Eof(); lineNumber++)
1530 if(f.GetLine(line, sizeof(line) - 1))
1532 bool moduleLoadLine;
1533 TrimLSpaces(line, line);
1534 moduleLoadLine = !strncmp(line, "eModule_Load", strlen("eModule_Load"));
1535 if(!moduleLoadBlock && moduleLoadLine)
1536 moduleLoadBlock = true;
1537 else if(moduleLoadBlock && !moduleLoadLine && strlen(line) > 0)
1543 char relative[MAX_LOCATION];
1544 bp.absoluteFilePath = CopyString(path);
1545 MakePathRelative(path, ide.workspace.projectDir, relative);
1546 delete bp.relativeFilePath;
1547 bp.relativeFilePath = CopyString(relative);
1548 bp.line = lineNumber;
1549 sentBreakInsert = true;
1550 GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, lineNumber);
1553 bp.inserted = (bp.bp && bp.bp.number != 0);
1554 ValidateBreakpoint(bp);
1567 void GdbBreakpointsInsert()
1571 //if(!breakpointsInserted)
1573 //if(!ignoreBreakpoints)
1574 //breakpointsInserted = true;
1575 for(bp : ide.workspace.breakpoints)
1577 if(!bp.inserted && bp.type == user)
1579 if(!ignoreBreakpoints && bp.enabled)
1581 sentBreakInsert = true;
1582 breakpointError = false;
1583 GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, bp.line);
1584 // Improve, GdbCommand should return a success value?
1587 char fileName[MAX_FILENAME];
1588 breakpointError = false;
1589 GetLastDirectory(bp.relativeFilePath, fileName);
1590 sentBreakInsert = true;
1591 GdbCommand(false, "-break-insert %s:%d", fileName, bp.line);
1595 bp.inserted = (bp.bp && bp.bp.number != 0);
1598 ValidateBreakpoint(bp);
1604 printf("problem\n");
1606 bp.bp = GdbDataBreakpoint { };
1610 if(bpRunToCursor && !bpRunToCursor.inserted)
1612 sentBreakInsert = true;
1613 GdbCommand(false, "-break-insert %s:%d", bpRunToCursor.relativeFilePath, bpRunToCursor.line);
1614 bpRunToCursor.bp = bpItem;
1616 bpRunToCursor.inserted = (bpRunToCursor.bp && bpRunToCursor.bp.number != 0);
1617 ValidateBreakpoint(bpRunToCursor);
1623 void GdbBreakpointsDelete(bool deleteRunToCursor)
1625 //breakpointsInserted = false;
1628 for(bp : ide.workspace.breakpoints)
1631 GdbCommand(false, "-break-delete %d", bp.bp.number);
1632 bp.inserted = false;
1634 //check here (reply form -break-delete, returns bpitem?)
1637 if(deleteRunToCursor && bpRunToCursor)
1639 GdbCommand(false, "-break-delete %d", bpRunToCursor.bp.number);
1640 bpRunToCursor.inserted = false;
1641 bpRunToCursor.bp = bpItem;
1642 //check here (reply form -break-delete, returns bpitem?)
1651 stackFrames.Free(Frame::Free);
1652 GdbCommand(false, "-stack-info-depth");
1654 GdbCommand(false, "-stack-info-depth 192");
1655 if(frameCount && frameCount <= 192)
1656 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 191));
1659 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 95));
1660 GdbCommand(false, "-stack-list-frames %d %d", Max(frameCount - 96, 96), frameCount - 1);
1662 GdbCommand(false, "");
1669 char escaped[MAX_LOCATION];
1670 strescpy(escaped, targetFile);
1671 GdbCommand(false, "file \"%s\"", escaped); //GDB/MI Missing Implementation -symbol-file, -target-attach
1676 for(prj : ide.workspace.projects)
1678 if(prj == ide.workspace.projects.firstIterator.data)
1681 //PrintLn("THIS: ", (String)prj.topNode.path);
1682 GdbCommand(false, "-environment-directory \"%s\"", prj.topNode.path);
1683 //GdbCommand(false, ""); // why this empty GDB command
1686 for(dir : ide.workspace.sourceDirs)
1688 GdbCommand(false, "-environment-directory \"%s\"", dir);
1689 //GdbCommand(false, ""); // why this empty GDB command
1691 GdbInsertInternalBreakpoint();
1697 void GdbTargetRelease()
1701 GdbBreakpointsDelete(true);
1702 GdbCommand(false, "file"); //GDB/MI Missing Implementation -target-detach
1708 void GdbDebugBreak(bool internal)
1713 breakType = DebuggerAction::internal;
1715 if(ide) ide.Update(null);
1717 if(Process_Break(targetProcessId)) //GdbCommand(false, "-exec-interrupt");
1718 serialSemaphore.Wait();
1721 ChangeState(loaded);
1722 targetProcessId = 0;
1727 ide.outputView.debugBox.Logf("Debugger Error: GdbDebugBreak with not target id should never happen\n");
1734 ShowDebuggerViews();
1735 GdbCommand(true, "-exec-run");
1738 void GdbExecContinue(bool focus)
1741 GdbCommand(focus, "-exec-continue");
1747 GdbCommand(true, "-exec-next");
1753 GdbCommand(true, "-exec-step");
1756 void GdbExecFinish()
1759 GdbCommand(true, "-exec-finish");
1762 void GdbExecCommon()
1764 ClearBreakDisplay();
1765 GdbBreakpointsInsert();
1768 #ifdef GDB_DEBUG_GUI
1769 void SendGDBCommand(char * command)
1771 DebuggerState oldState = state;
1776 GdbDebugBreak(true);
1779 GdbCommand(false, command);
1782 if(oldState == running)
1783 GdbExecContinue(false);
1787 void ClearBreakDisplay()
1790 activeFrameLevel = -1;
1801 stackFrames.Free(Frame::Free);
1802 WatchesCodeEditorLinkRelease();
1803 ide.callStackView.Clear();
1804 ide.threadsView.Clear();
1811 GdbCommand(false, "-interpreter-exec console \"kill\""); // should use -exec-abort -- GDB/MI implementation incomplete
1815 bool GdbInit(CompilerConfig compiler, ProjectConfig config)
1818 char oldDirectory[MAX_LOCATION];
1819 char tempPath[MAX_LOCATION];
1820 char command[MAX_LOCATION];
1821 Project project = ide.project;
1822 DirExpression targetDirExp = project.GetTargetDir(compiler, config);
1823 PathBackup pathBackup { };
1825 if(currentCompiler != compiler)
1827 delete currentCompiler;
1828 currentCompiler = compiler;
1829 incref currentCompiler;
1833 ChangeState(loaded);
1835 sentBreakInsert = false;
1836 breakpointError = false;
1840 //breakpointsInserted = false;
1842 ide.outputView.ShowClearSelectTab(debug);
1843 ide.outputView.debugBox.Logf($"Starting debug mode\n");
1845 #ifdef GDB_DEBUG_CONSOLE
1846 Log("Starting GDB"); Log("\n");
1848 #ifdef GDB_DEBUG_OUTPUT
1849 ide.outputView.gdbBox.Logf("run: Starting GDB\n");
1852 strcpy(tempPath, ide.workspace.projectDir);
1853 PathCatSlash(tempPath, targetDirExp.dir);
1855 targetDir = CopyString(tempPath);
1856 project.CatTargetFileName(tempPath, compiler, config);
1858 targetFile = CopyString(tempPath);
1860 GetWorkingDir(oldDirectory, MAX_LOCATION);
1861 if(ide.workspace.debugDir && ide.workspace.debugDir[0])
1863 char temp[MAX_LOCATION];
1864 strcpy(temp, ide.workspace.projectDir);
1865 PathCatSlash(temp, ide.workspace.debugDir);
1866 ChangeWorkingDir(temp);
1869 ChangeWorkingDir(ide.workspace.projectDir);
1871 ide.SetPath(true, compiler, config);
1873 // TODO: This pollutes the environment, but at least it works
1874 // It shouldn't really affect the IDE as the PATH gets restored and other variables set for testing will unlikely cause problems
1875 // What is the proper solution for this? DualPipeOpenEnv?
1876 // gdb set environment commands don't seem to take effect
1877 for(e : ide.workspace.environmentVars)
1879 SetEnvironment(e.name, e.string);
1882 strcpy(command, "gdb -n -silent --interpreter=mi2"); //-async //\"%s\"
1884 gdbHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
1887 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start GDB\n");
1895 gdbProcessId = gdbHandle.GetProcessID();
1898 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get GDB process ID\n");
1904 serialSemaphore.Wait();
1909 //ChangeState(terminated);
1915 #if defined(__unix__)
1917 CreateTemporaryDir(progFifoDir, "ecereide");
1918 strcpy(progFifoPath, progFifoDir);
1919 PathCat(progFifoPath, "ideprogfifo");
1920 if(!mkfifo(progFifoPath, 0600))
1922 //fileCreated = true;
1927 ide.outputView.debugBox.Logf(createFIFOMsg, progFifoPath);
1932 progThread.terminate = false;
1933 progThread.Create();
1936 #if defined(__WIN32__)
1937 GdbCommand(false, "-gdb-set new-console on");
1940 GdbCommand(false, "-gdb-set verbose off");
1941 //GdbCommand(false, "-gdb-set exec-done-display on");
1942 GdbCommand(false, "-gdb-set step-mode off");
1943 GdbCommand(false, "-gdb-set unwindonsignal on");
1944 //GdbCommand(false, "-gdb-set shell on");
1945 GdbCommand(false, "set print elements 992");
1946 GdbCommand(false, "-gdb-set backtrace limit 100000");
1948 #if defined(__unix__)
1949 GdbCommand(false, "-inferior-tty-set %s", progFifoPath);
1952 GdbCommand(false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
1954 for(e : ide.workspace.environmentVars)
1956 GdbCommand(false, "set environment %s=%s", e.name, e.string);
1963 ChangeWorkingDir(oldDirectory);
1969 delete targetDirExp;
1975 if(gdbHandle && gdbProcessId)
1977 GdbCommand(false, "-gdb-exit");
1992 ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
1996 for(bp : ide.workspace.breakpoints)
1997 bp.inserted = false;
1999 bp.inserted = false;
2001 bpRunToCursor.inserted = false;
2003 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2004 ClearBreakDisplay();
2007 #if defined(__unix__)
2008 if(FileExists(progFifoPath)) //fileCreated)
2010 progThread.terminate = true;
2013 fifoFile.CloseInput();
2019 DeleteFile(progFifoPath);
2020 progFifoPath[0] = '\0';
2026 void WatchesCodeEditorLinkInit()
2029 char tempPath[MAX_LOCATION];
2030 char path[MAX_LOCATION];
2032 //void MakeFilePathProjectRelative(char * path, char * relativePath)
2033 if(!ide.projectView.GetRelativePath(activeFrame.file, tempPath))
2034 strcpy(tempPath, activeFrame.file);
2036 strcpy(path, ide.workspace.projectDir);
2037 PathCat(path, tempPath);
2038 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no);
2041 for(srcDir : ide.workspace.sourceDirs)
2043 strcpy(path, srcDir);
2044 PathCat(path, tempPath);
2045 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no);
2046 if(codeEditor) break;
2051 if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2052 activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);
2053 if(!activeFrame || !activeFrame.absoluteFile)
2056 codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal);
2059 codeEditor.inUseDebug = true;
2062 //watchesInit = true;
2065 void WatchesCodeEditorLinkRelease()
2071 codeEditor.inUseDebug = false;
2072 if(!codeEditor.visible)
2073 codeEditor.Destroy(0);
2079 bool ResolveWatch(Watch wh)
2081 bool result = false;
2094 char watchmsg[MAX_F_STRING];
2095 if(state == stopped && !codeEditor)
2096 wh.value = CopyString($"No source file found for selected frame");
2097 //if(codeEditor && state == stopped || state != stopped)
2100 Module backupPrivateModule;
2101 Context backupContext;
2102 Class backupThisClass;
2106 backupPrivateModule = GetPrivateModule();
2107 backupContext = GetCurrentContext();
2108 backupThisClass = GetThisClass();
2111 SetPrivateModule(codeEditor.privateModule);
2112 SetCurrentContext(codeEditor.globalContext);
2113 SetTopContext(codeEditor.globalContext);
2114 SetGlobalContext(codeEditor.globalContext);
2115 SetGlobalData(&codeEditor.globalData);
2118 exp = ParseExpressionString(wh.expression);
2120 if(exp && !parseError)
2122 if(GetPrivateModule())
2125 DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2126 ProcessExpressionType(exp);
2128 wh.type = exp.expType;
2131 DebugComputeExpression(exp);
2133 /*if(exp.hasAddress)
2135 char temp[MAX_F_STRING];
2136 sprintf(temp, "0x%x", exp.address);
2137 wh.address = CopyString(temp);
2138 // wh.address = CopyStringf("0x%x", exp.address);
2143 Type dataType = exp.expType;
2146 char temp[MAX_F_STRING];
2147 switch(dataType.kind)
2150 sprintf(temp, "%i", exp.val.c);
2153 sprintf(temp, "%i", exp.val.s);
2158 sprintf(temp, "%i", exp.val.i);
2161 sprintf(temp, "%i", exp.val.i64);
2164 sprintf(temp, "%i", exp.val.p);
2169 long v = (long)exp.val.f;
2170 sprintf(temp, "%i", v);
2175 long v = (long)exp.val.d;
2176 sprintf(temp, "%i", v);
2181 wh.intVal = CopyString(temp);
2182 switch(dataType.kind)
2185 sprintf(temp, "0x%x", exp.val.c);
2188 sprintf(temp, "0x%x", exp.val.s);
2192 sprintf(temp, "0x%x", exp.val.i);
2195 sprintf(temp, "0x%x", exp.val.i64);
2198 sprintf(temp, "0x%x", exp.val.i64);
2201 sprintf(temp, "0x%x", exp.val.p);
2206 long v = (long)exp.val.f;
2207 sprintf(temp, "0x%x", v);
2212 long v = (long)exp.val.d;
2213 sprintf(temp, "0x%x", v);
2218 wh.hexVal = CopyString(temp);
2219 switch(dataType.kind)
2222 sprintf(temp, "0o%o", exp.val.c);
2225 sprintf(temp, "0o%o", exp.val.s);
2229 sprintf(temp, "0o%o", exp.val.i);
2232 sprintf(temp, "0o%o", exp.val.i64);
2235 sprintf(temp, "0o%o", exp.val.i64);
2238 sprintf(temp, "0o%o", exp.val.p);
2243 long v = (long)exp.val.f;
2244 sprintf(temp, "0o%o", v);
2249 long v = (long)exp.val.d;
2250 sprintf(temp, "0o%o", v);
2255 wh.octVal = CopyString(temp);
2258 // WHATS THIS HERE ?
2259 if(exp.type == constantExp && exp.constant)
2260 wh.constant = CopyString(exp.constant);
2266 case symbolErrorExp:
2267 snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2269 case structMemberSymbolErrorExp:
2270 // todo get info as in next case (ExpClassMemberSymbolError)
2271 snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2273 case classMemberSymbolErrorExp:
2276 Expression memberExp = exp.member.exp;
2277 Identifier memberID = exp.member.member;
2278 Type type = memberExp.expType;
2281 _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2284 char string[256] = "";
2286 PrintType(type, string, false, true);
2287 classSym = FindClass(string);
2288 _class = classSym ? classSym.registered : null;
2291 snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2293 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2296 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2299 case memoryErrorExp:
2300 // Need to ensure when set to memoryErrorExp, constant is set
2301 snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2303 case dereferenceErrorExp:
2304 snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2306 case unknownErrorExp:
2307 snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2309 case noDebuggerErrorExp:
2310 snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2312 case debugStateErrorExp:
2313 snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2316 snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2320 // Temporary Code for displaying Strings
2321 if((exp.expType && ((exp.expType.kind == pointerType ||
2322 exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) ||
2323 (wh.type && wh.type.kind == classType && wh.type._class &&
2324 wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2325 !strcmp(wh.type._class.registered.name, "String")))
2328 if(exp.expType.kind != arrayType || exp.hasAddress)
2334 //char temp[MAX_F_STRING * 32];
2336 ExpressionType evalError = dummyExp;
2337 /*if(exp.expType.kind == arrayType)
2338 sprintf(temp, "(char*)0x%x", exp.address);
2340 sprintf(temp, "(char*)%s", exp.constant);*/
2342 //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2343 address = strtoul(exp.constant, null, 0);
2344 //printf("%x\n", address);
2345 snprintf(value, sizeof(value), "0x%08x ", address);
2346 value[sizeof(value)-1] = 0;
2349 strcat(value, $"Null string");
2353 len = strlen(value);
2355 while(!string && size > 2)
2357 string = GdbReadMemory(address, size);
2360 if(string && string[0])
2363 if(UTF8Validate(string))
2368 for(c = 0; (ch = string[c]) && c<4096; c++)
2371 value[len++] = '\0';
2376 ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2377 strcat(value, ") (ISO8859-1)");
2384 strcat(value, $"Empty string");
2388 strcat(value, $"Couldn't read memory");
2390 wh.value = CopyString(value);
2393 else if(wh.type && wh.type.kind == classType && wh.type._class &&
2394 wh.type._class.registered && wh.type._class.registered.type == enumClass)
2396 uint64 value = strtoul(exp.constant, null, 0);
2397 Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2398 EnumClassData enumeration = (EnumClassData)enumClass.data;
2400 for(item = enumeration.values.first; item; item = item.next)
2401 if((int)item.data == value)
2404 wh.value = CopyString(item.name);
2406 wh.value = CopyString($"Invalid Enum Value");
2407 result = (bool)atoi(exp.constant);
2409 else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class &&
2410 wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2417 if(exp.constant[0] == '\'')
2419 if((int)((byte *)exp.constant)[1] > 127)
2422 value = UTF8GetChar(exp.constant + 1, &nb);
2423 if(nb < 2) value = exp.constant[1];
2424 signedValue = value;
2428 signedValue = exp.constant[1];
2430 // Precomp Syntax error with boot strap here:
2431 byte b = (byte)(char)signedValue;
2432 value = (unichar) b;
2438 if(wh.type.kind == charType && wh.type.isSigned)
2440 signedValue = (int)(char)strtol(exp.constant, null, 0);
2442 // Precomp Syntax error with boot strap here:
2443 byte b = (byte)(char)signedValue;
2444 value = (unichar) b;
2449 value = strtoul(exp.constant, null, 0);
2450 signedValue = (int)value;
2454 UTF32toUTF8Len(&value, 1, charString, 5);
2456 snprintf(string, sizeof(string), "\'\\0' (0)");
2457 else if(value == '\t')
2458 snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2459 else if(value == '\n')
2460 snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2461 else if(value == '\r')
2462 snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2463 else if(wh.type.kind == charType && wh.type.isSigned)
2464 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2465 else if(value > 256 || wh.type.kind != charType)
2467 if(value > 0x10FFFF || !GetCharCategory(value))
2468 snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2470 snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2473 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2474 string[sizeof(string)-1] = 0;
2476 wh.value = CopyString(string);
2481 wh.value = CopyString(exp.constant);
2482 result = (bool)atoi(exp.constant);
2488 wh.value = PrintHexUInt(exp.address);
2489 result = (bool)exp.address;
2493 char tempString[256];
2494 if(exp.member.memberType == propertyMember)
2495 snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2497 snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression,
2498 exp.type.OnGetString(tempString, null, null));
2504 snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2505 if(exp) FreeExpression(exp);
2508 SetPrivateModule(backupPrivateModule);
2509 SetCurrentContext(backupContext);
2510 SetTopContext(backupContext);
2511 SetGlobalContext(backupContext);
2512 SetThisClass(backupThisClass);
2515 // wh.value = CopyString("No source file found for selected frame");
2517 watchmsg[sizeof(watchmsg)-1] = 0;
2519 wh.value = CopyString(watchmsg);
2521 ide.watchesView.UpdateWatch(wh);
2525 void EvaluateWatches()
2527 for(wh : ide.workspace.watches)
2531 char * ::GdbEvaluateExpression(char * expression)
2535 GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2537 ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2541 // to be removed... use GdbReadMemory that returns a byte array instead
2542 char * ::GdbReadMemoryString(uint address, int size, char format, int rows, int cols)
2548 printf("GdbReadMemoryString called with size = 0!\n");
2550 GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
2552 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
2556 byte * ::GdbReadMemory(uint address, int bytes)
2560 GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
2563 printf("GdbReadMemory called with bytes = 0!\n");
2566 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
2567 else if(eval.result && strcmp(eval.result, "N/A"))
2569 byte * result = new byte[bytes];
2570 byte * string = eval.result;
2574 result[c++] = (byte)strtol(string, &string, 10);
2590 void EventHit(GdbDataStop stopItem)
2592 bool conditionMet = true;
2593 Breakpoint bp = bpHit;
2595 if(!bp && bpRunToCursor)
2599 GdbCommand(false, "-break-delete %d", bp.bp.number);
2604 if(bp.type == user && stopItem.frame.line && bp.line != stopItem.frame.line)
2606 bp.line = stopItem.frame.line;
2607 ide.breakpointsView.UpdateBreakpoint(bp.row);
2608 ide.workspace.Save();
2614 case internalWinMain:
2615 GdbBreakpointsInsert();
2616 if(userBreakOnInternBreak)
2618 userBreakOnInternBreak = false;
2619 // Why was SelectFrame missing here?
2620 SelectFrame(activeFrameLevel);
2621 GoToStackFrameLine(activeFrameLevel, true);
2622 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
2626 GdbExecContinue(false);
2628 case internalModulesLoaded:
2630 GdbBreakpointsInsert();
2631 GdbExecContinue(false);
2636 conditionMet = ResolveWatch(bp.condition);
2638 if((bp.level == -1 || bp.level == frameCount-1) && conditionMet)
2643 ignoreBreakpoints = false;
2644 // Why was SelectFrame missing here?
2645 SelectFrame(activeFrameLevel);
2646 GoToStackFrameLine(activeFrameLevel, true);
2647 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
2649 if(bp.type == BreakpointType::runToCursor)
2651 delete bpRunToCursor;
2652 bpRunToCursor = null;
2658 GdbExecContinue(false);
2662 GdbExecContinue(false);
2663 ide.breakpointsView.UpdateBreakpoint(bp.row);
2668 ide.outputView.debugBox.Logf("Debugger Error: Breakpoint hit could not match breakpoint instance\n");
2671 void GdbThreadExit()
2673 if(state != terminated)
2675 ChangeState(terminated);
2676 targetProcessId = 0;
2677 ClearBreakDisplay();
2681 serialSemaphore.Release();
2686 ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
2687 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2690 //ChangeState(terminated);
2694 void GdbThreadMain(char * output)
2697 Array<char *> outTokens { minAllocSize = 50 };
2698 Array<char *> subTokens { minAllocSize = 50 };
2699 DebugListItem item { };
2700 DebugListItem item2 { };
2701 bool setWaitingForPID = false;
2703 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
2704 #ifdef GDB_DEBUG_CONSOLE
2705 Log(output); Log("\n");
2707 #ifdef GDB_DEBUG_OUTPUT
2709 int len = strlen(output);
2717 for(c = 0; c < len / 1024; c++)
2719 strncpy(tmp, start, 1024);
2720 ide.outputView.gdbBox.Logf("out: %s\n", tmp);
2723 ide.outputView.gdbBox.Logf("out: %s\n", start);
2727 ide.outputView.gdbBox.Logf("out: %s\n", output);
2731 #ifdef GDB_DEBUG_CONSOLE
2732 strcpy(lastGdbOutput, output);
2734 #ifdef GDB_DEBUG_GUI
2735 if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
2742 if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
2745 ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
2751 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
2753 //if(outTokens.count == 1)
2758 ChangeState(loaded);
2759 targetProcessId = 0;
2760 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2762 if(!strcmp(item.name, "reason"))
2764 char * reason = item.value;
2765 StripQuotes(reason, reason);
2766 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
2769 if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
2771 StripQuotes(item2.value, item2.value);
2772 if(!strcmp(item2.name, "exit-code"))
2773 exitCode = item2.value;
2779 HandleExit(reason, exitCode);
2783 DebuggerProtocolUnknown("Unknown kill reply", item.name);
2786 HandleExit(null, null);
2789 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2791 if(!strcmp(item.name, "bkpt"))
2793 sentBreakInsert = false;
2796 printf("problem\n");
2798 bpItem = GdbDataBreakpoint { };
2799 item.value = StripCurlies(item.value);
2800 TokenizeList(item.value, ',', subTokens);
2801 for(i = 0; i < subTokens.count; i++)
2803 if(TokenizeListItem(subTokens[i], item))
2805 StripQuotes(item.value, item.value);
2806 if(!strcmp(item.name, "number"))
2807 bpItem.number = atoi(item.value);
2808 else if(!strcmp(item.name, "type"))
2809 bpItem.type = CopyString(item.value);
2810 else if(!strcmp(item.name, "disp"))
2811 bpItem.disp = CopyString(item.value);
2812 else if(!strcmp(item.name, "enabled"))
2813 bpItem.enabled = (!strcmpi(item.value, "y"));
2814 else if(!strcmp(item.name, "addr"))
2815 bpItem.addr = CopyString(item.value);
2816 else if(!strcmp(item.name, "func"))
2817 bpItem.func = CopyString(item.value);
2818 else if(!strcmp(item.name, "file"))
2819 bpItem.file = item.value;
2820 else if(!strcmp(item.name, "line"))
2821 bpItem.line = atoi(item.value);
2822 else if(!strcmp(item.name, "at"))
2823 bpItem.at = CopyString(item.value);
2824 else if(!strcmp(item.name, "times"))
2825 bpItem.times = atoi(item.value);
2828 //breakType = bpValidation;
2829 //app.SignalEvent();
2830 subTokens.RemoveAll();
2832 else if(!strcmp(item.name, "BreakpointTable"))
2833 ide.outputView.debugBox.Logf("Debugger Error: Command reply BreakpointTable not handled\n");
2834 else if(!strcmp(item.name, "depth"))
2836 StripQuotes(item.value, item.value);
2837 frameCount = atoi(item.value);
2839 stackFrames.Free(Frame::Free);
2841 else if(!strcmp(item.name, "stack"))
2844 if(stackFrames.count)
2845 ide.callStackView.Logf("...\n");
2848 item.value = StripBrackets(item.value);
2849 TokenizeList(item.value, ',', subTokens);
2850 for(i = 0; i < subTokens.count; i++)
2852 if(TokenizeListItem(subTokens[i], item))
2854 if(!strcmp(item.name, "frame"))
2857 stackFrames.Add(frame);
2858 item.value = StripCurlies(item.value);
2859 ParseFrame(frame, item.value);
2860 if(frame.file && frame.from)
2861 DebuggerProtocolUnknown("Unexpected frame file and from members present", "");
2865 if(activeFrameLevel == -1)
2867 if(ide.projectView.IsModuleInProject(frame.file));
2869 if(frame.level != 0)
2871 //stopItem.frame = frame;
2872 breakType = selectFrame;
2875 activeFrame = frame;
2876 activeFrameLevel = frame.level;
2879 ide.callStackView.Logf("%3d ", frame.level);
2880 if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
2881 ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
2882 else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
2883 ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
2884 else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
2885 ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
2886 else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
2887 ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
2889 ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
2894 ide.callStackView.Logf("%3d ", frame.level);
2899 ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
2903 ide.callStackView.Logf("%s\n", frame.func);
2905 ide.callStackView.Logf($"unknown source\n");
2909 DebuggerProtocolUnknown("Unknown stack content", item.name);
2912 if(activeFrameLevel == -1)
2914 activeFrameLevel = 0;
2915 activeFrame = stackFrames.first;
2917 ide.callStackView.Home();
2919 subTokens.RemoveAll();
2921 /*else if(!strcmp(item.name, "frame"))
2924 item.value = StripCurlies(item.value);
2925 ParseFrame(&frame, item.value);
2927 else if(!strcmp(item.name, "thread-ids"))
2929 ide.threadsView.Clear();
2930 item.value = StripCurlies(item.value);
2931 TokenizeList(item.value, ',', subTokens);
2932 for(i = subTokens.count - 1; ; i--)
2934 if(TokenizeListItem(subTokens[i], item))
2936 if(!strcmp(item.name, "thread-id"))
2939 StripQuotes(item.value, item.value);
2940 value = atoi(item.value);
2941 ide.threadsView.Logf("%3d \n", value);
2944 DebuggerProtocolUnknown("Unknown threads content", item.name);
2949 ide.threadsView.Home();
2951 subTokens.RemoveAll();
2952 //if(!strcmp(outTokens[2], "number-of-threads"))
2954 else if(!strcmp(item.name, "new-thread-id"))
2956 StripQuotes(item.value, item.value);
2957 activeThread = atoi(item.value);
2959 else if(!strcmp(item.name, "value"))
2961 StripQuotes(item.value, item.value);
2962 eval.result = CopyString(item.value);
2963 eval.active = false;
2965 else if(!strcmp(item.name, "addr"))
2967 for(i = 2; i < outTokens.count; i++)
2969 if(TokenizeListItem(outTokens[i], item))
2971 if(!strcmp(item.name, "total-bytes"))
2973 StripQuotes(item.value, item.value);
2974 eval.bytes = atoi(item.value);
2976 else if(!strcmp(item.name, "next-row"))
2978 StripQuotes(item.value, item.value);
2979 eval.nextBlockAddress = strtoul(item.value, null, 0);
2981 else if(!strcmp(item.name, "memory"))
2985 //StripQuotes(item.value, item.value);
2986 item.value = StripBrackets(item.value);
2987 // this should be treated as a list...
2988 item.value = StripCurlies(item.value);
2989 TokenizeList(item.value, ',', subTokens);
2990 for(j = 0; j < subTokens.count; j++)
2992 if(TokenizeListItem(subTokens[j], item))
2994 if(!strcmp(item.name, "data"))
2996 item.value = StripBrackets(item.value);
2997 StripQuotes2(item.value, item.value);
2998 eval.result = CopyString(item.value);
2999 eval.active = false;
3003 subTokens.RemoveAll();
3008 else if(!strcmp(item.name, "source-path"))
3012 DebuggerProtocolUnknown("Unknown command reply", item.name);
3015 else if(!strcmp(outTokens[0], "^running"))
3017 waitingForPID = true;
3018 setWaitingForPID = true;
3020 else if(!strcmp(outTokens[0], "^exit"))
3022 ChangeState(terminated);
3023 // ide.outputView.debugBox.Logf("Exit\n");
3024 // ide.Update(null);
3026 serialSemaphore.Release();
3028 else if(!strcmp(outTokens[0], "^error"))
3032 sentBreakInsert = false;
3033 breakpointError = true;
3036 printf("problem\n");
3038 bpItem = GdbDataBreakpoint { };
3041 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3043 if(!strcmp(item.name, "msg"))
3045 StripQuotes(item.value, item.value);
3048 eval.active = false;
3050 if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3051 eval.error = symbolNotFound;
3052 else if(strstr(item.value, "Cannot access memory at address"))
3053 eval.error = memoryCantBeRead;
3055 eval.error = unknown;
3057 else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3060 else if(!strncmp(item.value, "Cannot access memory at address", 31))
3063 else if(!strcmp(item.value, "Cannot find bounds of current function"))
3065 ChangeState(stopped);
3066 gdbHandle.Printf("-exec-continue\n");
3068 else if(!strcmp(item.value, "ptrace: No such process."))
3070 ChangeState(loaded);
3071 targetProcessId = 0;
3073 else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3076 else if(!strcmp(item.value, "You can't do that without a process to debug."))
3078 ChangeState(loaded);
3079 targetProcessId = 0;
3081 else if(strstr(item.value, "No such file or directory."))
3083 ChangeState(loaded);
3084 targetProcessId = 0;
3086 else if(strstr(item.value, "During startup program exited with code "))
3088 ChangeState(loaded);
3089 targetProcessId = 0;
3094 if(strlen(item.value) < MAX_F_STRING)
3097 ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3101 ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3107 DebuggerProtocolUnknown("Unknown error content", item.name);
3110 DebuggerProtocolUnknown("Unknown result-record", outTokens[0]);
3112 outTokens.RemoveAll();
3115 DebuggerProtocolUnknown("Unknown status-async-output", outTokens[0]);
3118 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "=thread-group-created")) //=thread-group-created,id="7611"
3120 else if(!strcmp(outTokens[0], "=thread-created")) //=thread-created,id="1",group-id="7611"
3122 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"
3125 DebuggerProtocolUnknown("Unknown notify-async-output", outTokens[0]);
3126 outTokens.RemoveAll();
3130 if(TokenizeList(output, ',', outTokens))
3132 if(!strcmp(outTokens[0],"*running"))
3134 waitingForPID = true;
3135 setWaitingForPID = true;
3137 else if(!strcmp(outTokens[0], "*stopped"))
3140 ChangeState(stopped);
3142 for(tk = 1; tk < outTokens.count; tk++)
3144 if(TokenizeListItem(outTokens[tk], item))
3146 if(!strcmp(item.name, "reason"))
3148 char * reason = item.value;
3149 StripQuotes(reason, reason);
3150 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3153 if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3156 StripQuotes(item2.value, item2.value);
3157 if(!strcmp(item2.name, "exit-code"))
3158 exitCode = item2.value;
3164 HandleExit(reason, exitCode);
3166 else if(!strcmp(reason, "breakpoint-hit"))
3170 printf("problem\n");
3172 stopItem = GdbDataStop { };
3174 for(i = tk+1; i < outTokens.count; i++)
3176 TokenizeListItem(outTokens[i], item);
3177 StripQuotes(item.value, item.value);
3178 if(!strcmp(item.name, "bkptno"))
3179 stopItem.bkptno = atoi(item.value);
3180 else if(!strcmp(item.name, "thread-id"))
3181 stopItem.threadid = atoi(item.value);
3182 else if(!strcmp(item.name, "frame"))
3184 item.value = StripCurlies(item.value);
3185 ParseFrame(stopItem.frame, item.value);
3188 DebuggerProtocolUnknown("Unknown breakpoint hit item name", item.name);
3193 else if(!strcmp(reason, "end-stepping-range"))
3197 printf("problem\n");
3199 stopItem = GdbDataStop { };
3201 for(i = tk+1; i < outTokens.count; i++)
3203 TokenizeListItem(outTokens[i], item);
3204 StripQuotes(item.value, item.value);
3205 if(!strcmp(item.name, "thread-id"))
3206 stopItem.threadid = atoi(item.value);
3207 else if(!strcmp(item.name, "frame"))
3209 item.value = StripCurlies(item.value);
3210 ParseFrame(stopItem.frame, item.value);
3212 else if(!strcmp(item.name, "reason"))
3214 else if(!strcmp(item.name, "bkptno"))
3217 DebuggerProtocolUnknown("Unknown end of stepping range item name", item.name);
3223 else if(!strcmp(reason, "function-finished"))
3227 printf("problem\n");
3229 stopItem = GdbDataStop { };
3230 stopItem.reason = CopyString(reason);
3232 for(i = tk+1; i < outTokens.count; i++)
3234 TokenizeListItem(outTokens[i], item);
3235 StripQuotes(item.value, item.value);
3236 if(!strcmp(item.name, "thread-id"))
3237 stopItem.threadid = atoi(item.value);
3238 else if(!strcmp(item.name, "frame"))
3240 item.value = StripCurlies(item.value);
3241 ParseFrame(stopItem.frame, item.value);
3243 else if(!strcmp(item.name, "gdb-result-var"))
3244 stopItem.gdbResultVar = CopyString(item.value);
3245 else if(!strcmp(item.name, "return-value"))
3246 stopItem.returnValue = CopyString(item.value);
3248 DebuggerProtocolUnknown("Unknown function finished item name", item.name);
3251 event = functionEnd;
3254 else if(!strcmp(reason, "signal-received"))
3258 printf("problem\n");
3260 stopItem = GdbDataStop { };
3261 stopItem.reason = CopyString(reason);
3263 for(i = tk+1; i < outTokens.count; i++)
3265 TokenizeListItem(outTokens[i], item);
3266 StripQuotes(item.value, item.value);
3267 if(!strcmp(item.name, "signal-name"))
3268 stopItem.name = CopyString(item.value);
3269 else if(!strcmp(item.name, "signal-meaning"))
3270 stopItem.meaning = CopyString(item.value);
3271 else if(!strcmp(item.name, "thread-id"))
3272 stopItem.threadid = atoi(item.value);
3273 else if(!strcmp(item.name, "frame"))
3275 item.value = StripCurlies(item.value);
3276 ParseFrame(stopItem.frame, item.value);
3279 DebuggerProtocolUnknown("Unknown signal reveived item name", item.name);
3281 if(!strcmp(stopItem.name, "SIGTRAP"))
3300 else if(!strcmp(reason, "watchpoint-trigger"))
3301 DebuggerProtocolUnknown("Reason watchpoint trigger not handled", "");
3302 else if(!strcmp(reason, "read-watchpoint-trigger"))
3303 DebuggerProtocolUnknown("Reason read watchpoint trigger not handled", "");
3304 else if(!strcmp(reason, "access-watchpoint-trigger"))
3305 DebuggerProtocolUnknown("Reason access watchpoint trigger not handled", "");
3306 else if(!strcmp(reason, "watchpoint-scope"))
3307 DebuggerProtocolUnknown("Reason watchpoint scope not handled", "");
3308 else if(!strcmp(reason, "location-reached"))
3309 DebuggerProtocolUnknown("Reason location reached not handled", "");
3311 DebuggerProtocolUnknown("Unknown reason", reason);
3319 DebuggerProtocolUnknown("Unknown exec-async-output", outTokens[0]);
3320 outTokens.RemoveAll();
3323 if(!strcmpi(output, "(gdb) "))
3327 char exeFile[MAX_LOCATION];
3328 int oldProcessID = targetProcessId;
3329 GetLastDirectory(targetFile, exeFile);
3333 targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3334 if(targetProcessId || gdbHandle.Peek()) break;
3339 ChangeState(running);
3340 else if(!oldProcessID)
3342 ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3343 // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3344 gdbHandle.Printf("-gdb-exit\n");
3346 ChangeState(terminated); //loaded;
3351 for(bp : ide.workspace.breakpoints)
3352 bp.inserted = false;
3355 bp.inserted = false;
3357 bpRunToCursor.inserted = false;
3359 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3360 ClearBreakDisplay();
3362 #if defined(__unix__)
3363 if(FileExists(progFifoPath)) //fileCreated)
3365 progThread.terminate = true;
3368 fifoFile.CloseInput();
3375 DeleteFile(progFifoPath);
3376 progFifoPath[0] = '\0';
3383 serialSemaphore.Release();
3386 DebuggerProtocolUnknown($"Unknown prompt", output);
3390 if(!strncmp(output, "&\"warning:", 10))
3393 content = strstr(output, "\"");
3394 StripQuotes(content, content);
3395 content = strstr(content, ":");
3401 ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3408 DebuggerProtocolUnknown($"Unknown output", output);
3410 if(!setWaitingForPID)
3411 waitingForPID = false;
3412 setWaitingForPID = false;
3420 void RunToCursorPrepare(char * absoluteFilePath, char * relativeFilePath, int lineNumber)
3424 //bpRunToCursor.Free();
3425 bpRunToCursor = Breakpoint { };
3428 bpRunToCursor = Breakpoint { };
3430 if(absoluteFilePath)
3431 bpRunToCursor.absoluteFilePath = CopyString(absoluteFilePath);
3432 if(relativeFilePath)
3433 bpRunToCursor.relativeFilePath = CopyString(relativeFilePath);
3434 bpRunToCursor.line = lineNumber;
3435 bpRunToCursor.type = runToCursor;
3436 bpRunToCursor.enabled = true;
3437 bpRunToCursor.condition = null;
3438 bpRunToCursor.ignore = 0;
3439 bpRunToCursor.level = -1;
3442 ExpressionType ::DebugEvalExpTypeError(char * result)
3448 case symbolNotFound:
3449 return symbolErrorExp;
3450 case memoryCantBeRead:
3451 return memoryErrorExp;
3453 return unknownErrorExp;
3456 char * ::EvaluateExpression(char * expression, ExpressionType * error)
3459 if(ide.projectView && ide.debugger.state == stopped)
3461 result = GdbEvaluateExpression(expression);
3462 *error = DebugEvalExpTypeError(result);
3467 *error = noDebuggerErrorExp;
3472 char * ::ReadMemory(uint address, int size, char format, ExpressionType * error)
3475 char * result = GdbReadMemoryString(address, size, format, 1, 1);
3476 if(!result || !strcmp(result, "N/A"))
3477 *error = memoryErrorExp;
3479 *error = DebugEvalExpTypeError(result);
3484 class GdbThread : Thread
3490 static char output[4096];
3491 Array<char> dynamicBuffer { minAllocSize = 4096 };
3492 DualPipe oldGdbHandle = gdbHandle;
3493 incref oldGdbHandle;
3496 while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
3500 result = gdbHandle.Read(output, 1, sizeof(output));
3502 if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
3509 for(c = 0; c<result; c++)
3511 if(output[c] == '\n')
3513 int pos = dynamicBuffer.size;
3514 dynamicBuffer.size += c - start;
3515 memcpy(&dynamicBuffer[pos], output + start, c - start);
3516 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3517 // COMMENTED OUT DUE TO ISSUE #135, FIXED
3518 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
3519 dynamicBuffer.size++;
3520 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
3522 // printf("%s\n", dynamicBuffer.array);
3524 debugger.GdbThreadMain(&dynamicBuffer[0]);
3525 dynamicBuffer.size = 0;
3531 int pos = dynamicBuffer.size;
3532 dynamicBuffer.size += c - start;
3533 memcpy(&dynamicBuffer[pos], output + start, c - start);
3539 printf("Got end of file from GDB!\n");
3543 delete dynamicBuffer;
3544 //if(oldGdbHandle == gdbHandle)
3545 debugger.GdbThreadExit();
3546 delete oldGdbHandle;
3552 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
3553 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
3555 #if defined(__unix__)
3560 #include <sys/types.h>
3565 class ProgramThread : Thread
3571 bool fileCreated = false;
3573 static char output[1000];
3576 /*if(!mkfifo(progFifoPath, mask))
3583 ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
3587 if(FileExists(progFifoPath)) //fileCreated)
3589 fifoFile = FileOpen(progFifoPath, read);
3593 ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
3598 fd = fileno((FILE *)fifoFile.input);
3599 //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
3603 while(!terminate && fifoFile && !fifoFile.Eof())
3606 struct timeval time;
3614 selectResult = select(fd + 1, &rs, null, null, &time);
3615 if(FD_ISSET(fd, &rs))
3617 int result = read(fd, output, sizeof(output)-1);
3618 if(!result || (result < 0 && errno != EAGAIN))
3622 output[result] = '\0';
3623 if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
3626 ide.outputView.debugBox.Log(output);
3635 //fifoFile.CloseInput();
3638 ide.outputView.debugBox.Log("\n");
3642 if(FileExists(progFifoPath)) //fileCreated)
3644 DeleteFile(progFifoPath);
3645 progFifoPath[0] = '\0';
3653 class Argument : struct
3655 Argument prev, next;
3671 class Frame : struct
3680 property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
3682 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3683 char * absoluteFile;
3692 delete absoluteFile;
3693 args.Free(Argument::Free);
3702 class GdbDataStop : struct
3719 char * gdbResultVar;
3729 if(!strcmp(reason, "signal-received"))
3734 else if(!strcmp(reason, "function-finished"))
3736 delete gdbResultVar;
3741 if(frame) frame.Free();
3750 class GdbDataBreakpoint : struct
3759 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3774 ~GdbDataBreakpoint()
3780 class Breakpoint : struct
3784 char * relativeFilePath;
3785 char * absoluteFilePath;
3794 BreakpointType type;
3797 GdbDataBreakpoint bp;
3799 char * LocationToString()
3801 char location[MAX_LOCATION+20];
3802 snprintf(location, sizeof(location), "%s:%d", relativeFilePath, line);
3803 location[sizeof(location)-1] = 0;
3804 #if defined(__WIN32__)
3805 ChangeCh(location, '/', '\\');
3807 return CopyString(location);
3812 if(relativeFilePath && relativeFilePath[0])
3814 f.Printf(" * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
3816 f.Printf(" ~ %s\n", condition.expression);
3825 delete relativeFilePath;
3826 delete absoluteFilePath;
3836 class Watch : struct
3847 f.Printf(" ~ %s\n", expression);
3871 class DebugListItem : struct
3877 struct DebugEvaluationData
3882 uint nextBlockAddress;
3884 DebuggerEvaluationError error;
3887 class CodeLocation : struct
3890 char * absoluteFile;
3893 CodeLocation ::ParseCodeLocation(char * location)
3897 char * colon = null;
3899 char loc[MAX_LOCATION];
3900 strcpy(loc, location);
3901 for(temp = loc; temp = strstr(temp, ":"); temp++)
3909 int line = atoi(colon);
3912 CodeLocation codloc { line = line };
3913 codloc.file = CopyString(loc);
3914 codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
3926 delete absoluteFile;