7 #define GDB_DEBUG_CONSOLE
10 extern char * strrchr(const char * s, int c);
13 #define strlen _strlen
23 #include <sys/time.h> // Required on Apple...
28 public char * StripQuotes2(char * string, char * output)
32 bool quoted = false, escaped = false;
34 for(c = 0; ch = string[c]; c++)
38 if(escaped || ch != '\"')
41 escaped = !escaped && ch == '\\';
56 static void strescpy(char * d, char * s)
109 static char * CopyUnescapedSystemPath(char * p)
111 char * d = new char[strlen(p) + 1];
113 #if defined(__WIN32__)
114 ChangeCh(d, '/', '\\');
119 static char * CopyUnescapedUnixPath(char * p)
121 char * d = new char[strlen(p) + 1];
123 #if defined(__WIN32__)
124 ChangeCh(d, '\\', '/');
129 static char * CopyUnescapedString(char * s)
131 char * d = new char[strlen(s) + 1];
136 // String Unescape Copy
138 // TOFIX: THIS DOESN'T HANDLE NUMERIC ESCAPE CODES (OCTAL/HEXADECIMAL...)?
139 // Seems very similar to ReadString in pass15.ec (which also misses numeric escape codes :) )
141 static void struscpy(char * d, char * s)
193 static char * StripBrackets(char * string)
195 int length = strlen(string);
196 if(length > 1 && *string == '[' && string[length - 1] == ']')
199 string[length - 1] = '\0';
206 static char * StripCurlies(char * string)
208 int length = strlen(string);
209 if(length > 1 && *string == '{' && string[length - 1] == '}')
212 string[length - 1] = '\0';
219 static int StringGetInt(char * string, int start)
222 int i, len = strlen(string);
224 for(i = start; i < len && i < start + 8; i++)
226 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')
227 strncat(number, &string[i], 1);
234 static int TokenizeList(char * string, const char seperator, Array<char *> tokens)
238 bool quoted = false, escaped = false;
239 char * start = string, ch;
241 for(; (ch = *string); string++)
248 if(escaped || ch != '\"')
249 escaped = !escaped && ch == '\\';
255 else if(ch == '{' || ch == '[' || ch == '(' || ch == '<')
257 else if(ch == '}' || ch == ']' || ch == ')' || ch == '>')
259 else if(ch == seperator && !level)
268 //tokens[count] = start;
269 //tokens[count++] = start;
276 static bool TokenizeListItem(char * string, DebugListItem item)
278 char * equal = strstr(string, "=");
292 static void DebuggerProtocolUnknown(char * message, char * gdbOutput)
294 #ifdef _DEBUG_GDB_PROTOCOL
295 ide.outputView.debugBox.Logf("Debugger Protocol Error: %s (%s)\n", message, gdbOutput);
299 // define GdbGetLineSize = 1638400;
300 define GdbGetLineSize = 5638400;
301 #if defined(__unix__)
302 char progFifoPath[MAX_LOCATION];
303 char progFifoDir[MAX_LOCATION];
306 enum DebuggerState { none, prompt, loaded, running, stopped, terminated };
307 enum DebuggerEvent { none, hit, breakEvent, signal, stepEnd, functionEnd, exit };
308 enum DebuggerAction { none, internal, restart, stop, selectFrame }; //, bpValidation
309 enum BreakpointType { none, internalMain, internalWinMain, internalModulesLoaded, user, runToCursor, internalModuleLoad };
310 enum DebuggerEvaluationError { none, symbolNotFound, memoryCantBeRead, unknown };
312 FileDialog debuggerFileDialog { type = selectDir };
314 static DualPipe gdbHandle;
315 static DebugEvaluationData eval { };
317 static int targetProcessId;
319 static bool gdbReady;
320 static bool breakpointError;
324 Semaphore serialSemaphore { };
329 //bool breakpointsInserted;
331 bool sentBreakInsert;
332 bool ignoreBreakpoints;
333 bool userBreakOnInternBreak;
340 int activeFrameLevel;
351 DebuggerAction breakType;
352 //DebuggerCommand lastCommand; // THE COMPILER COMPILES STUFF THAT DOES NOT EXIST???
354 GdbDataStop stopItem;
355 GdbDataBreakpoint bpItem;
358 List<Breakpoint> sysBPs { };
359 Breakpoint bpRunToCursor;
365 CompilerConfig currentCompiler;
366 ProjectConfig prjConfig;
369 CodeEditor codeEditor;
371 GdbThread gdbThread { debugger = this };
374 delay = 0.0, userData = this;
378 bool monitor = false;
379 DebuggerEvent curEvent = event;
380 GdbDataStop stopItem = this.stopItem;
386 this.stopItem = null;
389 if(curEvent && curEvent != exit)
392 printf("No stop item\n");
400 Restart(currentCompiler, prjConfig, bitDepth);
409 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
410 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
411 if(activeFrame.level == activeFrameLevel)
417 // GdbCommand(false, "-break-info %d", bpItem.number);
427 activeThread = stopItem.threadid;
428 GdbCommand(false, "-thread-list-ids");
433 Breakpoint bp = null;
435 for(i : ide.workspace.breakpoints; i.bp && i.bp.number == stopItem.bkptno)
442 for(i : sysBPs; i.bp && i.bp.number == stopItem.bkptno)
448 if(bp && bp.type != user && stopItem && stopItem.frame)
450 // In case the user put a breakpoint where an internal breakpoint is, avoid the confusion...
451 for(i : ide.workspace.breakpoints)
453 if(i.bp && i.line == stopItem.frame.line && !fstrcmp(i.absoluteFilePath, stopItem.frame.absoluteFile))
460 if(!(!userBreakOnInternBreak &&
461 bp && (bp.type == internalMain || bp.type == internalWinMain ||
462 bp.type == internalModulesLoaded || bp.type == internalModuleLoad)))
464 hitThread = stopItem.threadid;
468 signalThread = stopItem.threadid;
480 activeThread = stopItem.threadid;
481 GdbCommand(false, "-thread-list-ids");
483 if(activeFrameLevel > 0)
484 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
486 WatchesCodeEditorLinkInit();
496 ide.outputView.debugBox.Logf($"Signal received: %s - %s\n", stopItem.name, stopItem.meaning);
497 ide.outputView.debugBox.Logf(" %s:%d\n", (s = CopySystemPath(stopItem.frame.file)), stopItem.frame.line);
503 // Why was SelectFrame missing here?
504 SelectFrame(activeFrameLevel);
505 GoToStackFrameLine(activeFrameLevel, curEvent == signal || curEvent == stepEnd /*false*/);
506 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
508 if(curEvent == signal)
509 ide.outputView.Show();
510 if(curEvent == signal || curEvent == breakEvent)
512 if(curEvent == breakEvent)
513 ide.threadsView.Show();
514 ide.callStackView.Show();
516 ide.ShowCodeEditor();
517 if(curEvent == breakEvent)
518 ide.callStackView.Activate();
526 ignoreBreakpoints = false;
537 #ifdef GDB_DEBUG_CONSOLE
538 char lastGdbOutput[GdbGetLineSize];
540 #if defined(__unix__)
541 ProgramThread progThread { };
544 void ChangeState(DebuggerState value)
546 bool same = value == state;
547 // if(same) PrintLn("Debugger::ChangeState -- changing to same state");
549 if(!same && ide) ide.AdjustDebugMenus();
554 // Stop(); // Don't need to call Stop here, because ~ProjectView() will call it explicitly.
556 stackFrames.Free(Frame::Free);
566 waitingForPID = false;
571 sentBreakInsert = false;
572 ignoreBreakpoints = false;
573 userBreakOnInternBreak = false;
576 activeFrameLevel = 0;
593 bpRunToCursor = null;
596 delete currentCompiler;
600 /*GdbThread gdbThread
606 ideProcessId = Process_GetCurrentProcessId();
608 sysBPs.Add(Breakpoint { type = internalMain, enabled = true, level = -1 });
609 #if defined(__WIN32__)
610 sysBPs.Add(Breakpoint { type = internalWinMain, enabled = true, level = -1 });
612 sysBPs.Add(Breakpoint { type = internalModulesLoaded, enabled = true, level = -1 });
613 sysBPs.Add(Breakpoint { type = internalModuleLoad, enabled = true, level = -1 });
625 property bool isActive { get { return state == running || state == stopped; } }
626 property bool isPrepared { get { return state == loaded || state == running || state == stopped; } }
630 GdbExecContinue(true);
638 GdbDebugBreak(false);
650 GdbDebugBreak(false);
661 void Restart(CompilerConfig compiler, ProjectConfig config, int bitDepth)
669 GdbDebugBreak(false);
676 if(!GdbInit(compiler, config, bitDepth))
684 bool GoToCodeLine(char * location)
687 codloc = CodeLocation::ParseCodeLocation(location);
690 CodeEditor editor = (CodeEditor)ide.OpenFile(codloc.absoluteFile, normal, true, null, no, normal, false);
693 EditBox editBox = editor.editBox;
694 editBox.GoToLineNum(codloc.line - 1);
695 editBox.GoToPosition(editBox.line, codloc.line - 1, 0);
702 bool GoToStackFrameLine(int stackLevel, bool askForLocation)
706 char filePath[MAX_LOCATION];
707 char sourceDir[MAX_LOCATION];
709 CodeEditor editor = null;
710 if(stackLevel == -1) // this (the two lines) is part of that fix that I would not put in for some time
712 for(frame = stackFrames.first; frame; frame = frame.next)
713 if(frame.level == stackLevel)
717 ide.callStackView.Show();
719 if(!frame.absoluteFile && frame.file)
720 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
721 if(!frame.absoluteFile && askForLocation && frame.file)
724 char title[MAX_LOCATION];
725 snprintf(title, sizeof(title), $"Provide source file location for %s", (s = CopySystemPath(frame.file)));
726 title[sizeof(title)-1] = 0;
728 if(SourceDirDialog(title, ide.workspace.projectDir, frame.file, sourceDir))
730 AddSourceDir(sourceDir);
731 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
734 if(frame.absoluteFile)
735 editor = (CodeEditor)ide.OpenFile(frame.absoluteFile, normal, true, null, no, normal, false);
737 if(editor && frame.line)
739 EditBox editBox = editor.editBox;
740 editBox.GoToLineNum(frame.line - 1);
741 editBox.GoToPosition(editBox.line, frame.line - 1, 0);
749 void SelectThread(int thread)
753 if(thread != activeThread)
755 activeFrameLevel = -1;
756 ide.callStackView.Clear();
757 GdbCommand(false, "-thread-select %d", thread);
759 // Why was SelectFrame missing here?
760 SelectFrame(activeFrameLevel);
761 GoToStackFrameLine(activeFrameLevel, true);
762 WatchesCodeEditorLinkRelease();
763 WatchesCodeEditorLinkInit();
767 ide.callStackView.Show();
771 void SelectFrame(int frame)
775 if(frame != activeFrameLevel || !codeEditor || !codeEditor.visible)
777 activeFrameLevel = frame; // there is no active frame number in the gdb reply
778 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
779 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
780 if(activeFrame.level == activeFrameLevel)
783 WatchesCodeEditorLinkRelease();
784 WatchesCodeEditorLinkInit();
791 void HandleExit(char * reason, char * code)
793 bool returnedExitCode = false;
794 char verboseExitCode[128];
796 ChangeState(loaded); // this state change seems to be superfluous, might be in case of gdb crash
801 snprintf(verboseExitCode, sizeof(verboseExitCode), $" with exit code %s", code);
802 verboseExitCode[sizeof(verboseExitCode)-1] = 0;
805 verboseExitCode[0] = '\0';
809 // ClearBreakDisplay();
813 for(wh : ide.workspace.watches)
815 if(wh.type) FreeType(wh.type);
818 ide.watchesView.UpdateWatch(wh);
822 #if defined(__unix__)
823 progThread.terminate = true;
826 fifoFile.CloseInput();
835 char program[MAX_LOCATION];
836 GetSystemPathBuffer(program, targetFile);
838 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
839 else if(!strcmp(reason, "exited-normally"))
840 ide.outputView.debugBox.Logf($"The program %s has exited normally%s.\n", program, verboseExitCode);
841 else if(!strcmp(reason, "exited"))
842 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
843 else if(!strcmp(reason, "exited-signalled"))
844 ide.outputView.debugBox.Logf($"The program %s has exited with a signal%s.\n", program, verboseExitCode);
846 ide.outputView.debugBox.Logf($"The program %s has exited (gdb provided an unknown reason)%s.\n", program, verboseExitCode);
851 void Start(CompilerConfig compiler, ProjectConfig config, int bitDepth)
853 ide.outputView.debugBox.Clear();
858 if(!GdbInit(compiler, config, bitDepth))
866 void StepInto(CompilerConfig compiler, ProjectConfig config, int bitDepth)
872 if(!GdbInit(compiler, config, bitDepth))
875 ide.outputView.ShowClearSelectTab(debug);
876 ide.outputView.debugBox.Logf($"Starting debug mode\n");
877 userBreakOnInternBreak = true;
886 void StepOver(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool ignoreBkpts)
892 if(!GdbInit(compiler, config, bitDepth))
895 ide.outputView.ShowClearSelectTab(debug);
896 ide.outputView.debugBox.Logf($"Starting debug mode\n");
897 ignoreBreakpoints = ignoreBkpts;
898 userBreakOnInternBreak = true;
902 ignoreBreakpoints = ignoreBkpts;
903 if(ignoreBreakpoints)
904 GdbBreakpointsDelete(true);
910 void StepOut(bool ignoreBkpts)
914 ignoreBreakpoints = ignoreBkpts;
915 if(ignoreBreakpoints)
916 GdbBreakpointsDelete(true);
921 void RunToCursor(CompilerConfig compiler, ProjectConfig config, int bitDepth, char * absoluteFilePath, int lineNumber, bool ignoreBkpts, bool atSameLevel)
923 char relativeFilePath[MAX_LOCATION];
924 DebuggerState oldState = state;
925 ignoreBreakpoints = ignoreBkpts;
926 if(!ide.projectView.GetRelativePath(absoluteFilePath, relativeFilePath))
927 strcpy(relativeFilePath, absoluteFilePath);
932 Start(compiler, config, bitDepth);
939 ide.outputView.ShowClearSelectTab(debug);
940 ide.outputView.debugBox.Logf($"Starting debug mode\n");
942 RunToCursorPrepare(absoluteFilePath, relativeFilePath, lineNumber, atSameLevel);
943 sentBreakInsert = true;
944 GdbCommand(false, "-break-insert %s:%d", relativeFilePath, lineNumber);
945 bpRunToCursor.bp = bpItem;
947 bpRunToCursor.inserted = (bpRunToCursor.bp.number != 0);
948 ValidateBreakpoint(bpRunToCursor);
955 if(ignoreBreakpoints)
956 GdbBreakpointsDelete(false);
960 if(ignoreBreakpoints)
961 GdbBreakpointsDelete(false);
962 GdbExecContinue(true);
968 void GetCallStackCursorLine(bool * error, int * lineCursor, int * lineTopFrame)
970 if(activeFrameLevel == -1)
978 *error = signalOn && activeThread == signalThread;
979 *lineCursor = activeFrameLevel - ((frameCount > 192 && activeFrameLevel > 191) ? frameCount - 192 - 1 : 0) + 1;
980 *lineTopFrame = activeFrameLevel ? 1 : 0;
984 int GetMarginIconsLineNumbers(char * fileName, int lines[], bool enabled[], int max, bool * error, int * lineCursor, int * lineTopFrame)
986 char winFilePath[MAX_LOCATION];
987 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
989 Iterator<Breakpoint> it { ide.workspace.breakpoints };
990 while(it.Next() && count < max)
992 Breakpoint bp = it.data;
995 if(bp.absoluteFilePath && bp.absoluteFilePath[0] && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
997 lines[count] = bp.line;
998 enabled[count] = bp.enabled;
1003 if(activeFrameLevel == -1)
1011 *error = signalOn && activeThread == signalThread;
1012 if(activeFrame && activeFrame.absoluteFile && !fstrcmp(absoluteFilePath, activeFrame.absoluteFile))
1013 *lineCursor = activeFrame.line;
1016 if(activeFrame && stopItem && stopItem.frame && activeFrame.level == stopItem.frame.level)
1018 else if(stopItem && stopItem.frame && stopItem.frame.absoluteFile && !fstrcmp(absoluteFilePath, stopItem.frame.absoluteFile))
1019 *lineTopFrame = stopItem.frame.line;
1023 if(*lineTopFrame == *lineCursor && *lineTopFrame)
1029 void ChangeWatch(DataRow row, char * expression)
1031 Watch wh = (Watch)row.tag;
1034 delete wh.expression;
1036 wh.expression = CopyString(expression);
1039 Iterator<Watch> it { ide.workspace.watches };
1041 ide.workspace.watches.Delete(it.pointer);
1047 row.tag = (int64)wh;
1048 ide.workspace.watches.Add(wh);
1050 wh.expression = CopyString(expression);
1052 ide.workspace.Save();
1053 //if(expression && state == stopped)
1058 void MoveIcons(char * fileName, int lineNumber, int move, bool start)
1060 char winFilePath[MAX_LOCATION];
1061 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1064 for(bpLink = ide.workspace.breakpoints.first; bpLink; bpLink = next)
1066 Breakpoint bp = (Breakpoint)bpLink.data;
1069 if(bp.type == user && bp.absoluteFilePath && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1071 if(bp.line > lineNumber || (bp.line == lineNumber && start))
1073 if(move < 0 && (bp.line < lineNumber - move))
1074 ide.workspace.RemoveBreakpoint(bp);
1078 ide.breakpointsView.UpdateBreakpoint(bp.row);
1079 ide.workspace.Save();
1085 // moving code cursors is futile, on next step, stop, hit, cursors will be offset anyways
1088 bool SourceDirDialog(char * title, char * startDir, char * test, char * sourceDir)
1092 String srcDir = null;
1094 debuggerFileDialog.text = title;
1095 debuggerFileDialog.currentDirectory = startDir;
1096 debuggerFileDialog.master = ide;
1098 while(debuggerFileDialog.Modal())
1100 strcpy(sourceDir, debuggerFileDialog.filePath);
1101 if(!fstrcmp(ide.workspace.projectDir, sourceDir) &&
1102 MessageBox { type = yesNo, master = ide,
1103 contents = $"This is the project directory.\nWould you like to try again?",
1104 text = $"Invalid Source Directory" }.Modal() == no)
1108 for(dir : ide.workspace.sourceDirs)
1110 if(!fstrcmp(dir, sourceDir))
1118 MessageBox { type = yesNo, master = ide,
1119 contents = $"This source directory is already specified.\nWould you like to try again?",
1120 text = $"Invalid Source Directory" }.Modal() == no)
1126 char file[MAX_LOCATION];
1127 strcpy(file, sourceDir);
1128 PathCat(file, test);
1129 result = FileExists(file);
1131 MessageBox { type = yesNo, master = ide,
1132 contents = $"Unable to locate source file.\nWould you like to try again?",
1133 text = $"Invalid Source Directory" }.Modal() == no)
1147 void AddSourceDir(char * sourceDir)
1149 ide.workspace.sourceDirs.Add(CopyString(sourceDir));
1150 ide.workspace.Save();
1154 DebuggerState oldState = state;
1159 GdbDebugBreak(true);
1162 GdbCommand(false, "-environment-directory \"%s\"", sourceDir);
1165 if(oldState == running)
1166 GdbExecContinue(false);
1170 void ToggleBreakpoint(char * fileName, int lineNumber, Project prj)
1172 char winFilePath[MAX_LOCATION];
1173 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1174 char absolutePath[MAX_LOCATION];
1175 char relativePath[MAX_LOCATION];
1176 char sourceDir[MAX_LOCATION];
1177 Breakpoint bp = null;
1179 strcpy(absolutePath, absoluteFilePath);
1180 for(i : ide.workspace.breakpoints; i.type == user && i.absoluteFilePath && !fstrcmp(i.absoluteFilePath, absolutePath) && i.line == lineNumber)
1189 ide.workspace.RemoveBreakpoint(bp);
1197 // FIXED: This is how it should have been... Source locations are only for files not in project
1198 // if(IsPathInsideOf(absolutePath, ide.workspace.projectDir))
1199 // MakePathRelative(absolutePath, ide.workspace.projectDir, relativePath);
1200 bool result = false;
1202 result = prj.GetRelativePath(absolutePath, relativePath);
1204 ide.projectView.GetRelativePath(absolutePath, relativePath);
1205 //if(ide.projectView.GetRelativePath(absolutePath, relativePath));
1209 char title[MAX_LOCATION];
1210 char directory[MAX_LOCATION];
1211 StripLastDirectory(absolutePath, directory);
1212 snprintf(title, sizeof(title), $"Provide source files location directory for %s", absolutePath);
1213 title[sizeof(title)-1] = 0;
1216 String srcDir = null;
1217 for(dir : ide.workspace.sourceDirs)
1219 if(IsPathInsideOf(absolutePath, dir))
1221 MakePathRelative(absoluteFilePath, dir, relativePath);
1229 if(SourceDirDialog(title, directory, null, sourceDir))
1231 if(IsPathInsideOf(absolutePath, sourceDir))
1233 AddSourceDir(sourceDir);
1234 MakePathRelative(absoluteFilePath, sourceDir, relativePath);
1237 else if(MessageBox { type = yesNo, master = ide,
1238 contents = $"You must provide a valid source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1239 text = $"Invalid Source Directory" }.Modal() == no)
1242 else if(MessageBox { type = yesNo, master = ide,
1243 contents = $"You must provide a source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1244 text = $"No Source Directory Provided" }.Modal() == no)
1248 ide.workspace.bpCount++;
1249 bp = { line = lineNumber, type = user, enabled = true, level = -1 };
1250 ide.workspace.breakpoints.Add(bp);
1251 bp.absoluteFilePath = CopyString(absolutePath);
1252 bp.relativeFilePath = CopyString(relativePath);
1253 ide.breakpointsView.AddBreakpoint(bp);
1258 DebuggerState oldState = state;
1263 GdbDebugBreak(true);
1268 sentBreakInsert = true;
1269 GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, bp.line);
1272 bp.inserted = (bp.bp && bp.bp.number != 0);
1273 ValidateBreakpoint(bp);
1277 if(oldState == running)
1278 GdbExecContinue(false);
1281 ide.workspace.Save();
1284 void UpdateRemovedBreakpoint(Breakpoint bp)
1286 if(targeted && bp.inserted)
1288 DebuggerState oldState = state;
1293 GdbDebugBreak(true);
1297 GdbCommand(false, "-break-delete %d", bp.bp.number);
1300 if(oldState == running)
1301 GdbExecContinue(false);
1307 void ParseFrame(Frame frame, char * string)
1310 Array<char *> frameTokens { minAllocSize = 50 };
1311 Array<char *> argsTokens { minAllocSize = 50 };
1312 Array<char *> argumentTokens { minAllocSize = 50 };
1313 DebugListItem item { };
1316 TokenizeList(string, ',', frameTokens);
1317 for(i = 0; i < frameTokens.count; i++)
1319 if(TokenizeListItem(frameTokens[i], item))
1321 StripQuotes(item.value, item.value);
1322 if(!strcmp(item.name, "level"))
1323 frame.level = atoi(item.value);
1324 else if(!strcmp(item.name, "addr"))
1325 frame.addr = CopyString(item.value);
1326 else if(!strcmp(item.name, "func"))
1327 frame.func = CopyString(item.value);
1328 else if(!strcmp(item.name, "args"))
1330 if(!strcmp(item.value, "[]"))
1331 frame.argsCount = 0;
1334 item.value = StripBrackets(item.value);
1335 TokenizeList(item.value, ',', argsTokens);
1336 for(j = 0; j < argsTokens.count; j++)
1338 argsTokens[j] = StripCurlies(argsTokens[j]);
1339 TokenizeList(argsTokens[j], ',', argumentTokens);
1340 for(k = 0; k < argumentTokens.count; k++)
1343 frame.args.Add(arg);
1344 if(TokenizeListItem(argumentTokens[k], item))
1346 if(!strcmp(item.name, "name"))
1348 StripQuotes(item.value, item.value);
1349 arg.name = CopyString(item.value);
1351 else if(!strcmp(item.name, "value"))
1353 StripQuotes(item.value, item.value);
1354 arg.value = CopyString(item.value);
1357 DebuggerProtocolUnknown("Unknown frame args item name", item.name);
1360 DebuggerProtocolUnknown("Bad frame args item", "");
1362 argumentTokens.RemoveAll();
1364 frame.argsCount = argsTokens.count;
1365 argsTokens.RemoveAll();
1368 else if(!strcmp(item.name, "from"))
1369 frame.from = item.value;
1370 else if(!strcmp(item.name, "file"))
1372 frame.file = item.value;
1373 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1375 else if(!strcmp(item.name, "line"))
1376 frame.line = atoi(item.value);
1377 else if(!strcmp(item.name, "fullname"))
1379 // GDB 6.3 on OS X is giving "fullname" and "dir", all in absolute, but file name only in 'file'
1380 String path = ide.workspace.GetPathWorkspaceRelativeOrAbsolute(item.value);
1381 if(strcmp(frame.file, path))
1384 delete frame.absoluteFile;
1385 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1390 DebuggerProtocolUnknown("Unknown frame member name", item.name);
1393 DebuggerProtocolUnknown("Bad frame", "");
1398 delete argumentTokens;
1402 void ShowDebuggerViews()
1404 ide.outputView.Show();
1405 ide.outputView.SelectTab(debug);
1406 ide.threadsView.Show();
1407 ide.callStackView.Show();
1408 ide.watchesView.Show();
1412 void HideDebuggerViews()
1414 ide.RepositionWindows(true);
1417 void ::GdbCommand(bool focus, char * format, ...)
1421 // TODO: Improve this limit
1422 static char string[MAX_F_STRING*4];
1424 va_start(args, format);
1425 vsnprintf(string, sizeof(string), format, args);
1426 string[sizeof(string)-1] = 0;
1430 ide.debugger.serialSemaphore.TryWait();
1433 #ifdef GDB_DEBUG_CONSOLE
1434 Log(string); Log("\n");
1436 #ifdef GDB_DEBUG_OUTPUT
1437 ide.outputView.gdbBox.Logf("cmd: %s\n", string);
1439 #ifdef GDB_DEBUG_GUI
1441 ide.gdbDialog.AddCommand(string);
1443 strcat(string,"\n");
1444 gdbHandle.Puts(string);
1447 Process_ShowWindows(targetProcessId);
1450 ide.debugger.serialSemaphore.Wait();
1455 bool ValidateBreakpoint(Breakpoint bp)
1457 if(modules && bp.bp)
1459 if(bp.bp.line != bp.line)
1464 ide.outputView.debugBox.Logf("WOULD HAVE -- Invalid breakpoint disabled: %s:%d\n", bp.relativeFilePath, bp.line);
1468 //GdbCommand(false, "-break-delete %d", bp.bp.number);
1469 //bp.inserted = false;
1471 //bp.enabled = false;
1476 ide.outputView.debugBox.Logf("Debugger Error: ValidateBreakpoint error\n");
1477 bp.line = bp.bp.line;
1484 static void GdbInsertInternalBreakpoints()
1488 //if(!breakpointsInserted)
1490 DirExpression objDir = ide.project.GetObjDir(currentCompiler, prjConfig, bitDepth);
1495 if(bp.type == internalMain)
1497 sentBreakInsert = true;
1498 GdbCommand(false, "-break-insert main");
1501 bp.inserted = (bp.bp && bp.bp.number != 0);
1503 #if defined(__WIN32__)
1504 else if(bp.type == internalWinMain)
1506 sentBreakInsert = true;
1507 GdbCommand(false, "-break-insert WinMain");
1510 bp.inserted = (bp.bp && bp.bp.number != 0);
1513 else if(bp.type == internalModulesLoaded)
1515 char path[MAX_LOCATION];
1516 char name[MAX_LOCATION];
1517 char fixedModuleName[MAX_FILENAME];
1520 bool moduleLoadBlock = false;
1522 ReplaceSpaces(fixedModuleName, ide.project.moduleName);
1523 snprintf(name, sizeof(name),"%s.main.ec", fixedModuleName);
1524 name[sizeof(name)-1] = 0;
1525 strcpy(path, ide.workspace.projectDir);
1526 PathCatSlash(path, objDir.dir);
1527 PathCatSlash(path, name);
1528 f = FileOpen(path, read);
1531 for(lineNumber = 1; !f.Eof(); lineNumber++)
1533 if(f.GetLine(line, sizeof(line) - 1))
1535 bool moduleLoadLine;
1536 TrimLSpaces(line, line);
1537 moduleLoadLine = !strncmp(line, "eModule_Load", strlen("eModule_Load"));
1538 if(!moduleLoadBlock && moduleLoadLine)
1539 moduleLoadBlock = true;
1540 else if(moduleLoadBlock && !moduleLoadLine && strlen(line) > 0)
1546 char relative[MAX_LOCATION];
1547 bp.absoluteFilePath = CopyString(path);
1548 MakePathRelative(path, ide.workspace.projectDir, relative);
1549 delete bp.relativeFilePath;
1550 bp.relativeFilePath = CopyString(relative);
1551 bp.line = lineNumber;
1552 sentBreakInsert = true;
1553 GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, lineNumber);
1556 bp.inserted = (bp.bp && bp.bp.number != 0);
1561 else if(bp.type == internalModuleLoad && modules)
1563 Project ecerePrj = null;
1564 for(p : ide.workspace.projects)
1566 if(!strcmp(p.topNode.name, "ecere.epj"))
1574 ProjectNode node = ecerePrj.topNode.Find("instance.c", false);
1577 char path[MAX_LOCATION];
1578 char relative[MAX_LOCATION];
1579 node.GetFullFilePath(path);
1580 bp.absoluteFilePath = CopyString(path);
1581 MakePathRelative(path, ecerePrj.topNode.path, relative);
1582 delete bp.relativeFilePath;
1583 bp.relativeFilePath = CopyString(relative);
1584 sentBreakInsert = true;
1585 GdbCommand(false, "-break-insert %s:InternalModuleLoadBreakpoint", bp.relativeFilePath);
1588 bp.inserted = (bp.bp && bp.bp.number != 0);
1599 void GdbBreakpointsInsert()
1603 //if(!breakpointsInserted)
1605 //if(!ignoreBreakpoints)
1606 //breakpointsInserted = true;
1607 for(bp : ide.workspace.breakpoints)
1609 if(!bp.inserted && bp.type == user)
1611 if(!ignoreBreakpoints && bp.enabled)
1613 sentBreakInsert = true;
1614 breakpointError = false;
1615 GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, bp.line);
1616 // Improve, GdbCommand should return a success value?
1619 char fileName[MAX_FILENAME];
1620 breakpointError = false;
1621 GetLastDirectory(bp.relativeFilePath, fileName);
1622 sentBreakInsert = true;
1623 GdbCommand(false, "-break-insert %s:%d", fileName, bp.line);
1627 bp.inserted = (bp.bp && bp.bp.number != 0);
1630 ValidateBreakpoint(bp);
1636 printf("problem\n");
1638 bp.bp = GdbDataBreakpoint { };
1642 if(bpRunToCursor && !bpRunToCursor.inserted)
1644 sentBreakInsert = true;
1645 GdbCommand(false, "-break-insert %s:%d", bpRunToCursor.relativeFilePath, bpRunToCursor.line);
1646 bpRunToCursor.bp = bpItem;
1648 bpRunToCursor.inserted = (bpRunToCursor.bp && bpRunToCursor.bp.number != 0);
1649 ValidateBreakpoint(bpRunToCursor);
1655 void GdbBreakpointsDelete(bool deleteRunToCursor)
1657 //breakpointsInserted = false;
1663 GdbCommand(false, "-break-delete %d", bp.bp.number);
1664 bp.inserted = false;
1666 //check here (reply form -break-delete, returns bpitem?)
1669 for(bp : ide.workspace.breakpoints)
1672 GdbCommand(false, "-break-delete %d", bp.bp.number);
1673 bp.inserted = false;
1675 //check here (reply form -break-delete, returns bpitem?)
1678 if(deleteRunToCursor && bpRunToCursor)
1680 GdbCommand(false, "-break-delete %d", bpRunToCursor.bp.number);
1681 bpRunToCursor.inserted = false;
1682 bpRunToCursor.bp = bpItem;
1683 //check here (reply form -break-delete, returns bpitem?)
1692 stackFrames.Free(Frame::Free);
1693 GdbCommand(false, "-stack-info-depth");
1695 GdbCommand(false, "-stack-info-depth 192");
1696 if(frameCount && frameCount <= 192)
1697 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 191));
1700 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 95));
1701 GdbCommand(false, "-stack-list-frames %d %d", Max(frameCount - 96, 96), frameCount - 1);
1703 GdbCommand(false, "");
1710 char escaped[MAX_LOCATION];
1711 strescpy(escaped, targetFile);
1712 GdbCommand(false, "file \"%s\"", escaped); //GDB/MI Missing Implementation -symbol-file, -target-attach
1717 for(prj : ide.workspace.projects)
1719 if(prj == ide.workspace.projects.firstIterator.data)
1722 //PrintLn("THIS: ", (String)prj.topNode.path);
1723 GdbCommand(false, "-environment-directory \"%s\"", prj.topNode.path);
1724 //GdbCommand(false, ""); // why this empty GDB command
1727 for(dir : ide.workspace.sourceDirs)
1729 GdbCommand(false, "-environment-directory \"%s\"", dir);
1730 //GdbCommand(false, ""); // why this empty GDB command
1732 GdbInsertInternalBreakpoints();
1738 void GdbTargetRelease()
1742 GdbBreakpointsDelete(true);
1743 GdbCommand(false, "file"); //GDB/MI Missing Implementation -target-detach
1749 void GdbDebugBreak(bool internal)
1754 breakType = DebuggerAction::internal;
1756 if(ide) ide.Update(null);
1758 if(Process_Break(targetProcessId)) //GdbCommand(false, "-exec-interrupt");
1759 serialSemaphore.Wait();
1762 ChangeState(loaded);
1763 targetProcessId = 0;
1768 ide.outputView.debugBox.Logf("Debugger Error: GdbDebugBreak with not target id should never happen\n");
1775 ShowDebuggerViews();
1776 GdbCommand(true, "-exec-run");
1779 void GdbExecContinue(bool focus)
1782 GdbCommand(focus, "-exec-continue");
1788 GdbCommand(true, "-exec-next");
1794 GdbCommand(true, "-exec-step");
1797 void GdbExecFinish()
1800 GdbCommand(true, "-exec-finish");
1803 void GdbExecCommon()
1805 ClearBreakDisplay();
1806 GdbInsertInternalBreakpoints();
1807 GdbBreakpointsInsert();
1810 #ifdef GDB_DEBUG_GUI
1811 void SendGDBCommand(char * command)
1813 DebuggerState oldState = state;
1818 GdbDebugBreak(true);
1821 GdbCommand(false, command);
1824 if(oldState == running)
1825 GdbExecContinue(false);
1829 void ClearBreakDisplay()
1832 activeFrameLevel = -1;
1843 stackFrames.Free(Frame::Free);
1844 WatchesCodeEditorLinkRelease();
1845 ide.callStackView.Clear();
1846 ide.threadsView.Clear();
1853 GdbCommand(false, "-interpreter-exec console \"kill\""); // should use -exec-abort -- GDB/MI implementation incomplete
1857 bool GdbInit(CompilerConfig compiler, ProjectConfig config, int bitDepth)
1860 char oldDirectory[MAX_LOCATION];
1861 char tempPath[MAX_LOCATION];
1862 char command[MAX_LOCATION];
1863 Project project = ide.project;
1864 DirExpression targetDirExp = project.GetTargetDir(compiler, config, bitDepth);
1865 PathBackup pathBackup { };
1867 if(currentCompiler != compiler)
1869 delete currentCompiler;
1870 currentCompiler = compiler;
1871 incref currentCompiler;
1874 this.bitDepth = bitDepth;
1876 ChangeState(loaded);
1878 sentBreakInsert = false;
1879 breakpointError = false;
1883 //breakpointsInserted = false;
1885 ide.outputView.ShowClearSelectTab(debug);
1886 ide.outputView.debugBox.Logf($"Starting debug mode\n");
1888 #ifdef GDB_DEBUG_CONSOLE
1889 Log("Starting GDB"); Log("\n");
1891 #ifdef GDB_DEBUG_OUTPUT
1892 ide.outputView.gdbBox.Logf("run: Starting GDB\n");
1895 strcpy(tempPath, ide.workspace.projectDir);
1896 PathCatSlash(tempPath, targetDirExp.dir);
1898 targetDir = CopyString(tempPath);
1899 project.CatTargetFileName(tempPath, compiler, config);
1901 targetFile = CopyString(tempPath);
1903 GetWorkingDir(oldDirectory, MAX_LOCATION);
1904 if(ide.workspace.debugDir && ide.workspace.debugDir[0])
1906 char temp[MAX_LOCATION];
1907 strcpy(temp, ide.workspace.projectDir);
1908 PathCatSlash(temp, ide.workspace.debugDir);
1909 ChangeWorkingDir(temp);
1912 ChangeWorkingDir(ide.workspace.projectDir);
1914 ide.SetPath(true, compiler, config, bitDepth);
1916 // TODO: This pollutes the environment, but at least it works
1917 // It shouldn't really affect the IDE as the PATH gets restored and other variables set for testing will unlikely cause problems
1918 // What is the proper solution for this? DualPipeOpenEnv?
1919 // gdb set environment commands don't seem to take effect
1920 for(e : ide.workspace.environmentVars)
1922 SetEnvironment(e.name, e.string);
1926 (compiler.targetPlatform == win32 && bitDepth == 64) ? "x86_64-w64-mingw32-gdb" :
1927 (compiler.targetPlatform == win32 && bitDepth == 32) ? "i686-w64-mingw32-gdb" :
1929 strcat(command, " -n -silent --interpreter=mi2"); //-async //\"%s\"
1931 gdbHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
1934 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start GDB\n");
1942 gdbProcessId = gdbHandle.GetProcessID();
1945 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get GDB process ID\n");
1951 serialSemaphore.Wait();
1956 //ChangeState(terminated);
1962 #if defined(__unix__)
1964 CreateTemporaryDir(progFifoDir, "ecereide");
1965 strcpy(progFifoPath, progFifoDir);
1966 PathCat(progFifoPath, "ideprogfifo");
1967 if(!mkfifo(progFifoPath, 0600))
1969 //fileCreated = true;
1974 ide.outputView.debugBox.Logf(createFIFOMsg, progFifoPath);
1979 progThread.terminate = false;
1980 progThread.Create();
1983 #if defined(__WIN32__)
1984 GdbCommand(false, "-gdb-set new-console on");
1987 GdbCommand(false, "-gdb-set verbose off");
1988 //GdbCommand(false, "-gdb-set exec-done-display on");
1989 GdbCommand(false, "-gdb-set step-mode off");
1990 GdbCommand(false, "-gdb-set unwindonsignal on");
1991 //GdbCommand(false, "-gdb-set shell on");
1992 GdbCommand(false, "set print elements 992");
1993 GdbCommand(false, "-gdb-set backtrace limit 100000");
1995 #if defined(__unix__)
1996 GdbCommand(false, "-inferior-tty-set %s", progFifoPath);
1999 GdbCommand(false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
2001 for(e : ide.workspace.environmentVars)
2003 GdbCommand(false, "set environment %s=%s", e.name, e.string);
2010 ChangeWorkingDir(oldDirectory);
2016 delete targetDirExp;
2022 if(gdbHandle && gdbProcessId)
2024 GdbCommand(false, "-gdb-exit");
2039 ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
2043 for(bp : ide.workspace.breakpoints)
2044 bp.inserted = false;
2046 bp.inserted = false;
2048 bpRunToCursor.inserted = false;
2050 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2051 ClearBreakDisplay();
2054 #if defined(__unix__)
2055 if(FileExists(progFifoPath)) //fileCreated)
2057 progThread.terminate = true;
2060 fifoFile.CloseInput();
2066 DeleteFile(progFifoPath);
2067 progFifoPath[0] = '\0';
2073 void WatchesCodeEditorLinkInit()
2076 char tempPath[MAX_LOCATION];
2077 char path[MAX_LOCATION];
2079 //void MakeFilePathProjectRelative(char * path, char * relativePath)
2080 if(!ide.projectView.GetRelativePath(activeFrame.file, tempPath))
2081 strcpy(tempPath, activeFrame.file);
2083 strcpy(path, ide.workspace.projectDir);
2084 PathCat(path, tempPath);
2085 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2088 for(srcDir : ide.workspace.sourceDirs)
2090 strcpy(path, srcDir);
2091 PathCat(path, tempPath);
2092 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2093 if(codeEditor) break;
2098 if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2099 activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);
2100 if(!activeFrame || !activeFrame.absoluteFile)
2103 codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal, false);
2106 codeEditor.inUseDebug = true;
2109 //watchesInit = true;
2112 void WatchesCodeEditorLinkRelease()
2118 codeEditor.inUseDebug = false;
2119 if(!codeEditor.visible)
2120 codeEditor.Destroy(0);
2126 bool ResolveWatch(Watch wh)
2128 bool result = false;
2141 char watchmsg[MAX_F_STRING];
2142 if(state == stopped && !codeEditor)
2143 wh.value = CopyString($"No source file found for selected frame");
2144 //if(codeEditor && state == stopped || state != stopped)
2147 Module backupPrivateModule;
2148 Context backupContext;
2149 Class backupThisClass;
2153 backupPrivateModule = GetPrivateModule();
2154 backupContext = GetCurrentContext();
2155 backupThisClass = GetThisClass();
2158 SetPrivateModule(codeEditor.privateModule);
2159 SetCurrentContext(codeEditor.globalContext);
2160 SetTopContext(codeEditor.globalContext);
2161 SetGlobalContext(codeEditor.globalContext);
2162 SetGlobalData(&codeEditor.globalData);
2165 exp = ParseExpressionString(wh.expression);
2167 if(exp && !parseError)
2169 if(GetPrivateModule())
2172 DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2173 ProcessExpressionType(exp);
2175 wh.type = exp.expType;
2178 DebugComputeExpression(exp);
2180 /*if(exp.hasAddress)
2182 char temp[MAX_F_STRING];
2183 sprintf(temp, "0x%x", exp.address);
2184 wh.address = CopyString(temp);
2185 // wh.address = CopyStringf("0x%x", exp.address);
2190 Type dataType = exp.expType;
2193 char temp[MAX_F_STRING];
2194 switch(dataType.kind)
2197 sprintf(temp, "%i", exp.val.c);
2200 sprintf(temp, "%i", exp.val.s);
2205 sprintf(temp, "%i", exp.val.i);
2208 sprintf(temp, "%i", exp.val.i64);
2211 sprintf(temp, "%i", exp.val.p);
2216 long v = (long)exp.val.f;
2217 sprintf(temp, "%i", v);
2222 long v = (long)exp.val.d;
2223 sprintf(temp, "%i", v);
2228 wh.intVal = CopyString(temp);
2229 switch(dataType.kind)
2232 sprintf(temp, "0x%x", exp.val.c);
2235 sprintf(temp, "0x%x", exp.val.s);
2239 sprintf(temp, "0x%x", exp.val.i);
2242 sprintf(temp, "0x%x", exp.val.i64);
2245 sprintf(temp, "0x%x", exp.val.i64);
2248 sprintf(temp, "0x%x", exp.val.p);
2253 long v = (long)exp.val.f;
2254 sprintf(temp, "0x%x", v);
2259 long v = (long)exp.val.d;
2260 sprintf(temp, "0x%x", v);
2265 wh.hexVal = CopyString(temp);
2266 switch(dataType.kind)
2269 sprintf(temp, "0o%o", exp.val.c);
2272 sprintf(temp, "0o%o", exp.val.s);
2276 sprintf(temp, "0o%o", exp.val.i);
2279 sprintf(temp, "0o%o", exp.val.i64);
2282 sprintf(temp, "0o%o", exp.val.i64);
2285 sprintf(temp, "0o%o", exp.val.p);
2290 long v = (long)exp.val.f;
2291 sprintf(temp, "0o%o", v);
2296 long v = (long)exp.val.d;
2297 sprintf(temp, "0o%o", v);
2302 wh.octVal = CopyString(temp);
2305 // WHATS THIS HERE ?
2306 if(exp.type == constantExp && exp.constant)
2307 wh.constant = CopyString(exp.constant);
2313 case symbolErrorExp:
2314 snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2316 case structMemberSymbolErrorExp:
2317 // todo get info as in next case (ExpClassMemberSymbolError)
2318 snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2320 case classMemberSymbolErrorExp:
2323 Expression memberExp = exp.member.exp;
2324 Identifier memberID = exp.member.member;
2325 Type type = memberExp.expType;
2328 _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2331 char string[256] = "";
2333 PrintTypeNoConst(type, string, false, true);
2334 classSym = FindClass(string);
2335 _class = classSym ? classSym.registered : null;
2338 snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2340 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2343 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2346 case memoryErrorExp:
2347 // Need to ensure when set to memoryErrorExp, constant is set
2348 snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2350 case dereferenceErrorExp:
2351 snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2353 case unknownErrorExp:
2354 snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2356 case noDebuggerErrorExp:
2357 snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2359 case debugStateErrorExp:
2360 snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2363 snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2367 // Temporary Code for displaying Strings
2368 if((exp.expType && ((exp.expType.kind == pointerType ||
2369 exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) ||
2370 (wh.type && wh.type.kind == classType && wh.type._class &&
2371 wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2372 !strcmp(wh.type._class.registered.name, "String")))
2375 if(exp.expType.kind != arrayType || exp.hasAddress)
2381 //char temp[MAX_F_STRING * 32];
2383 ExpressionType evalError = dummyExp;
2384 /*if(exp.expType.kind == arrayType)
2385 sprintf(temp, "(char*)0x%x", exp.address);
2387 sprintf(temp, "(char*)%s", exp.constant);*/
2389 //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2390 // address = strtoul(exp.constant, null, 0);
2391 address = _strtoui64(exp.constant, null, 0);
2392 //printf("%x\n", address);
2393 // snprintf(value, sizeof(value), "0x%08x ", address);
2395 if(address > 0xFFFFFFFFLL)
2396 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2398 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2399 value[sizeof(value)-1] = 0;
2402 strcat(value, $"Null string");
2406 len = strlen(value);
2408 while(!string && size > 2)
2410 string = GdbReadMemory(address, size);
2413 if(string && string[0])
2416 if(UTF8Validate(string))
2421 for(c = 0; (ch = string[c]) && c<4096; c++)
2424 value[len++] = '\0';
2429 ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2430 strcat(value, ") (ISO8859-1)");
2437 strcat(value, $"Empty string");
2441 strcat(value, $"Couldn't read memory");
2443 wh.value = CopyString(value);
2446 else if(wh.type && wh.type.kind == classType && wh.type._class &&
2447 wh.type._class.registered && wh.type._class.registered.type == enumClass)
2449 uint64 value = strtoul(exp.constant, null, 0);
2450 Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2451 EnumClassData enumeration = (EnumClassData)enumClass.data;
2453 for(item = enumeration.values.first; item; item = item.next)
2454 if((int)item.data == value)
2457 wh.value = CopyString(item.name);
2459 wh.value = CopyString($"Invalid Enum Value");
2460 result = (bool)atoi(exp.constant);
2462 else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class &&
2463 wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2470 if(exp.constant[0] == '\'')
2472 if((int)((byte *)exp.constant)[1] > 127)
2475 value = UTF8GetChar(exp.constant + 1, &nb);
2476 if(nb < 2) value = exp.constant[1];
2477 signedValue = value;
2481 signedValue = exp.constant[1];
2483 // Precomp Syntax error with boot strap here:
2484 byte b = (byte)(char)signedValue;
2485 value = (unichar) b;
2491 if(wh.type.kind == charType && wh.type.isSigned)
2493 signedValue = (int)(char)strtol(exp.constant, null, 0);
2495 // Precomp Syntax error with boot strap here:
2496 byte b = (byte)(char)signedValue;
2497 value = (unichar) b;
2502 value = (uint)strtoul(exp.constant, null, 0);
2503 signedValue = (int)value;
2507 UTF32toUTF8Len(&value, 1, charString, 5);
2509 snprintf(string, sizeof(string), "\'\\0' (0)");
2510 else if(value == '\t')
2511 snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2512 else if(value == '\n')
2513 snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2514 else if(value == '\r')
2515 snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2516 else if(wh.type.kind == charType && wh.type.isSigned)
2517 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2518 else if(value > 256 || wh.type.kind != charType)
2520 if(value > 0x10FFFF || !GetCharCategory(value))
2521 snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2523 snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2526 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2527 string[sizeof(string)-1] = 0;
2529 wh.value = CopyString(string);
2534 wh.value = CopyString(exp.constant);
2535 result = (bool)atoi(exp.constant);
2541 wh.value = PrintHexUInt64(exp.address);
2542 result = (bool)exp.address;
2546 char tempString[256];
2547 if(exp.member.memberType == propertyMember)
2548 snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2550 snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression,
2551 exp.type.OnGetString(tempString, null, null));
2557 snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2558 if(exp) FreeExpression(exp);
2561 SetPrivateModule(backupPrivateModule);
2562 SetCurrentContext(backupContext);
2563 SetTopContext(backupContext);
2564 SetGlobalContext(backupContext);
2565 SetThisClass(backupThisClass);
2568 // wh.value = CopyString("No source file found for selected frame");
2570 watchmsg[sizeof(watchmsg)-1] = 0;
2572 wh.value = CopyString(watchmsg);
2574 ide.watchesView.UpdateWatch(wh);
2578 void EvaluateWatches()
2580 for(wh : ide.workspace.watches)
2584 char * ::GdbEvaluateExpression(char * expression)
2588 GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2590 ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2594 // to be removed... use GdbReadMemory that returns a byte array instead
2595 char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
2601 printf("GdbReadMemoryString called with size = 0!\n");
2603 // GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
2604 if(GetRuntimePlatform() == win32)
2605 GdbCommand(false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
2607 GdbCommand(false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
2609 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
2613 byte * ::GdbReadMemory(uint64 address, int bytes)
2617 //GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
2618 if(GetRuntimePlatform() == win32)
2619 GdbCommand(false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
2621 GdbCommand(false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
2624 printf("GdbReadMemory called with bytes = 0!\n");
2627 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
2628 else if(eval.result && strcmp(eval.result, "N/A"))
2630 byte * result = new byte[bytes];
2631 byte * string = eval.result;
2635 result[c++] = (byte)strtol(string, &string, 10);
2651 void EventHit(GdbDataStop stopItem)
2653 bool conditionMet = true;
2654 Breakpoint bp = bpHit;
2656 if(!bp && bpRunToCursor)
2661 if(bp.type == user && stopItem.frame.line && bp.line != stopItem.frame.line)
2663 bp.line = stopItem.frame.line;
2664 ide.breakpointsView.UpdateBreakpoint(bp.row);
2665 ide.workspace.Save();
2671 case internalWinMain:
2672 GdbBreakpointsInsert();
2673 if(userBreakOnInternBreak)
2675 userBreakOnInternBreak = false;
2676 // Why was SelectFrame missing here?
2677 SelectFrame(activeFrameLevel);
2678 GoToStackFrameLine(activeFrameLevel, true);
2679 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
2683 GdbExecContinue(false);
2685 case internalModulesLoaded:
2687 GdbInsertInternalBreakpoints();
2688 GdbBreakpointsInsert();
2689 GdbExecContinue(false);
2691 case internalModuleLoad:
2692 GdbBreakpointsInsert();
2693 GdbExecContinue(false);
2698 conditionMet = ResolveWatch(bp.condition);
2700 if((bp.level == -1 || bp.level == frameCount-1) && conditionMet)
2705 ignoreBreakpoints = false;
2706 // Why was SelectFrame missing here?
2707 SelectFrame(activeFrameLevel);
2708 GoToStackFrameLine(activeFrameLevel, true);
2709 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
2711 if(bp.type == BreakpointType::runToCursor)
2714 GdbCommand(false, "-break-delete %d", bp.bp.number);
2715 delete bpRunToCursor;
2716 bpRunToCursor = null;
2722 GdbExecContinue(false);
2726 GdbExecContinue(false);
2727 ide.breakpointsView.UpdateBreakpoint(bp.row);
2732 ide.outputView.debugBox.Logf("Debugger Error: Breakpoint hit could not match breakpoint instance\n");
2735 void GdbThreadExit()
2737 if(state != terminated)
2739 ChangeState(terminated);
2740 targetProcessId = 0;
2741 ClearBreakDisplay();
2745 serialSemaphore.Release();
2750 ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
2751 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2754 //ChangeState(terminated);
2758 void GdbThreadMain(char * output)
2761 Array<char *> outTokens { minAllocSize = 50 };
2762 Array<char *> subTokens { minAllocSize = 50 };
2763 DebugListItem item { };
2764 DebugListItem item2 { };
2765 bool setWaitingForPID = false;
2767 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
2768 #ifdef GDB_DEBUG_CONSOLE
2769 Log(output); Log("\n");
2771 #ifdef GDB_DEBUG_OUTPUT
2773 int len = strlen(output);
2781 for(c = 0; c < len / 1024; c++)
2783 strncpy(tmp, start, 1024);
2784 ide.outputView.gdbBox.Logf("out: %s\n", tmp);
2787 ide.outputView.gdbBox.Logf("out: %s\n", start);
2791 ide.outputView.gdbBox.Logf("out: %s\n", output);
2795 #ifdef GDB_DEBUG_CONSOLE
2796 strcpy(lastGdbOutput, output);
2798 #ifdef GDB_DEBUG_GUI
2799 if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
2806 if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
2809 ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
2815 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
2817 //if(outTokens.count == 1)
2822 ChangeState(loaded);
2823 targetProcessId = 0;
2824 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2826 if(!strcmp(item.name, "reason"))
2828 char * reason = item.value;
2829 StripQuotes(reason, reason);
2830 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
2833 if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
2835 StripQuotes(item2.value, item2.value);
2836 if(!strcmp(item2.name, "exit-code"))
2837 exitCode = item2.value;
2843 HandleExit(reason, exitCode);
2847 DebuggerProtocolUnknown("Unknown kill reply", item.name);
2850 HandleExit(null, null);
2853 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2855 if(!strcmp(item.name, "bkpt"))
2857 sentBreakInsert = false;
2860 printf("problem\n");
2862 bpItem = GdbDataBreakpoint { };
2863 item.value = StripCurlies(item.value);
2864 TokenizeList(item.value, ',', subTokens);
2865 for(i = 0; i < subTokens.count; i++)
2867 if(TokenizeListItem(subTokens[i], item))
2869 StripQuotes(item.value, item.value);
2870 if(!strcmp(item.name, "number"))
2871 bpItem.number = atoi(item.value);
2872 else if(!strcmp(item.name, "type"))
2873 bpItem.type = CopyString(item.value);
2874 else if(!strcmp(item.name, "disp"))
2875 bpItem.disp = CopyString(item.value);
2876 else if(!strcmp(item.name, "enabled"))
2877 bpItem.enabled = (!strcmpi(item.value, "y"));
2878 else if(!strcmp(item.name, "addr"))
2879 bpItem.addr = CopyString(item.value);
2880 else if(!strcmp(item.name, "func"))
2881 bpItem.func = CopyString(item.value);
2882 else if(!strcmp(item.name, "file"))
2883 bpItem.file = item.value;
2884 else if(!strcmp(item.name, "line"))
2885 bpItem.line = atoi(item.value);
2886 else if(!strcmp(item.name, "at"))
2887 bpItem.at = CopyString(item.value);
2888 else if(!strcmp(item.name, "times"))
2889 bpItem.times = atoi(item.value);
2892 //breakType = bpValidation;
2893 //app.SignalEvent();
2894 subTokens.RemoveAll();
2896 else if(!strcmp(item.name, "BreakpointTable"))
2897 ide.outputView.debugBox.Logf("Debugger Error: Command reply BreakpointTable not handled\n");
2898 else if(!strcmp(item.name, "depth"))
2900 StripQuotes(item.value, item.value);
2901 frameCount = atoi(item.value);
2903 stackFrames.Free(Frame::Free);
2905 else if(!strcmp(item.name, "stack"))
2908 if(stackFrames.count)
2909 ide.callStackView.Logf("...\n");
2912 item.value = StripBrackets(item.value);
2913 TokenizeList(item.value, ',', subTokens);
2914 for(i = 0; i < subTokens.count; i++)
2916 if(TokenizeListItem(subTokens[i], item))
2918 if(!strcmp(item.name, "frame"))
2921 stackFrames.Add(frame);
2922 item.value = StripCurlies(item.value);
2923 ParseFrame(frame, item.value);
2924 if(frame.file && frame.from)
2925 DebuggerProtocolUnknown("Unexpected frame file and from members present", "");
2929 if(activeFrameLevel == -1)
2931 if(ide.projectView.IsModuleInProject(frame.file));
2933 if(frame.level != 0)
2935 //stopItem.frame = frame;
2936 breakType = selectFrame;
2939 activeFrame = frame;
2940 activeFrameLevel = frame.level;
2943 ide.callStackView.Logf("%3d ", frame.level);
2944 if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
2945 ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
2946 else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
2947 ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
2948 else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
2949 ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
2950 else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
2951 ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
2953 ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
2958 ide.callStackView.Logf("%3d ", frame.level);
2963 ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
2967 ide.callStackView.Logf("%s\n", frame.func);
2969 ide.callStackView.Logf($"unknown source\n");
2973 DebuggerProtocolUnknown("Unknown stack content", item.name);
2976 if(activeFrameLevel == -1)
2978 activeFrameLevel = 0;
2979 activeFrame = stackFrames.first;
2981 ide.callStackView.Home();
2983 subTokens.RemoveAll();
2985 /*else if(!strcmp(item.name, "frame"))
2988 item.value = StripCurlies(item.value);
2989 ParseFrame(&frame, item.value);
2991 else if(!strcmp(item.name, "thread-ids"))
2993 ide.threadsView.Clear();
2994 item.value = StripCurlies(item.value);
2995 TokenizeList(item.value, ',', subTokens);
2996 for(i = subTokens.count - 1; ; i--)
2998 if(TokenizeListItem(subTokens[i], item))
3000 if(!strcmp(item.name, "thread-id"))
3003 StripQuotes(item.value, item.value);
3004 value = atoi(item.value);
3005 ide.threadsView.Logf("%3d \n", value);
3008 DebuggerProtocolUnknown("Unknown threads content", item.name);
3013 ide.threadsView.Home();
3015 subTokens.RemoveAll();
3016 //if(!strcmp(outTokens[2], "number-of-threads"))
3018 else if(!strcmp(item.name, "new-thread-id"))
3020 StripQuotes(item.value, item.value);
3021 activeThread = atoi(item.value);
3023 else if(!strcmp(item.name, "value"))
3025 StripQuotes(item.value, item.value);
3026 eval.result = CopyString(item.value);
3027 eval.active = false;
3029 else if(!strcmp(item.name, "addr"))
3031 for(i = 2; i < outTokens.count; i++)
3033 if(TokenizeListItem(outTokens[i], item))
3035 if(!strcmp(item.name, "total-bytes"))
3037 StripQuotes(item.value, item.value);
3038 eval.bytes = atoi(item.value);
3040 else if(!strcmp(item.name, "next-row"))
3042 StripQuotes(item.value, item.value);
3043 eval.nextBlockAddress = _strtoui64(item.value, null, 0);
3045 else if(!strcmp(item.name, "memory"))
3049 //StripQuotes(item.value, item.value);
3050 item.value = StripBrackets(item.value);
3051 // this should be treated as a list...
3052 item.value = StripCurlies(item.value);
3053 TokenizeList(item.value, ',', subTokens);
3054 for(j = 0; j < subTokens.count; j++)
3056 if(TokenizeListItem(subTokens[j], item))
3058 if(!strcmp(item.name, "data"))
3060 item.value = StripBrackets(item.value);
3061 StripQuotes2(item.value, item.value);
3062 eval.result = CopyString(item.value);
3063 eval.active = false;
3067 subTokens.RemoveAll();
3072 else if(!strcmp(item.name, "source-path"))
3076 DebuggerProtocolUnknown("Unknown command reply", item.name);
3079 else if(!strcmp(outTokens[0], "^running"))
3081 waitingForPID = true;
3082 setWaitingForPID = true;
3084 else if(!strcmp(outTokens[0], "^exit"))
3086 ChangeState(terminated);
3087 // ide.outputView.debugBox.Logf("Exit\n");
3088 // ide.Update(null);
3090 serialSemaphore.Release();
3092 else if(!strcmp(outTokens[0], "^error"))
3096 sentBreakInsert = false;
3097 breakpointError = true;
3100 printf("problem\n");
3102 bpItem = GdbDataBreakpoint { };
3105 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3107 if(!strcmp(item.name, "msg"))
3109 StripQuotes(item.value, item.value);
3112 eval.active = false;
3114 if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3115 eval.error = symbolNotFound;
3116 else if(strstr(item.value, "Cannot access memory at address"))
3117 eval.error = memoryCantBeRead;
3119 eval.error = unknown;
3121 else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3124 else if(!strncmp(item.value, "Cannot access memory at address", 31))
3127 else if(!strcmp(item.value, "Cannot find bounds of current function"))
3129 ChangeState(stopped);
3130 gdbHandle.Printf("-exec-continue\n");
3132 else if(!strcmp(item.value, "ptrace: No such process."))
3134 ChangeState(loaded);
3135 targetProcessId = 0;
3137 else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3140 else if(!strcmp(item.value, "You can't do that without a process to debug."))
3142 ChangeState(loaded);
3143 targetProcessId = 0;
3145 else if(strstr(item.value, "No such file or directory."))
3147 ChangeState(loaded);
3148 targetProcessId = 0;
3150 else if(strstr(item.value, "During startup program exited with code "))
3152 ChangeState(loaded);
3153 targetProcessId = 0;
3158 if(strlen(item.value) < MAX_F_STRING)
3161 ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3165 ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3171 DebuggerProtocolUnknown("Unknown error content", item.name);
3174 DebuggerProtocolUnknown("Unknown result-record", outTokens[0]);
3176 outTokens.RemoveAll();
3179 DebuggerProtocolUnknown("Unknown status-async-output", outTokens[0]);
3182 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "=thread-group-created")) //=thread-group-created,id="7611"
3184 else if(!strcmp(outTokens[0], "=thread-created")) //=thread-created,id="1",group-id="7611"
3186 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"
3189 DebuggerProtocolUnknown("Unknown notify-async-output", outTokens[0]);
3190 outTokens.RemoveAll();
3194 if(TokenizeList(output, ',', outTokens))
3196 if(!strcmp(outTokens[0],"*running"))
3198 waitingForPID = true;
3199 setWaitingForPID = true;
3201 else if(!strcmp(outTokens[0], "*stopped"))
3204 ChangeState(stopped);
3206 for(tk = 1; tk < outTokens.count; tk++)
3208 if(TokenizeListItem(outTokens[tk], item))
3210 if(!strcmp(item.name, "reason"))
3212 char * reason = item.value;
3213 StripQuotes(reason, reason);
3214 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3217 if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3220 StripQuotes(item2.value, item2.value);
3221 if(!strcmp(item2.name, "exit-code"))
3222 exitCode = item2.value;
3228 HandleExit(reason, exitCode);
3230 else if(!strcmp(reason, "breakpoint-hit"))
3234 printf("problem\n");
3236 stopItem = GdbDataStop { };
3238 for(i = tk+1; i < outTokens.count; i++)
3240 TokenizeListItem(outTokens[i], item);
3241 StripQuotes(item.value, item.value);
3242 if(!strcmp(item.name, "bkptno"))
3243 stopItem.bkptno = atoi(item.value);
3244 else if(!strcmp(item.name, "thread-id"))
3245 stopItem.threadid = atoi(item.value);
3246 else if(!strcmp(item.name, "frame"))
3248 item.value = StripCurlies(item.value);
3249 ParseFrame(stopItem.frame, item.value);
3252 DebuggerProtocolUnknown("Unknown breakpoint hit item name", item.name);
3257 else if(!strcmp(reason, "end-stepping-range"))
3261 printf("problem\n");
3263 stopItem = GdbDataStop { };
3265 for(i = tk+1; i < outTokens.count; i++)
3267 TokenizeListItem(outTokens[i], item);
3268 StripQuotes(item.value, item.value);
3269 if(!strcmp(item.name, "thread-id"))
3270 stopItem.threadid = atoi(item.value);
3271 else if(!strcmp(item.name, "frame"))
3273 item.value = StripCurlies(item.value);
3274 ParseFrame(stopItem.frame, item.value);
3276 else if(!strcmp(item.name, "reason"))
3278 else if(!strcmp(item.name, "bkptno"))
3281 DebuggerProtocolUnknown("Unknown end of stepping range item name", item.name);
3287 else if(!strcmp(reason, "function-finished"))
3291 printf("problem\n");
3293 stopItem = GdbDataStop { };
3294 stopItem.reason = CopyString(reason);
3296 for(i = tk+1; i < outTokens.count; i++)
3298 TokenizeListItem(outTokens[i], item);
3299 StripQuotes(item.value, item.value);
3300 if(!strcmp(item.name, "thread-id"))
3301 stopItem.threadid = atoi(item.value);
3302 else if(!strcmp(item.name, "frame"))
3304 item.value = StripCurlies(item.value);
3305 ParseFrame(stopItem.frame, item.value);
3307 else if(!strcmp(item.name, "gdb-result-var"))
3308 stopItem.gdbResultVar = CopyString(item.value);
3309 else if(!strcmp(item.name, "return-value"))
3310 stopItem.returnValue = CopyString(item.value);
3312 DebuggerProtocolUnknown("Unknown function finished item name", item.name);
3315 event = functionEnd;
3318 else if(!strcmp(reason, "signal-received"))
3322 printf("problem\n");
3324 stopItem = GdbDataStop { };
3325 stopItem.reason = CopyString(reason);
3327 for(i = tk+1; i < outTokens.count; i++)
3329 TokenizeListItem(outTokens[i], item);
3330 StripQuotes(item.value, item.value);
3331 if(!strcmp(item.name, "signal-name"))
3332 stopItem.name = CopyString(item.value);
3333 else if(!strcmp(item.name, "signal-meaning"))
3334 stopItem.meaning = CopyString(item.value);
3335 else if(!strcmp(item.name, "thread-id"))
3336 stopItem.threadid = atoi(item.value);
3337 else if(!strcmp(item.name, "frame"))
3339 item.value = StripCurlies(item.value);
3340 ParseFrame(stopItem.frame, item.value);
3343 DebuggerProtocolUnknown("Unknown signal reveived item name", item.name);
3345 if(!strcmp(stopItem.name, "SIGTRAP"))
3364 else if(!strcmp(reason, "watchpoint-trigger"))
3365 DebuggerProtocolUnknown("Reason watchpoint trigger not handled", "");
3366 else if(!strcmp(reason, "read-watchpoint-trigger"))
3367 DebuggerProtocolUnknown("Reason read watchpoint trigger not handled", "");
3368 else if(!strcmp(reason, "access-watchpoint-trigger"))
3369 DebuggerProtocolUnknown("Reason access watchpoint trigger not handled", "");
3370 else if(!strcmp(reason, "watchpoint-scope"))
3371 DebuggerProtocolUnknown("Reason watchpoint scope not handled", "");
3372 else if(!strcmp(reason, "location-reached"))
3373 DebuggerProtocolUnknown("Reason location reached not handled", "");
3375 DebuggerProtocolUnknown("Unknown reason", reason);
3383 DebuggerProtocolUnknown("Unknown exec-async-output", outTokens[0]);
3384 outTokens.RemoveAll();
3387 if(!strcmpi(output, "(gdb) "))
3391 char exeFile[MAX_LOCATION];
3392 int oldProcessID = targetProcessId;
3393 GetLastDirectory(targetFile, exeFile);
3397 targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3398 if(targetProcessId || gdbHandle.Peek()) break;
3403 ChangeState(running);
3404 else if(!oldProcessID)
3406 ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3407 // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3408 gdbHandle.Printf("-gdb-exit\n");
3410 ChangeState(terminated); //loaded;
3415 for(bp : ide.workspace.breakpoints)
3416 bp.inserted = false;
3419 bp.inserted = false;
3421 bpRunToCursor.inserted = false;
3423 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3424 ClearBreakDisplay();
3426 #if defined(__unix__)
3427 if(FileExists(progFifoPath)) //fileCreated)
3429 progThread.terminate = true;
3432 fifoFile.CloseInput();
3439 DeleteFile(progFifoPath);
3440 progFifoPath[0] = '\0';
3447 serialSemaphore.Release();
3450 DebuggerProtocolUnknown($"Unknown prompt", output);
3454 if(!strncmp(output, "&\"warning:", 10))
3457 content = strstr(output, "\"");
3458 StripQuotes(content, content);
3459 content = strstr(content, ":");
3465 ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3472 DebuggerProtocolUnknown($"Unknown output", output);
3474 if(!setWaitingForPID)
3475 waitingForPID = false;
3476 setWaitingForPID = false;
3484 void RunToCursorPrepare(char * absoluteFilePath, char * relativeFilePath, int lineNumber, bool atSameLevel)
3488 //bpRunToCursor.Free();
3489 bpRunToCursor = Breakpoint { };
3492 bpRunToCursor = Breakpoint { };
3494 if(absoluteFilePath)
3495 bpRunToCursor.absoluteFilePath = CopyString(absoluteFilePath);
3496 if(relativeFilePath)
3497 bpRunToCursor.relativeFilePath = CopyString(relativeFilePath);
3498 bpRunToCursor.line = lineNumber;
3499 bpRunToCursor.type = runToCursor;
3500 bpRunToCursor.enabled = true;
3501 bpRunToCursor.condition = null;
3502 bpRunToCursor.ignore = 0;
3503 bpRunToCursor.level = atSameLevel ? frameCount - activeFrameLevel -1 : -1;
3506 ExpressionType ::DebugEvalExpTypeError(char * result)
3512 case symbolNotFound:
3513 return symbolErrorExp;
3514 case memoryCantBeRead:
3515 return memoryErrorExp;
3517 return unknownErrorExp;
3520 char * ::EvaluateExpression(char * expression, ExpressionType * error)
3523 if(ide.projectView && ide.debugger.state == stopped)
3525 result = GdbEvaluateExpression(expression);
3526 *error = DebugEvalExpTypeError(result);
3531 *error = noDebuggerErrorExp;
3536 char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
3539 char * result = GdbReadMemoryString(address, size, format, 1, 1);
3540 if(!result || !strcmp(result, "N/A"))
3541 *error = memoryErrorExp;
3543 *error = DebugEvalExpTypeError(result);
3548 class GdbThread : Thread
3554 static char output[4096];
3555 Array<char> dynamicBuffer { minAllocSize = 4096 };
3556 DualPipe oldGdbHandle = gdbHandle;
3557 incref oldGdbHandle;
3560 while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
3564 result = gdbHandle.Read(output, 1, sizeof(output));
3566 if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
3573 for(c = 0; c<result; c++)
3575 if(output[c] == '\n')
3577 int pos = dynamicBuffer.size;
3578 dynamicBuffer.size += c - start;
3579 memcpy(&dynamicBuffer[pos], output + start, c - start);
3580 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3581 // COMMENTED OUT DUE TO ISSUE #135, FIXED
3582 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
3583 dynamicBuffer.size++;
3584 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
3586 // printf("%s\n", dynamicBuffer.array);
3588 debugger.GdbThreadMain(&dynamicBuffer[0]);
3589 dynamicBuffer.size = 0;
3595 int pos = dynamicBuffer.size;
3596 dynamicBuffer.size += c - start;
3597 memcpy(&dynamicBuffer[pos], output + start, c - start);
3603 printf("Got end of file from GDB!\n");
3607 delete dynamicBuffer;
3608 //if(oldGdbHandle == gdbHandle)
3609 debugger.GdbThreadExit();
3610 delete oldGdbHandle;
3616 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
3617 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
3619 #if defined(__unix__)
3624 #include <sys/types.h>
3629 class ProgramThread : Thread
3635 bool fileCreated = false;
3637 static char output[1000];
3640 /*if(!mkfifo(progFifoPath, mask))
3647 ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
3651 if(FileExists(progFifoPath)) //fileCreated)
3653 fifoFile = FileOpen(progFifoPath, read);
3657 ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
3662 fd = fileno((FILE *)fifoFile.input);
3663 //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
3667 while(!terminate && fifoFile && !fifoFile.Eof())
3670 struct timeval time;
3678 selectResult = select(fd + 1, &rs, null, null, &time);
3679 if(FD_ISSET(fd, &rs))
3681 int result = (int)read(fd, output, sizeof(output)-1);
3682 if(!result || (result < 0 && errno != EAGAIN))
3686 output[result] = '\0';
3687 if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
3690 ide.outputView.debugBox.Log(output);
3699 //fifoFile.CloseInput();
3702 ide.outputView.debugBox.Log("\n");
3706 if(FileExists(progFifoPath)) //fileCreated)
3708 DeleteFile(progFifoPath);
3709 progFifoPath[0] = '\0';
3717 class Argument : struct
3719 Argument prev, next;
3735 class Frame : struct
3744 property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
3746 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3747 char * absoluteFile;
3756 delete absoluteFile;
3757 args.Free(Argument::Free);
3766 class GdbDataStop : struct
3783 char * gdbResultVar;
3793 if(!strcmp(reason, "signal-received"))
3798 else if(!strcmp(reason, "function-finished"))
3800 delete gdbResultVar;
3805 if(frame) frame.Free();
3814 class GdbDataBreakpoint : struct
3823 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3838 ~GdbDataBreakpoint()
3844 class Breakpoint : struct
3848 char * relativeFilePath;
3849 char * absoluteFilePath;
3858 BreakpointType type;
3861 GdbDataBreakpoint bp;
3863 char * LocationToString()
3865 char location[MAX_LOCATION+20];
3866 snprintf(location, sizeof(location), "%s:%d", relativeFilePath, line);
3867 location[sizeof(location)-1] = 0;
3868 #if defined(__WIN32__)
3869 ChangeCh(location, '/', '\\');
3871 return CopyString(location);
3876 if(relativeFilePath && relativeFilePath[0])
3878 f.Printf(" * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
3880 f.Printf(" ~ %s\n", condition.expression);
3889 delete relativeFilePath;
3890 delete absoluteFilePath;
3900 class Watch : struct
3911 f.Printf(" ~ %s\n", expression);
3935 class DebugListItem : struct
3941 struct DebugEvaluationData
3946 uint64 nextBlockAddress;
3948 DebuggerEvaluationError error;
3951 class CodeLocation : struct
3954 char * absoluteFile;
3957 CodeLocation ::ParseCodeLocation(char * location)
3961 char * colon = null;
3963 char loc[MAX_LOCATION];
3964 strcpy(loc, location);
3965 for(temp = loc; temp = strstr(temp, ":"); temp++)
3973 int line = atoi(colon);
3976 CodeLocation codloc { line = line };
3977 codloc.file = CopyString(loc);
3978 codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
3990 delete absoluteFile;