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 address = _strtoui64(exp.constant, null, 0);
2345 //printf("%x\n", address);
2346 // snprintf(value, sizeof(value), "0x%08x ", address);
2348 if(address > 0xFFFFFFFFLL)
2349 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2351 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2352 value[sizeof(value)-1] = 0;
2355 strcat(value, $"Null string");
2359 len = strlen(value);
2361 while(!string && size > 2)
2363 string = GdbReadMemory(address, size);
2366 if(string && string[0])
2369 if(UTF8Validate(string))
2374 for(c = 0; (ch = string[c]) && c<4096; c++)
2377 value[len++] = '\0';
2382 ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2383 strcat(value, ") (ISO8859-1)");
2390 strcat(value, $"Empty string");
2394 strcat(value, $"Couldn't read memory");
2396 wh.value = CopyString(value);
2399 else if(wh.type && wh.type.kind == classType && wh.type._class &&
2400 wh.type._class.registered && wh.type._class.registered.type == enumClass)
2402 uint64 value = strtoul(exp.constant, null, 0);
2403 Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2404 EnumClassData enumeration = (EnumClassData)enumClass.data;
2406 for(item = enumeration.values.first; item; item = item.next)
2407 if((int)item.data == value)
2410 wh.value = CopyString(item.name);
2412 wh.value = CopyString($"Invalid Enum Value");
2413 result = (bool)atoi(exp.constant);
2415 else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class &&
2416 wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2423 if(exp.constant[0] == '\'')
2425 if((int)((byte *)exp.constant)[1] > 127)
2428 value = UTF8GetChar(exp.constant + 1, &nb);
2429 if(nb < 2) value = exp.constant[1];
2430 signedValue = value;
2434 signedValue = exp.constant[1];
2436 // Precomp Syntax error with boot strap here:
2437 byte b = (byte)(char)signedValue;
2438 value = (unichar) b;
2444 if(wh.type.kind == charType && wh.type.isSigned)
2446 signedValue = (int)(char)strtol(exp.constant, null, 0);
2448 // Precomp Syntax error with boot strap here:
2449 byte b = (byte)(char)signedValue;
2450 value = (unichar) b;
2455 value = strtoul(exp.constant, null, 0);
2456 signedValue = (int)value;
2460 UTF32toUTF8Len(&value, 1, charString, 5);
2462 snprintf(string, sizeof(string), "\'\\0' (0)");
2463 else if(value == '\t')
2464 snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2465 else if(value == '\n')
2466 snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2467 else if(value == '\r')
2468 snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2469 else if(wh.type.kind == charType && wh.type.isSigned)
2470 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2471 else if(value > 256 || wh.type.kind != charType)
2473 if(value > 0x10FFFF || !GetCharCategory(value))
2474 snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2476 snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2479 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2480 string[sizeof(string)-1] = 0;
2482 wh.value = CopyString(string);
2487 wh.value = CopyString(exp.constant);
2488 result = (bool)atoi(exp.constant);
2494 wh.value = PrintHexUInt64(exp.address);
2495 result = (bool)exp.address;
2499 char tempString[256];
2500 if(exp.member.memberType == propertyMember)
2501 snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2503 snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression,
2504 exp.type.OnGetString(tempString, null, null));
2510 snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2511 if(exp) FreeExpression(exp);
2514 SetPrivateModule(backupPrivateModule);
2515 SetCurrentContext(backupContext);
2516 SetTopContext(backupContext);
2517 SetGlobalContext(backupContext);
2518 SetThisClass(backupThisClass);
2521 // wh.value = CopyString("No source file found for selected frame");
2523 watchmsg[sizeof(watchmsg)-1] = 0;
2525 wh.value = CopyString(watchmsg);
2527 ide.watchesView.UpdateWatch(wh);
2531 void EvaluateWatches()
2533 for(wh : ide.workspace.watches)
2537 char * ::GdbEvaluateExpression(char * expression)
2541 GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2543 ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2547 // to be removed... use GdbReadMemory that returns a byte array instead
2548 char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
2554 printf("GdbReadMemoryString called with size = 0!\n");
2556 // GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
2557 if(GetRuntimePlatform() == win32)
2558 GdbCommand(false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
2560 GdbCommand(false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
2562 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
2566 byte * ::GdbReadMemory(uint64 address, int bytes)
2570 //GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
2571 if(GetRuntimePlatform() == win32)
2572 GdbCommand(false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
2574 GdbCommand(false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
2577 printf("GdbReadMemory called with bytes = 0!\n");
2580 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
2581 else if(eval.result && strcmp(eval.result, "N/A"))
2583 byte * result = new byte[bytes];
2584 byte * string = eval.result;
2588 result[c++] = (byte)strtol(string, &string, 10);
2604 void EventHit(GdbDataStop stopItem)
2606 bool conditionMet = true;
2607 Breakpoint bp = bpHit;
2609 if(!bp && bpRunToCursor)
2613 GdbCommand(false, "-break-delete %d", bp.bp.number);
2618 if(bp.type == user && stopItem.frame.line && bp.line != stopItem.frame.line)
2620 bp.line = stopItem.frame.line;
2621 ide.breakpointsView.UpdateBreakpoint(bp.row);
2622 ide.workspace.Save();
2628 case internalWinMain:
2629 GdbBreakpointsInsert();
2630 if(userBreakOnInternBreak)
2632 userBreakOnInternBreak = false;
2633 // Why was SelectFrame missing here?
2634 SelectFrame(activeFrameLevel);
2635 GoToStackFrameLine(activeFrameLevel, true);
2636 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
2640 GdbExecContinue(false);
2642 case internalModulesLoaded:
2644 GdbBreakpointsInsert();
2645 GdbExecContinue(false);
2650 conditionMet = ResolveWatch(bp.condition);
2652 if((bp.level == -1 || bp.level == frameCount-1) && conditionMet)
2657 ignoreBreakpoints = false;
2658 // Why was SelectFrame missing here?
2659 SelectFrame(activeFrameLevel);
2660 GoToStackFrameLine(activeFrameLevel, true);
2661 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
2663 if(bp.type == BreakpointType::runToCursor)
2665 delete bpRunToCursor;
2666 bpRunToCursor = null;
2672 GdbExecContinue(false);
2676 GdbExecContinue(false);
2677 ide.breakpointsView.UpdateBreakpoint(bp.row);
2682 ide.outputView.debugBox.Logf("Debugger Error: Breakpoint hit could not match breakpoint instance\n");
2685 void GdbThreadExit()
2687 if(state != terminated)
2689 ChangeState(terminated);
2690 targetProcessId = 0;
2691 ClearBreakDisplay();
2695 serialSemaphore.Release();
2700 ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
2701 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2704 //ChangeState(terminated);
2708 void GdbThreadMain(char * output)
2711 Array<char *> outTokens { minAllocSize = 50 };
2712 Array<char *> subTokens { minAllocSize = 50 };
2713 DebugListItem item { };
2714 DebugListItem item2 { };
2715 bool setWaitingForPID = false;
2717 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
2718 #ifdef GDB_DEBUG_CONSOLE
2719 Log(output); Log("\n");
2721 #ifdef GDB_DEBUG_OUTPUT
2723 int len = strlen(output);
2731 for(c = 0; c < len / 1024; c++)
2733 strncpy(tmp, start, 1024);
2734 ide.outputView.gdbBox.Logf("out: %s\n", tmp);
2737 ide.outputView.gdbBox.Logf("out: %s\n", start);
2741 ide.outputView.gdbBox.Logf("out: %s\n", output);
2745 #ifdef GDB_DEBUG_CONSOLE
2746 strcpy(lastGdbOutput, output);
2748 #ifdef GDB_DEBUG_GUI
2749 if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
2756 if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
2759 ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
2765 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
2767 //if(outTokens.count == 1)
2772 ChangeState(loaded);
2773 targetProcessId = 0;
2774 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2776 if(!strcmp(item.name, "reason"))
2778 char * reason = item.value;
2779 StripQuotes(reason, reason);
2780 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
2783 if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
2785 StripQuotes(item2.value, item2.value);
2786 if(!strcmp(item2.name, "exit-code"))
2787 exitCode = item2.value;
2793 HandleExit(reason, exitCode);
2797 DebuggerProtocolUnknown("Unknown kill reply", item.name);
2800 HandleExit(null, null);
2803 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2805 if(!strcmp(item.name, "bkpt"))
2807 sentBreakInsert = false;
2810 printf("problem\n");
2812 bpItem = GdbDataBreakpoint { };
2813 item.value = StripCurlies(item.value);
2814 TokenizeList(item.value, ',', subTokens);
2815 for(i = 0; i < subTokens.count; i++)
2817 if(TokenizeListItem(subTokens[i], item))
2819 StripQuotes(item.value, item.value);
2820 if(!strcmp(item.name, "number"))
2821 bpItem.number = atoi(item.value);
2822 else if(!strcmp(item.name, "type"))
2823 bpItem.type = CopyString(item.value);
2824 else if(!strcmp(item.name, "disp"))
2825 bpItem.disp = CopyString(item.value);
2826 else if(!strcmp(item.name, "enabled"))
2827 bpItem.enabled = (!strcmpi(item.value, "y"));
2828 else if(!strcmp(item.name, "addr"))
2829 bpItem.addr = CopyString(item.value);
2830 else if(!strcmp(item.name, "func"))
2831 bpItem.func = CopyString(item.value);
2832 else if(!strcmp(item.name, "file"))
2833 bpItem.file = item.value;
2834 else if(!strcmp(item.name, "line"))
2835 bpItem.line = atoi(item.value);
2836 else if(!strcmp(item.name, "at"))
2837 bpItem.at = CopyString(item.value);
2838 else if(!strcmp(item.name, "times"))
2839 bpItem.times = atoi(item.value);
2842 //breakType = bpValidation;
2843 //app.SignalEvent();
2844 subTokens.RemoveAll();
2846 else if(!strcmp(item.name, "BreakpointTable"))
2847 ide.outputView.debugBox.Logf("Debugger Error: Command reply BreakpointTable not handled\n");
2848 else if(!strcmp(item.name, "depth"))
2850 StripQuotes(item.value, item.value);
2851 frameCount = atoi(item.value);
2853 stackFrames.Free(Frame::Free);
2855 else if(!strcmp(item.name, "stack"))
2858 if(stackFrames.count)
2859 ide.callStackView.Logf("...\n");
2862 item.value = StripBrackets(item.value);
2863 TokenizeList(item.value, ',', subTokens);
2864 for(i = 0; i < subTokens.count; i++)
2866 if(TokenizeListItem(subTokens[i], item))
2868 if(!strcmp(item.name, "frame"))
2871 stackFrames.Add(frame);
2872 item.value = StripCurlies(item.value);
2873 ParseFrame(frame, item.value);
2874 if(frame.file && frame.from)
2875 DebuggerProtocolUnknown("Unexpected frame file and from members present", "");
2879 if(activeFrameLevel == -1)
2881 if(ide.projectView.IsModuleInProject(frame.file));
2883 if(frame.level != 0)
2885 //stopItem.frame = frame;
2886 breakType = selectFrame;
2889 activeFrame = frame;
2890 activeFrameLevel = frame.level;
2893 ide.callStackView.Logf("%3d ", frame.level);
2894 if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
2895 ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
2896 else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
2897 ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
2898 else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
2899 ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
2900 else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
2901 ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
2903 ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
2908 ide.callStackView.Logf("%3d ", frame.level);
2913 ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
2917 ide.callStackView.Logf("%s\n", frame.func);
2919 ide.callStackView.Logf($"unknown source\n");
2923 DebuggerProtocolUnknown("Unknown stack content", item.name);
2926 if(activeFrameLevel == -1)
2928 activeFrameLevel = 0;
2929 activeFrame = stackFrames.first;
2931 ide.callStackView.Home();
2933 subTokens.RemoveAll();
2935 /*else if(!strcmp(item.name, "frame"))
2938 item.value = StripCurlies(item.value);
2939 ParseFrame(&frame, item.value);
2941 else if(!strcmp(item.name, "thread-ids"))
2943 ide.threadsView.Clear();
2944 item.value = StripCurlies(item.value);
2945 TokenizeList(item.value, ',', subTokens);
2946 for(i = subTokens.count - 1; ; i--)
2948 if(TokenizeListItem(subTokens[i], item))
2950 if(!strcmp(item.name, "thread-id"))
2953 StripQuotes(item.value, item.value);
2954 value = atoi(item.value);
2955 ide.threadsView.Logf("%3d \n", value);
2958 DebuggerProtocolUnknown("Unknown threads content", item.name);
2963 ide.threadsView.Home();
2965 subTokens.RemoveAll();
2966 //if(!strcmp(outTokens[2], "number-of-threads"))
2968 else if(!strcmp(item.name, "new-thread-id"))
2970 StripQuotes(item.value, item.value);
2971 activeThread = atoi(item.value);
2973 else if(!strcmp(item.name, "value"))
2975 StripQuotes(item.value, item.value);
2976 eval.result = CopyString(item.value);
2977 eval.active = false;
2979 else if(!strcmp(item.name, "addr"))
2981 for(i = 2; i < outTokens.count; i++)
2983 if(TokenizeListItem(outTokens[i], item))
2985 if(!strcmp(item.name, "total-bytes"))
2987 StripQuotes(item.value, item.value);
2988 eval.bytes = atoi(item.value);
2990 else if(!strcmp(item.name, "next-row"))
2992 StripQuotes(item.value, item.value);
2993 eval.nextBlockAddress = _strtoui64(item.value, null, 0);
2995 else if(!strcmp(item.name, "memory"))
2999 //StripQuotes(item.value, item.value);
3000 item.value = StripBrackets(item.value);
3001 // this should be treated as a list...
3002 item.value = StripCurlies(item.value);
3003 TokenizeList(item.value, ',', subTokens);
3004 for(j = 0; j < subTokens.count; j++)
3006 if(TokenizeListItem(subTokens[j], item))
3008 if(!strcmp(item.name, "data"))
3010 item.value = StripBrackets(item.value);
3011 StripQuotes2(item.value, item.value);
3012 eval.result = CopyString(item.value);
3013 eval.active = false;
3017 subTokens.RemoveAll();
3022 else if(!strcmp(item.name, "source-path"))
3026 DebuggerProtocolUnknown("Unknown command reply", item.name);
3029 else if(!strcmp(outTokens[0], "^running"))
3031 waitingForPID = true;
3032 setWaitingForPID = true;
3034 else if(!strcmp(outTokens[0], "^exit"))
3036 ChangeState(terminated);
3037 // ide.outputView.debugBox.Logf("Exit\n");
3038 // ide.Update(null);
3040 serialSemaphore.Release();
3042 else if(!strcmp(outTokens[0], "^error"))
3046 sentBreakInsert = false;
3047 breakpointError = true;
3050 printf("problem\n");
3052 bpItem = GdbDataBreakpoint { };
3055 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3057 if(!strcmp(item.name, "msg"))
3059 StripQuotes(item.value, item.value);
3062 eval.active = false;
3064 if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3065 eval.error = symbolNotFound;
3066 else if(strstr(item.value, "Cannot access memory at address"))
3067 eval.error = memoryCantBeRead;
3069 eval.error = unknown;
3071 else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3074 else if(!strncmp(item.value, "Cannot access memory at address", 31))
3077 else if(!strcmp(item.value, "Cannot find bounds of current function"))
3079 ChangeState(stopped);
3080 gdbHandle.Printf("-exec-continue\n");
3082 else if(!strcmp(item.value, "ptrace: No such process."))
3084 ChangeState(loaded);
3085 targetProcessId = 0;
3087 else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3090 else if(!strcmp(item.value, "You can't do that without a process to debug."))
3092 ChangeState(loaded);
3093 targetProcessId = 0;
3095 else if(strstr(item.value, "No such file or directory."))
3097 ChangeState(loaded);
3098 targetProcessId = 0;
3100 else if(strstr(item.value, "During startup program exited with code "))
3102 ChangeState(loaded);
3103 targetProcessId = 0;
3108 if(strlen(item.value) < MAX_F_STRING)
3111 ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3115 ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3121 DebuggerProtocolUnknown("Unknown error content", item.name);
3124 DebuggerProtocolUnknown("Unknown result-record", outTokens[0]);
3126 outTokens.RemoveAll();
3129 DebuggerProtocolUnknown("Unknown status-async-output", outTokens[0]);
3132 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "=thread-group-created")) //=thread-group-created,id="7611"
3134 else if(!strcmp(outTokens[0], "=thread-created")) //=thread-created,id="1",group-id="7611"
3136 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"
3139 DebuggerProtocolUnknown("Unknown notify-async-output", outTokens[0]);
3140 outTokens.RemoveAll();
3144 if(TokenizeList(output, ',', outTokens))
3146 if(!strcmp(outTokens[0],"*running"))
3148 waitingForPID = true;
3149 setWaitingForPID = true;
3151 else if(!strcmp(outTokens[0], "*stopped"))
3154 ChangeState(stopped);
3156 for(tk = 1; tk < outTokens.count; tk++)
3158 if(TokenizeListItem(outTokens[tk], item))
3160 if(!strcmp(item.name, "reason"))
3162 char * reason = item.value;
3163 StripQuotes(reason, reason);
3164 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3167 if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3170 StripQuotes(item2.value, item2.value);
3171 if(!strcmp(item2.name, "exit-code"))
3172 exitCode = item2.value;
3178 HandleExit(reason, exitCode);
3180 else if(!strcmp(reason, "breakpoint-hit"))
3184 printf("problem\n");
3186 stopItem = GdbDataStop { };
3188 for(i = tk+1; i < outTokens.count; i++)
3190 TokenizeListItem(outTokens[i], item);
3191 StripQuotes(item.value, item.value);
3192 if(!strcmp(item.name, "bkptno"))
3193 stopItem.bkptno = atoi(item.value);
3194 else if(!strcmp(item.name, "thread-id"))
3195 stopItem.threadid = atoi(item.value);
3196 else if(!strcmp(item.name, "frame"))
3198 item.value = StripCurlies(item.value);
3199 ParseFrame(stopItem.frame, item.value);
3202 DebuggerProtocolUnknown("Unknown breakpoint hit item name", item.name);
3207 else if(!strcmp(reason, "end-stepping-range"))
3211 printf("problem\n");
3213 stopItem = GdbDataStop { };
3215 for(i = tk+1; i < outTokens.count; i++)
3217 TokenizeListItem(outTokens[i], item);
3218 StripQuotes(item.value, item.value);
3219 if(!strcmp(item.name, "thread-id"))
3220 stopItem.threadid = atoi(item.value);
3221 else if(!strcmp(item.name, "frame"))
3223 item.value = StripCurlies(item.value);
3224 ParseFrame(stopItem.frame, item.value);
3226 else if(!strcmp(item.name, "reason"))
3228 else if(!strcmp(item.name, "bkptno"))
3231 DebuggerProtocolUnknown("Unknown end of stepping range item name", item.name);
3237 else if(!strcmp(reason, "function-finished"))
3241 printf("problem\n");
3243 stopItem = GdbDataStop { };
3244 stopItem.reason = CopyString(reason);
3246 for(i = tk+1; i < outTokens.count; i++)
3248 TokenizeListItem(outTokens[i], item);
3249 StripQuotes(item.value, item.value);
3250 if(!strcmp(item.name, "thread-id"))
3251 stopItem.threadid = atoi(item.value);
3252 else if(!strcmp(item.name, "frame"))
3254 item.value = StripCurlies(item.value);
3255 ParseFrame(stopItem.frame, item.value);
3257 else if(!strcmp(item.name, "gdb-result-var"))
3258 stopItem.gdbResultVar = CopyString(item.value);
3259 else if(!strcmp(item.name, "return-value"))
3260 stopItem.returnValue = CopyString(item.value);
3262 DebuggerProtocolUnknown("Unknown function finished item name", item.name);
3265 event = functionEnd;
3268 else if(!strcmp(reason, "signal-received"))
3272 printf("problem\n");
3274 stopItem = GdbDataStop { };
3275 stopItem.reason = CopyString(reason);
3277 for(i = tk+1; i < outTokens.count; i++)
3279 TokenizeListItem(outTokens[i], item);
3280 StripQuotes(item.value, item.value);
3281 if(!strcmp(item.name, "signal-name"))
3282 stopItem.name = CopyString(item.value);
3283 else if(!strcmp(item.name, "signal-meaning"))
3284 stopItem.meaning = CopyString(item.value);
3285 else if(!strcmp(item.name, "thread-id"))
3286 stopItem.threadid = atoi(item.value);
3287 else if(!strcmp(item.name, "frame"))
3289 item.value = StripCurlies(item.value);
3290 ParseFrame(stopItem.frame, item.value);
3293 DebuggerProtocolUnknown("Unknown signal reveived item name", item.name);
3295 if(!strcmp(stopItem.name, "SIGTRAP"))
3314 else if(!strcmp(reason, "watchpoint-trigger"))
3315 DebuggerProtocolUnknown("Reason watchpoint trigger not handled", "");
3316 else if(!strcmp(reason, "read-watchpoint-trigger"))
3317 DebuggerProtocolUnknown("Reason read watchpoint trigger not handled", "");
3318 else if(!strcmp(reason, "access-watchpoint-trigger"))
3319 DebuggerProtocolUnknown("Reason access watchpoint trigger not handled", "");
3320 else if(!strcmp(reason, "watchpoint-scope"))
3321 DebuggerProtocolUnknown("Reason watchpoint scope not handled", "");
3322 else if(!strcmp(reason, "location-reached"))
3323 DebuggerProtocolUnknown("Reason location reached not handled", "");
3325 DebuggerProtocolUnknown("Unknown reason", reason);
3333 DebuggerProtocolUnknown("Unknown exec-async-output", outTokens[0]);
3334 outTokens.RemoveAll();
3337 if(!strcmpi(output, "(gdb) "))
3341 char exeFile[MAX_LOCATION];
3342 int oldProcessID = targetProcessId;
3343 GetLastDirectory(targetFile, exeFile);
3347 targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3348 if(targetProcessId || gdbHandle.Peek()) break;
3353 ChangeState(running);
3354 else if(!oldProcessID)
3356 ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3357 // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3358 gdbHandle.Printf("-gdb-exit\n");
3360 ChangeState(terminated); //loaded;
3365 for(bp : ide.workspace.breakpoints)
3366 bp.inserted = false;
3369 bp.inserted = false;
3371 bpRunToCursor.inserted = false;
3373 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3374 ClearBreakDisplay();
3376 #if defined(__unix__)
3377 if(FileExists(progFifoPath)) //fileCreated)
3379 progThread.terminate = true;
3382 fifoFile.CloseInput();
3389 DeleteFile(progFifoPath);
3390 progFifoPath[0] = '\0';
3397 serialSemaphore.Release();
3400 DebuggerProtocolUnknown($"Unknown prompt", output);
3404 if(!strncmp(output, "&\"warning:", 10))
3407 content = strstr(output, "\"");
3408 StripQuotes(content, content);
3409 content = strstr(content, ":");
3415 ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3422 DebuggerProtocolUnknown($"Unknown output", output);
3424 if(!setWaitingForPID)
3425 waitingForPID = false;
3426 setWaitingForPID = false;
3434 void RunToCursorPrepare(char * absoluteFilePath, char * relativeFilePath, int lineNumber)
3438 //bpRunToCursor.Free();
3439 bpRunToCursor = Breakpoint { };
3442 bpRunToCursor = Breakpoint { };
3444 if(absoluteFilePath)
3445 bpRunToCursor.absoluteFilePath = CopyString(absoluteFilePath);
3446 if(relativeFilePath)
3447 bpRunToCursor.relativeFilePath = CopyString(relativeFilePath);
3448 bpRunToCursor.line = lineNumber;
3449 bpRunToCursor.type = runToCursor;
3450 bpRunToCursor.enabled = true;
3451 bpRunToCursor.condition = null;
3452 bpRunToCursor.ignore = 0;
3453 bpRunToCursor.level = -1;
3456 ExpressionType ::DebugEvalExpTypeError(char * result)
3462 case symbolNotFound:
3463 return symbolErrorExp;
3464 case memoryCantBeRead:
3465 return memoryErrorExp;
3467 return unknownErrorExp;
3470 char * ::EvaluateExpression(char * expression, ExpressionType * error)
3473 if(ide.projectView && ide.debugger.state == stopped)
3475 result = GdbEvaluateExpression(expression);
3476 *error = DebugEvalExpTypeError(result);
3481 *error = noDebuggerErrorExp;
3486 char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
3489 char * result = GdbReadMemoryString(address, size, format, 1, 1);
3490 if(!result || !strcmp(result, "N/A"))
3491 *error = memoryErrorExp;
3493 *error = DebugEvalExpTypeError(result);
3498 class GdbThread : Thread
3504 static char output[4096];
3505 Array<char> dynamicBuffer { minAllocSize = 4096 };
3506 DualPipe oldGdbHandle = gdbHandle;
3507 incref oldGdbHandle;
3510 while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
3514 result = gdbHandle.Read(output, 1, sizeof(output));
3516 if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
3523 for(c = 0; c<result; c++)
3525 if(output[c] == '\n')
3527 int pos = dynamicBuffer.size;
3528 dynamicBuffer.size += c - start;
3529 memcpy(&dynamicBuffer[pos], output + start, c - start);
3530 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3531 // COMMENTED OUT DUE TO ISSUE #135, FIXED
3532 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
3533 dynamicBuffer.size++;
3534 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
3536 // printf("%s\n", dynamicBuffer.array);
3538 debugger.GdbThreadMain(&dynamicBuffer[0]);
3539 dynamicBuffer.size = 0;
3545 int pos = dynamicBuffer.size;
3546 dynamicBuffer.size += c - start;
3547 memcpy(&dynamicBuffer[pos], output + start, c - start);
3553 printf("Got end of file from GDB!\n");
3557 delete dynamicBuffer;
3558 //if(oldGdbHandle == gdbHandle)
3559 debugger.GdbThreadExit();
3560 delete oldGdbHandle;
3566 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
3567 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
3569 #if defined(__unix__)
3574 #include <sys/types.h>
3579 class ProgramThread : Thread
3585 bool fileCreated = false;
3587 static char output[1000];
3590 /*if(!mkfifo(progFifoPath, mask))
3597 ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
3601 if(FileExists(progFifoPath)) //fileCreated)
3603 fifoFile = FileOpen(progFifoPath, read);
3607 ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
3612 fd = fileno((FILE *)fifoFile.input);
3613 //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
3617 while(!terminate && fifoFile && !fifoFile.Eof())
3620 struct timeval time;
3628 selectResult = select(fd + 1, &rs, null, null, &time);
3629 if(FD_ISSET(fd, &rs))
3631 int result = read(fd, output, sizeof(output)-1);
3632 if(!result || (result < 0 && errno != EAGAIN))
3636 output[result] = '\0';
3637 if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
3640 ide.outputView.debugBox.Log(output);
3649 //fifoFile.CloseInput();
3652 ide.outputView.debugBox.Log("\n");
3656 if(FileExists(progFifoPath)) //fileCreated)
3658 DeleteFile(progFifoPath);
3659 progFifoPath[0] = '\0';
3667 class Argument : struct
3669 Argument prev, next;
3685 class Frame : struct
3694 property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
3696 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3697 char * absoluteFile;
3706 delete absoluteFile;
3707 args.Free(Argument::Free);
3716 class GdbDataStop : struct
3733 char * gdbResultVar;
3743 if(!strcmp(reason, "signal-received"))
3748 else if(!strcmp(reason, "function-finished"))
3750 delete gdbResultVar;
3755 if(frame) frame.Free();
3764 class GdbDataBreakpoint : struct
3773 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3788 ~GdbDataBreakpoint()
3794 class Breakpoint : struct
3798 char * relativeFilePath;
3799 char * absoluteFilePath;
3808 BreakpointType type;
3811 GdbDataBreakpoint bp;
3813 char * LocationToString()
3815 char location[MAX_LOCATION+20];
3816 snprintf(location, sizeof(location), "%s:%d", relativeFilePath, line);
3817 location[sizeof(location)-1] = 0;
3818 #if defined(__WIN32__)
3819 ChangeCh(location, '/', '\\');
3821 return CopyString(location);
3826 if(relativeFilePath && relativeFilePath[0])
3828 f.Printf(" * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
3830 f.Printf(" ~ %s\n", condition.expression);
3839 delete relativeFilePath;
3840 delete absoluteFilePath;
3850 class Watch : struct
3861 f.Printf(" ~ %s\n", expression);
3885 class DebugListItem : struct
3891 struct DebugEvaluationData
3896 uint64 nextBlockAddress;
3898 DebuggerEvaluationError error;
3901 class CodeLocation : struct
3904 char * absoluteFile;
3907 CodeLocation ::ParseCodeLocation(char * location)
3911 char * colon = null;
3913 char loc[MAX_LOCATION];
3914 strcpy(loc, location);
3915 for(temp = loc; temp = strstr(temp, ":"); temp++)
3923 int line = atoi(colon);
3926 CodeLocation codloc { line = line };
3927 codloc.file = CopyString(loc);
3928 codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
3940 delete absoluteFile;