2 public import static "ecere"
3 public import static "ec"
15 #define GDB_DEBUG_CONSOLE
18 extern char * strrchr(const char * s, int c);
21 #define strlen _strlen
31 #include <sys/time.h> // Required on Apple...
36 public char * StripQuotes2(char * string, char * output)
40 bool quoted = false, escaped = false;
42 for(c = 0; ch = string[c]; c++)
46 if(escaped || ch != '\"')
49 escaped = !escaped && ch == '\\';
64 static void strescpy(char * d, char * s)
117 static char * CopyUnescapedSystemPath(char * p)
119 char * d = new char[strlen(p) + 1];
121 #if defined(__WIN32__)
122 ChangeCh(d, '/', '\\');
127 static char * CopyUnescapedUnixPath(char * p)
129 char * d = new char[strlen(p) + 1];
131 #if defined(__WIN32__)
132 ChangeCh(d, '\\', '/');
137 static char * CopyUnescapedString(char * s)
139 char * d = new char[strlen(s) + 1];
144 // String Unescape Copy
146 // TOFIX: THIS DOESN'T HANDLE NUMERIC ESCAPE CODES (OCTAL/HEXADECIMAL...)?
147 // Seems very similar to ReadString in pass15.ec (which also misses numeric escape codes :) )
149 static void struscpy(char * d, char * s)
201 static char * StripBrackets(char * string)
203 int length = strlen(string);
204 if(length > 1 && *string == '[' && string[length - 1] == ']')
207 string[length - 1] = '\0';
214 static char * StripCurlies(char * string)
216 int length = strlen(string);
217 if(length > 1 && *string == '{' && string[length - 1] == '}')
220 string[length - 1] = '\0';
227 static int StringGetInt(char * string, int start)
230 int i, len = strlen(string);
232 for(i = start; i < len && i < start + 8; i++)
234 if(string[i] == '0' || string[i] == '1' || string[i] == '2' || string[i] == '3' || string[i] == '4' || string[i] == '5' || string[i] == '6' || string[i] == '7' || string[i] == '8' || string[i] == '9')
235 strncat(number, &string[i], 1);
242 static int TokenizeList(char * string, const char seperator, Array<char *> tokens)
246 bool quoted = false, escaped = false;
247 char * start = string, ch;
249 for(; (ch = *string); string++)
256 if(escaped || ch != '\"')
257 escaped = !escaped && ch == '\\';
263 else if(ch == '{' || ch == '[' || ch == '(' || ch == '<')
265 else if(ch == '}' || ch == ']' || ch == ')' || ch == '>')
267 else if(ch == seperator && !level)
276 //tokens[count] = start;
277 //tokens[count++] = start;
284 static bool TokenizeListItem(char * string, DebugListItem item)
286 char * equal = strstr(string, "=");
300 static void DebuggerProtocolUnknown(char * message, char * gdbOutput)
302 #ifdef _DEBUG_GDB_PROTOCOL
303 ide.outputView.debugBox.Logf("Debugger Protocol Error: %s (%s)\n", message, gdbOutput);
307 // define GdbGetLineSize = 1638400;
308 define GdbGetLineSize = 5638400;
309 #if defined(__unix__)
310 char progFifoPath[MAX_LOCATION];
311 char progFifoDir[MAX_LOCATION];
314 enum DebuggerState { none, prompt, loaded, running, stopped, terminated };
315 enum DebuggerEvent { none, hit, breakEvent, signal, stepEnd, functionEnd, exit };
316 enum DebuggerAction { none, internal, restart, stop, selectFrame }; //, bpValidation
317 enum BreakpointType { none, internalMain, internalWinMain, internalModulesLoaded, user, runToCursor, internalModuleLoad };
318 enum DebuggerEvaluationError { none, symbolNotFound, memoryCantBeRead, unknown };
320 FileDialog debuggerFileDialog { type = selectDir };
322 static DualPipe gdbHandle;
323 static DebugEvaluationData eval { };
325 static int targetProcessId;
327 static bool gdbReady;
328 static bool breakpointError;
332 Semaphore serialSemaphore { };
337 //bool breakpointsInserted;
339 bool sentBreakInsert;
340 bool ignoreBreakpoints;
341 bool userBreakOnInternBreak;
348 int activeFrameLevel;
359 DebuggerAction breakType;
360 //DebuggerCommand lastCommand; // THE COMPILER COMPILES STUFF THAT DOES NOT EXIST???
362 GdbDataStop stopItem;
363 GdbDataBreakpoint bpItem;
366 List<Breakpoint> sysBPs { };
367 Breakpoint bpRunToCursor;
373 CompilerConfig currentCompiler;
374 ProjectConfig prjConfig;
377 CodeEditor codeEditor;
379 GdbThread gdbThread { debugger = this };
382 delay = 0.0, userData = this;
386 bool monitor = false;
387 DebuggerEvent curEvent = event;
388 GdbDataStop stopItem = this.stopItem;
394 this.stopItem = null;
397 if(curEvent && curEvent != exit)
400 printf("No stop item\n");
408 Restart(currentCompiler, prjConfig, bitDepth);
417 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
418 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
419 if(activeFrame.level == activeFrameLevel)
425 // GdbCommand(false, "-break-info %d", bpItem.number);
435 activeThread = stopItem.threadid;
436 GdbCommand(false, "-thread-list-ids");
441 Breakpoint bp = null;
443 for(i : ide.workspace.breakpoints; i.bp && i.bp.number == stopItem.bkptno)
450 for(i : sysBPs; i.bp && i.bp.number == stopItem.bkptno)
456 if(bp && bp.type != user && stopItem && stopItem.frame)
458 // In case the user put a breakpoint where an internal breakpoint is, avoid the confusion...
459 for(i : ide.workspace.breakpoints)
461 if(i.bp && i.line == stopItem.frame.line && !fstrcmp(i.absoluteFilePath, stopItem.frame.absoluteFile))
470 if(!(!userBreakOnInternBreak &&
471 bp && (bp.type == internalMain || bp.type == internalWinMain ||
472 bp.type == internalModulesLoaded || bp.type == internalModuleLoad)))
474 hitThread = stopItem.threadid;
478 signalThread = stopItem.threadid;
490 activeThread = stopItem.threadid;
491 GdbCommand(false, "-thread-list-ids");
493 if(activeFrameLevel > 0)
494 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
496 WatchesCodeEditorLinkInit();
506 ide.outputView.debugBox.Logf($"Signal received: %s - %s\n", stopItem.name, stopItem.meaning);
507 ide.outputView.debugBox.Logf(" %s:%d\n", (s = CopySystemPath(stopItem.frame.file)), stopItem.frame.line);
513 // Why was SelectFrame missing here?
514 SelectFrame(activeFrameLevel);
515 GoToStackFrameLine(activeFrameLevel, curEvent == signal || curEvent == stepEnd /*false*/);
516 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
518 if(curEvent == signal)
519 ide.outputView.Show();
520 if(curEvent == signal || curEvent == breakEvent)
522 if(curEvent == breakEvent)
523 ide.threadsView.Show();
524 ide.callStackView.Show();
526 ide.ShowCodeEditor();
527 if(curEvent == breakEvent)
528 ide.callStackView.Activate();
536 ignoreBreakpoints = false;
547 #ifdef GDB_DEBUG_CONSOLE
548 char lastGdbOutput[GdbGetLineSize];
550 #if defined(__unix__)
551 ProgramThread progThread { };
554 void ChangeState(DebuggerState value)
556 bool same = value == state;
557 // if(same) PrintLn("Debugger::ChangeState -- changing to same state");
559 if(!same && ide) ide.AdjustDebugMenus();
564 // Stop(); // Don't need to call Stop here, because ~ProjectView() will call it explicitly.
566 stackFrames.Free(Frame::Free);
576 waitingForPID = false;
581 sentBreakInsert = false;
582 ignoreBreakpoints = false;
583 userBreakOnInternBreak = false;
586 activeFrameLevel = 0;
603 bpRunToCursor = null;
606 delete currentCompiler;
610 /*GdbThread gdbThread
616 ideProcessId = Process_GetCurrentProcessId();
618 sysBPs.Add(Breakpoint { type = internalMain, enabled = true, level = -1 });
619 #if defined(__WIN32__)
620 sysBPs.Add(Breakpoint { type = internalWinMain, enabled = true, level = -1 });
622 sysBPs.Add(Breakpoint { type = internalModulesLoaded, enabled = true, level = -1 });
623 sysBPs.Add(Breakpoint { type = internalModuleLoad, enabled = true, level = -1 });
635 property bool isActive { get { return state == running || state == stopped; } }
636 property bool isPrepared { get { return state == loaded || state == running || state == stopped; } }
640 GdbExecContinue(true);
648 GdbDebugBreak(false);
660 GdbDebugBreak(false);
672 void Restart(CompilerConfig compiler, ProjectConfig config, int bitDepth)
680 GdbDebugBreak(false);
687 if(!GdbInit(compiler, config, bitDepth))
695 bool GoToCodeLine(char * location)
698 codloc = CodeLocation::ParseCodeLocation(location);
701 CodeEditor editor = (CodeEditor)ide.OpenFile(codloc.absoluteFile, normal, true, null, no, normal, false);
704 EditBox editBox = editor.editBox;
705 editBox.GoToLineNum(codloc.line - 1);
706 editBox.GoToPosition(editBox.line, codloc.line - 1, 0);
713 bool GoToStackFrameLine(int stackLevel, bool askForLocation)
717 char filePath[MAX_LOCATION];
718 char sourceDir[MAX_LOCATION];
720 CodeEditor editor = null;
721 if(stackLevel == -1) // this (the two lines) is part of that fix that I would not put in for some time
723 for(frame = stackFrames.first; frame; frame = frame.next)
724 if(frame.level == stackLevel)
728 ide.callStackView.Show();
730 if(!frame.absoluteFile && frame.file)
731 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
732 if(!frame.absoluteFile && askForLocation && frame.file)
735 char title[MAX_LOCATION];
736 snprintf(title, sizeof(title), $"Provide source file location for %s", (s = CopySystemPath(frame.file)));
737 title[sizeof(title)-1] = 0;
739 if(SourceDirDialog(title, ide.workspace.projectDir, frame.file, sourceDir))
741 AddSourceDir(sourceDir);
742 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
745 if(frame.absoluteFile)
746 editor = (CodeEditor)ide.OpenFile(frame.absoluteFile, normal, true, null, no, normal, false);
748 if(editor && frame.line)
750 EditBox editBox = editor.editBox;
751 editBox.GoToLineNum(frame.line - 1);
752 editBox.GoToPosition(editBox.line, frame.line - 1, 0);
760 void SelectThread(int thread)
764 if(thread != activeThread)
766 activeFrameLevel = -1;
767 ide.callStackView.Clear();
768 GdbCommand(false, "-thread-select %d", thread);
770 // Why was SelectFrame missing here?
771 SelectFrame(activeFrameLevel);
772 GoToStackFrameLine(activeFrameLevel, true);
773 WatchesCodeEditorLinkRelease();
774 WatchesCodeEditorLinkInit();
778 ide.callStackView.Show();
782 void SelectFrame(int frame)
786 if(frame != activeFrameLevel || !codeEditor || !codeEditor.visible)
788 activeFrameLevel = frame; // there is no active frame number in the gdb reply
789 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
790 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
791 if(activeFrame.level == activeFrameLevel)
794 WatchesCodeEditorLinkRelease();
795 WatchesCodeEditorLinkInit();
802 void HandleExit(char * reason, char * code)
804 bool returnedExitCode = false;
805 char verboseExitCode[128];
807 ChangeState(loaded); // this state change seems to be superfluous, might be in case of gdb crash
812 snprintf(verboseExitCode, sizeof(verboseExitCode), $" with exit code %s", code);
813 verboseExitCode[sizeof(verboseExitCode)-1] = 0;
816 verboseExitCode[0] = '\0';
820 // ClearBreakDisplay();
824 for(wh : ide.workspace.watches)
826 if(wh.type) FreeType(wh.type);
829 ide.watchesView.UpdateWatch(wh);
833 #if defined(__unix__)
834 progThread.terminate = true;
837 fifoFile.CloseInput();
846 char program[MAX_LOCATION];
847 GetSystemPathBuffer(program, targetFile);
849 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
850 else if(!strcmp(reason, "exited-normally"))
851 ide.outputView.debugBox.Logf($"The program %s has exited normally%s.\n", program, verboseExitCode);
852 else if(!strcmp(reason, "exited"))
853 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
854 else if(!strcmp(reason, "exited-signalled"))
855 ide.outputView.debugBox.Logf($"The program %s has exited with a signal%s.\n", program, verboseExitCode);
857 ide.outputView.debugBox.Logf($"The program %s has exited (gdb provided an unknown reason)%s.\n", program, verboseExitCode);
862 void Start(CompilerConfig compiler, ProjectConfig config, int bitDepth)
864 ide.outputView.debugBox.Clear();
869 if(!GdbInit(compiler, config, bitDepth))
877 void StepInto(CompilerConfig compiler, ProjectConfig config, int bitDepth)
883 if(!GdbInit(compiler, config, bitDepth))
886 ide.outputView.ShowClearSelectTab(debug);
887 ide.outputView.debugBox.Logf($"Starting debug mode\n");
888 userBreakOnInternBreak = true;
897 void StepOver(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool ignoreBkpts)
903 if(!GdbInit(compiler, config, bitDepth))
906 ide.outputView.ShowClearSelectTab(debug);
907 ide.outputView.debugBox.Logf($"Starting debug mode\n");
908 ignoreBreakpoints = ignoreBkpts;
909 userBreakOnInternBreak = true;
913 ignoreBreakpoints = ignoreBkpts;
914 if(ignoreBreakpoints)
915 GdbBreakpointsDelete(true);
921 void StepOut(bool ignoreBkpts)
925 ignoreBreakpoints = ignoreBkpts;
926 if(ignoreBreakpoints)
927 GdbBreakpointsDelete(true);
932 void RunToCursor(CompilerConfig compiler, ProjectConfig config, int bitDepth, char * absoluteFilePath, int lineNumber, bool ignoreBkpts, bool atSameLevel)
934 char relativeFilePath[MAX_LOCATION];
935 DebuggerState oldState = state;
936 ignoreBreakpoints = ignoreBkpts;
937 if(!ide.projectView.project.GetRelativePath(absoluteFilePath, relativeFilePath))
938 strcpy(relativeFilePath, absoluteFilePath);
943 Start(compiler, config, bitDepth);
950 ide.outputView.ShowClearSelectTab(debug);
951 ide.outputView.debugBox.Logf($"Starting debug mode\n");
953 RunToCursorPrepare(absoluteFilePath, relativeFilePath, lineNumber, atSameLevel);
954 sentBreakInsert = true;
955 GdbCommand(false, "-break-insert %s:%d", relativeFilePath, lineNumber);
956 bpRunToCursor.bp = bpItem;
958 bpRunToCursor.inserted = (bpRunToCursor.bp.number != 0);
959 ValidateBreakpoint(bpRunToCursor);
966 if(ignoreBreakpoints)
967 GdbBreakpointsDelete(false);
971 if(ignoreBreakpoints)
972 GdbBreakpointsDelete(false);
973 GdbExecContinue(true);
979 void GetCallStackCursorLine(bool * error, int * lineCursor, int * lineTopFrame)
981 if(activeFrameLevel == -1)
989 *error = signalOn && activeThread == signalThread;
990 *lineCursor = activeFrameLevel - ((frameCount > 192 && activeFrameLevel > 191) ? frameCount - 192 - 1 : 0) + 1;
991 *lineTopFrame = activeFrameLevel ? 1 : 0;
995 int GetMarginIconsLineNumbers(char * fileName, int lines[], bool enabled[], int max, bool * error, int * lineCursor, int * lineTopFrame)
997 char winFilePath[MAX_LOCATION];
998 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1000 Iterator<Breakpoint> it { ide.workspace.breakpoints };
1001 while(it.Next() && count < max)
1003 Breakpoint bp = it.data;
1006 if(bp.absoluteFilePath && bp.absoluteFilePath[0] && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1008 lines[count] = bp.line;
1009 enabled[count] = bp.enabled;
1014 if(activeFrameLevel == -1)
1022 *error = signalOn && activeThread == signalThread;
1023 if(activeFrame && activeFrame.absoluteFile && !fstrcmp(absoluteFilePath, activeFrame.absoluteFile))
1024 *lineCursor = activeFrame.line;
1027 if(activeFrame && stopItem && stopItem.frame && activeFrame.level == stopItem.frame.level)
1029 else if(stopItem && stopItem.frame && stopItem.frame.absoluteFile && !fstrcmp(absoluteFilePath, stopItem.frame.absoluteFile))
1030 *lineTopFrame = stopItem.frame.line;
1034 if(*lineTopFrame == *lineCursor && *lineTopFrame)
1040 void ChangeWatch(DataRow row, char * expression)
1042 Watch wh = (Watch)row.tag;
1045 delete wh.expression;
1047 wh.expression = CopyString(expression);
1050 Iterator<Watch> it { ide.workspace.watches };
1052 ide.workspace.watches.Delete(it.pointer);
1058 row.tag = (int64)wh;
1059 ide.workspace.watches.Add(wh);
1061 wh.expression = CopyString(expression);
1063 ide.workspace.Save();
1064 //if(expression && state == stopped)
1069 void MoveIcons(char * fileName, int lineNumber, int move, bool start)
1071 char winFilePath[MAX_LOCATION];
1072 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1075 for(bpLink = ide.workspace.breakpoints.first; bpLink; bpLink = next)
1077 Breakpoint bp = (Breakpoint)bpLink.data;
1080 if(bp.type == user && bp.absoluteFilePath && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1082 if(bp.line > lineNumber || (bp.line == lineNumber && start))
1084 if(move < 0 && (bp.line < lineNumber - move))
1085 ide.workspace.RemoveBreakpoint(bp);
1089 ide.breakpointsView.UpdateBreakpoint(bp.row);
1090 ide.workspace.Save();
1096 // moving code cursors is futile, on next step, stop, hit, cursors will be offset anyways
1099 bool SourceDirDialog(char * title, char * startDir, char * test, char * sourceDir)
1103 String srcDir = null;
1105 debuggerFileDialog.text = title;
1106 debuggerFileDialog.currentDirectory = startDir;
1107 debuggerFileDialog.master = ide;
1109 while(debuggerFileDialog.Modal())
1111 strcpy(sourceDir, debuggerFileDialog.filePath);
1112 if(!fstrcmp(ide.workspace.projectDir, sourceDir) &&
1113 MessageBox { type = yesNo, master = ide,
1114 contents = $"This is the project directory.\nWould you like to try again?",
1115 text = $"Invalid Source Directory" }.Modal() == no)
1119 for(dir : ide.workspace.sourceDirs)
1121 if(!fstrcmp(dir, sourceDir))
1129 MessageBox { type = yesNo, master = ide,
1130 contents = $"This source directory is already specified.\nWould you like to try again?",
1131 text = $"Invalid Source Directory" }.Modal() == no)
1137 char file[MAX_LOCATION];
1138 strcpy(file, sourceDir);
1139 PathCat(file, test);
1140 result = FileExists(file);
1142 MessageBox { type = yesNo, master = ide,
1143 contents = $"Unable to locate source file.\nWould you like to try again?",
1144 text = $"Invalid Source Directory" }.Modal() == no)
1158 void AddSourceDir(char * sourceDir)
1160 ide.workspace.sourceDirs.Add(CopyString(sourceDir));
1161 ide.workspace.Save();
1165 DebuggerState oldState = state;
1170 GdbDebugBreak(true);
1173 GdbCommand(false, "-environment-directory \"%s\"", sourceDir);
1176 if(oldState == running)
1177 GdbExecContinue(false);
1181 void ToggleBreakpoint(char * fileName, int lineNumber, Project prj)
1183 char winFilePath[MAX_LOCATION];
1184 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1185 char absolutePath[MAX_LOCATION];
1186 char relativePath[MAX_LOCATION];
1187 char sourceDir[MAX_LOCATION];
1188 Breakpoint bp = null;
1190 strcpy(absolutePath, absoluteFilePath);
1191 for(i : ide.workspace.breakpoints; i.type == user && i.absoluteFilePath && !fstrcmp(i.absoluteFilePath, absolutePath) && i.line == lineNumber)
1200 ide.workspace.RemoveBreakpoint(bp);
1208 // FIXED: This is how it should have been... Source locations are only for files not in project
1209 // if(IsPathInsideOf(absolutePath, ide.workspace.projectDir))
1210 // MakePathRelative(absolutePath, ide.workspace.projectDir, relativePath);
1211 bool result = false;
1213 result = prj.GetRelativePath(absolutePath, relativePath);
1215 result = ide.projectView.project.GetRelativePath(absolutePath, relativePath);
1216 //if(ide.projectView.project.GetRelativePath(absolutePath, relativePath));
1220 char title[MAX_LOCATION];
1221 char directory[MAX_LOCATION];
1222 StripLastDirectory(absolutePath, directory);
1223 snprintf(title, sizeof(title), $"Provide source files location directory for %s", absolutePath);
1224 title[sizeof(title)-1] = 0;
1227 String srcDir = null;
1228 for(dir : ide.workspace.sourceDirs)
1230 if(IsPathInsideOf(absolutePath, dir))
1232 MakePathRelative(absoluteFilePath, dir, relativePath);
1240 if(SourceDirDialog(title, directory, null, sourceDir))
1242 if(IsPathInsideOf(absolutePath, sourceDir))
1244 AddSourceDir(sourceDir);
1245 MakePathRelative(absoluteFilePath, sourceDir, relativePath);
1248 else if(MessageBox { type = yesNo, master = ide,
1249 contents = $"You must provide a valid source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1250 text = $"Invalid Source Directory" }.Modal() == no)
1253 else if(MessageBox { type = yesNo, master = ide,
1254 contents = $"You must provide a source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1255 text = $"No Source Directory Provided" }.Modal() == no)
1259 ide.workspace.bpCount++;
1260 bp = { line = lineNumber, type = user, enabled = true, level = -1 };
1261 ide.workspace.breakpoints.Add(bp);
1262 bp.absoluteFilePath = CopyString(absolutePath);
1263 bp.relativeFilePath = CopyString(relativePath);
1264 ide.breakpointsView.AddBreakpoint(bp);
1269 DebuggerState oldState = state;
1274 GdbDebugBreak(true);
1279 sentBreakInsert = true;
1280 GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, bp.line);
1283 bp.inserted = (bp.bp && bp.bp.number != 0);
1284 ValidateBreakpoint(bp);
1288 if(oldState == running)
1289 GdbExecContinue(false);
1292 ide.workspace.Save();
1295 void UpdateRemovedBreakpoint(Breakpoint bp)
1297 if(targeted && bp.inserted)
1299 DebuggerState oldState = state;
1304 GdbDebugBreak(true);
1308 GdbCommand(false, "-break-delete %d", bp.bp.number);
1311 if(oldState == running)
1312 GdbExecContinue(false);
1318 void ParseFrame(Frame frame, char * string)
1321 Array<char *> frameTokens { minAllocSize = 50 };
1322 Array<char *> argsTokens { minAllocSize = 50 };
1323 Array<char *> argumentTokens { minAllocSize = 50 };
1324 DebugListItem item { };
1327 TokenizeList(string, ',', frameTokens);
1328 for(i = 0; i < frameTokens.count; i++)
1330 if(TokenizeListItem(frameTokens[i], item))
1332 StripQuotes(item.value, item.value);
1333 if(!strcmp(item.name, "level"))
1334 frame.level = atoi(item.value);
1335 else if(!strcmp(item.name, "addr"))
1336 frame.addr = CopyString(item.value);
1337 else if(!strcmp(item.name, "func"))
1338 frame.func = CopyString(item.value);
1339 else if(!strcmp(item.name, "args"))
1341 if(!strcmp(item.value, "[]"))
1342 frame.argsCount = 0;
1345 item.value = StripBrackets(item.value);
1346 TokenizeList(item.value, ',', argsTokens);
1347 for(j = 0; j < argsTokens.count; j++)
1349 argsTokens[j] = StripCurlies(argsTokens[j]);
1350 TokenizeList(argsTokens[j], ',', argumentTokens);
1351 for(k = 0; k < argumentTokens.count; k++)
1354 frame.args.Add(arg);
1355 if(TokenizeListItem(argumentTokens[k], item))
1357 if(!strcmp(item.name, "name"))
1359 StripQuotes(item.value, item.value);
1360 arg.name = CopyString(item.value);
1362 else if(!strcmp(item.name, "value"))
1364 StripQuotes(item.value, item.value);
1365 arg.value = CopyString(item.value);
1368 DebuggerProtocolUnknown("Unknown frame args item name", item.name);
1371 DebuggerProtocolUnknown("Bad frame args item", "");
1373 argumentTokens.RemoveAll();
1375 frame.argsCount = argsTokens.count;
1376 argsTokens.RemoveAll();
1379 else if(!strcmp(item.name, "from"))
1380 frame.from = item.value;
1381 else if(!strcmp(item.name, "file"))
1383 frame.file = item.value;
1384 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1386 else if(!strcmp(item.name, "line"))
1387 frame.line = atoi(item.value);
1388 else if(!strcmp(item.name, "fullname"))
1390 // GDB 6.3 on OS X is giving "fullname" and "dir", all in absolute, but file name only in 'file'
1391 String path = ide.workspace.GetPathWorkspaceRelativeOrAbsolute(item.value);
1392 if(strcmp(frame.file, path))
1395 delete frame.absoluteFile;
1396 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1401 DebuggerProtocolUnknown("Unknown frame member name", item.name);
1404 DebuggerProtocolUnknown("Bad frame", "");
1409 delete argumentTokens;
1413 void ShowDebuggerViews()
1415 ide.outputView.Show();
1416 ide.outputView.SelectTab(debug);
1417 ide.threadsView.Show();
1418 ide.callStackView.Show();
1419 ide.watchesView.Show();
1423 void HideDebuggerViews()
1425 ide.RepositionWindows(true);
1428 void ::GdbCommand(bool focus, char * format, ...)
1432 // TODO: Improve this limit
1433 static char string[MAX_F_STRING*4];
1435 va_start(args, format);
1436 vsnprintf(string, sizeof(string), format, args);
1437 string[sizeof(string)-1] = 0;
1441 ide.debugger.serialSemaphore.TryWait();
1444 #ifdef GDB_DEBUG_CONSOLE
1445 Log(string); Log("\n");
1447 #ifdef GDB_DEBUG_OUTPUT
1448 ide.outputView.gdbBox.Logf("cmd: %s\n", string);
1450 #ifdef GDB_DEBUG_GUI
1452 ide.gdbDialog.AddCommand(string);
1454 strcat(string,"\n");
1455 gdbHandle.Puts(string);
1458 Process_ShowWindows(targetProcessId);
1461 ide.debugger.serialSemaphore.Wait();
1466 bool ValidateBreakpoint(Breakpoint bp)
1468 if(modules && bp.bp)
1470 if(bp.bp.line != bp.line)
1475 ide.outputView.debugBox.Logf("WOULD HAVE -- Invalid breakpoint disabled: %s:%d\n", bp.relativeFilePath, bp.line);
1479 //GdbCommand(false, "-break-delete %d", bp.bp.number);
1480 //bp.inserted = false;
1482 //bp.enabled = false;
1487 ide.outputView.debugBox.Logf("Debugger Error: ValidateBreakpoint error\n");
1488 bp.line = bp.bp.line;
1495 static void GdbInsertInternalBreakpoints()
1499 //if(!breakpointsInserted)
1501 DirExpression objDir = ide.project.GetObjDir(currentCompiler, prjConfig, bitDepth);
1506 if(bp.type == internalMain)
1508 sentBreakInsert = true;
1509 GdbCommand(false, "-break-insert main");
1512 bp.inserted = (bp.bp && bp.bp.number != 0);
1514 #if defined(__WIN32__)
1515 else if(bp.type == internalWinMain)
1517 sentBreakInsert = true;
1518 GdbCommand(false, "-break-insert WinMain");
1521 bp.inserted = (bp.bp && bp.bp.number != 0);
1524 else if(bp.type == internalModulesLoaded)
1526 char path[MAX_LOCATION];
1527 char name[MAX_LOCATION];
1528 char fixedModuleName[MAX_FILENAME];
1531 bool moduleLoadBlock = false;
1533 ReplaceSpaces(fixedModuleName, ide.project.moduleName);
1534 snprintf(name, sizeof(name),"%s.main.ec", fixedModuleName);
1535 name[sizeof(name)-1] = 0;
1536 strcpy(path, ide.workspace.projectDir);
1537 PathCatSlash(path, objDir.dir);
1538 PathCatSlash(path, name);
1539 f = FileOpen(path, read);
1542 for(lineNumber = 1; !f.Eof(); lineNumber++)
1544 if(f.GetLine(line, sizeof(line) - 1))
1546 bool moduleLoadLine;
1547 TrimLSpaces(line, line);
1548 moduleLoadLine = !strncmp(line, "eModule_Load", strlen("eModule_Load"));
1549 if(!moduleLoadBlock && moduleLoadLine)
1550 moduleLoadBlock = true;
1551 else if(moduleLoadBlock && !moduleLoadLine && strlen(line) > 0)
1557 char relative[MAX_LOCATION];
1558 bp.absoluteFilePath = CopyString(path);
1559 MakePathRelative(path, ide.workspace.projectDir, relative);
1560 delete bp.relativeFilePath;
1561 bp.relativeFilePath = CopyString(relative);
1562 bp.line = lineNumber;
1563 sentBreakInsert = true;
1564 GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, lineNumber);
1567 bp.inserted = (bp.bp && bp.bp.number != 0);
1572 else if(bp.type == internalModuleLoad && modules)
1574 Project ecerePrj = null;
1575 for(p : ide.workspace.projects)
1577 if(!strcmp(p.topNode.name, "ecere.epj"))
1585 ProjectNode node = ecerePrj.topNode.Find("instance.c", false);
1588 char path[MAX_LOCATION];
1589 char relative[MAX_LOCATION];
1590 node.GetFullFilePath(path);
1591 bp.absoluteFilePath = CopyString(path);
1592 MakePathRelative(path, ecerePrj.topNode.path, relative);
1593 delete bp.relativeFilePath;
1594 bp.relativeFilePath = CopyString(relative);
1595 sentBreakInsert = true;
1596 GdbCommand(false, "-break-insert %s:InternalModuleLoadBreakpoint", bp.relativeFilePath);
1599 bp.inserted = (bp.bp && bp.bp.number != 0);
1610 void GdbBreakpointsInsert()
1614 //if(!breakpointsInserted)
1616 //if(!ignoreBreakpoints)
1617 //breakpointsInserted = true;
1618 for(bp : ide.workspace.breakpoints)
1620 if(!bp.inserted && bp.type == user)
1622 if(!ignoreBreakpoints && bp.enabled)
1624 sentBreakInsert = true;
1625 breakpointError = false;
1626 GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, bp.line);
1627 // Improve, GdbCommand should return a success value?
1630 char fileName[MAX_FILENAME];
1631 breakpointError = false;
1632 GetLastDirectory(bp.relativeFilePath, fileName);
1633 sentBreakInsert = true;
1634 GdbCommand(false, "-break-insert %s:%d", fileName, bp.line);
1638 bp.inserted = (bp.bp && bp.bp.number != 0);
1641 ValidateBreakpoint(bp);
1647 printf("problem\n");
1649 bp.bp = GdbDataBreakpoint { };
1653 if(bpRunToCursor && !bpRunToCursor.inserted)
1655 sentBreakInsert = true;
1656 GdbCommand(false, "-break-insert %s:%d", bpRunToCursor.relativeFilePath, bpRunToCursor.line);
1657 bpRunToCursor.bp = bpItem;
1659 bpRunToCursor.inserted = (bpRunToCursor.bp && bpRunToCursor.bp.number != 0);
1660 ValidateBreakpoint(bpRunToCursor);
1666 void GdbBreakpointsDelete(bool deleteRunToCursor)
1668 //breakpointsInserted = false;
1674 GdbCommand(false, "-break-delete %d", bp.bp.number);
1675 bp.inserted = false;
1677 //check here (reply form -break-delete, returns bpitem?)
1680 for(bp : ide.workspace.breakpoints)
1683 GdbCommand(false, "-break-delete %d", bp.bp.number);
1684 bp.inserted = false;
1686 //check here (reply form -break-delete, returns bpitem?)
1689 if(deleteRunToCursor && bpRunToCursor)
1691 GdbCommand(false, "-break-delete %d", bpRunToCursor.bp.number);
1692 bpRunToCursor.inserted = false;
1693 bpRunToCursor.bp = bpItem;
1694 //check here (reply form -break-delete, returns bpitem?)
1703 stackFrames.Free(Frame::Free);
1704 GdbCommand(false, "-stack-info-depth");
1706 GdbCommand(false, "-stack-info-depth 192");
1707 if(frameCount && frameCount <= 192)
1708 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 191));
1711 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 95));
1712 GdbCommand(false, "-stack-list-frames %d %d", Max(frameCount - 96, 96), frameCount - 1);
1714 GdbCommand(false, "");
1721 char escaped[MAX_LOCATION];
1722 strescpy(escaped, targetFile);
1723 GdbCommand(false, "file \"%s\"", escaped); //GDB/MI Missing Implementation -symbol-file, -target-attach
1728 for(prj : ide.workspace.projects)
1730 if(prj == ide.workspace.projects.firstIterator.data)
1732 GdbCommand(false, "-environment-directory \"%s\"", prj.topNode.path);
1735 for(dir : ide.workspace.sourceDirs)
1737 GdbCommand(false, "-environment-directory \"%s\"", dir);
1739 GdbInsertInternalBreakpoints();
1745 void GdbTargetRelease()
1749 GdbBreakpointsDelete(true);
1750 GdbCommand(false, "file"); //GDB/MI Missing Implementation -target-detach
1756 void GdbDebugBreak(bool internal)
1761 breakType = DebuggerAction::internal;
1763 if(ide) ide.Update(null);
1765 if(Process_Break(targetProcessId)) //GdbCommand(false, "-exec-interrupt");
1766 serialSemaphore.Wait();
1769 ChangeState(loaded);
1770 targetProcessId = 0;
1775 ide.outputView.debugBox.Logf("Debugger Error: GdbDebugBreak with not target id should never happen\n");
1782 ShowDebuggerViews();
1783 GdbCommand(true, "-exec-run");
1786 void GdbExecContinue(bool focus)
1789 GdbCommand(focus, "-exec-continue");
1795 GdbCommand(true, "-exec-next");
1801 GdbCommand(true, "-exec-step");
1804 void GdbExecFinish()
1807 GdbCommand(true, "-exec-finish");
1810 void GdbExecCommon()
1812 ClearBreakDisplay();
1813 GdbInsertInternalBreakpoints();
1814 GdbBreakpointsInsert();
1817 #ifdef GDB_DEBUG_GUI
1818 void SendGDBCommand(char * command)
1820 DebuggerState oldState = state;
1825 GdbDebugBreak(true);
1828 GdbCommand(false, command);
1831 if(oldState == running)
1832 GdbExecContinue(false);
1836 void ClearBreakDisplay()
1839 activeFrameLevel = -1;
1850 stackFrames.Free(Frame::Free);
1851 WatchesCodeEditorLinkRelease();
1852 ide.callStackView.Clear();
1853 ide.threadsView.Clear();
1860 GdbCommand(false, "-interpreter-exec console \"kill\""); // should use -exec-abort -- GDB/MI implementation incomplete
1864 bool GdbInit(CompilerConfig compiler, ProjectConfig config, int bitDepth)
1867 char oldDirectory[MAX_LOCATION];
1868 char tempPath[MAX_LOCATION];
1869 char command[MAX_LOCATION];
1870 Project project = ide.project;
1871 DirExpression targetDirExp = project.GetTargetDir(compiler, config, bitDepth);
1872 PathBackup pathBackup { };
1874 if(currentCompiler != compiler)
1876 delete currentCompiler;
1877 currentCompiler = compiler;
1878 incref currentCompiler;
1881 this.bitDepth = bitDepth;
1883 ChangeState(loaded);
1885 sentBreakInsert = false;
1886 breakpointError = false;
1890 //breakpointsInserted = false;
1892 ide.outputView.ShowClearSelectTab(debug);
1893 ide.outputView.debugBox.Logf($"Starting debug mode\n");
1895 #ifdef GDB_DEBUG_CONSOLE
1896 Log("Starting GDB"); Log("\n");
1898 #ifdef GDB_DEBUG_OUTPUT
1899 ide.outputView.gdbBox.Logf("run: Starting GDB\n");
1902 strcpy(tempPath, ide.workspace.projectDir);
1903 PathCatSlash(tempPath, targetDirExp.dir);
1905 targetDir = CopyString(tempPath);
1906 project.CatTargetFileName(tempPath, compiler, config);
1908 targetFile = CopyString(tempPath);
1910 GetWorkingDir(oldDirectory, MAX_LOCATION);
1911 if(ide.workspace.debugDir && ide.workspace.debugDir[0])
1913 char temp[MAX_LOCATION];
1914 strcpy(temp, ide.workspace.projectDir);
1915 PathCatSlash(temp, ide.workspace.debugDir);
1916 ChangeWorkingDir(temp);
1919 ChangeWorkingDir(ide.workspace.projectDir);
1921 ide.SetPath(true, compiler, config, bitDepth);
1923 // TODO: This pollutes the environment, but at least it works
1924 // It shouldn't really affect the IDE as the PATH gets restored and other variables set for testing will unlikely cause problems
1925 // What is the proper solution for this? DualPipeOpenEnv?
1926 // gdb set environment commands don't seem to take effect
1927 for(e : ide.workspace.environmentVars)
1929 SetEnvironment(e.name, e.string);
1933 (compiler.targetPlatform == win32 && bitDepth == 64) ? "x86_64-w64-mingw32-gdb" :
1934 (compiler.targetPlatform == win32 && bitDepth == 32) ? "i686-w64-mingw32-gdb" :
1936 strcat(command, " -n -silent --interpreter=mi2"); //-async //\"%s\"
1938 gdbHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
1941 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start GDB\n");
1949 gdbProcessId = gdbHandle.GetProcessID();
1952 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get GDB process ID\n");
1958 serialSemaphore.Wait();
1963 //ChangeState(terminated);
1969 #if defined(__unix__)
1971 CreateTemporaryDir(progFifoDir, "ecereide");
1972 strcpy(progFifoPath, progFifoDir);
1973 PathCat(progFifoPath, "ideprogfifo");
1974 if(!mkfifo(progFifoPath, 0600))
1976 //fileCreated = true;
1981 ide.outputView.debugBox.Logf(createFIFOMsg, progFifoPath);
1986 progThread.terminate = false;
1987 progThread.Create();
1990 #if defined(__WIN32__)
1991 GdbCommand(false, "-gdb-set new-console on");
1994 GdbCommand(false, "-gdb-set verbose off");
1995 //GdbCommand(false, "-gdb-set exec-done-display on");
1996 GdbCommand(false, "-gdb-set step-mode off");
1997 GdbCommand(false, "-gdb-set unwindonsignal on");
1998 //GdbCommand(false, "-gdb-set shell on");
1999 GdbCommand(false, "set print elements 992");
2000 GdbCommand(false, "-gdb-set backtrace limit 100000");
2002 #if defined(__unix__)
2003 GdbCommand(false, "-inferior-tty-set %s", progFifoPath);
2006 GdbCommand(false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
2008 for(e : ide.workspace.environmentVars)
2010 GdbCommand(false, "set environment %s=%s", e.name, e.string);
2017 ChangeWorkingDir(oldDirectory);
2023 delete targetDirExp;
2029 if(gdbHandle && gdbProcessId)
2031 GdbCommand(false, "-gdb-exit");
2046 ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
2050 for(bp : ide.workspace.breakpoints)
2051 bp.inserted = false;
2053 bp.inserted = false;
2055 bpRunToCursor.inserted = false;
2057 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2058 ClearBreakDisplay();
2061 #if defined(__unix__)
2062 if(FileExists(progFifoPath)) //fileCreated)
2064 progThread.terminate = true;
2067 fifoFile.CloseInput();
2073 DeleteFile(progFifoPath);
2074 progFifoPath[0] = '\0';
2080 void WatchesCodeEditorLinkInit()
2083 char tempPath[MAX_LOCATION];
2084 char path[MAX_LOCATION];
2086 //void MakeFilePathProjectRelative(char * path, char * relativePath)
2087 if(!ide.projectView.project.GetRelativePath(activeFrame.file, tempPath))
2088 strcpy(tempPath, activeFrame.file);
2090 strcpy(path, ide.workspace.projectDir);
2091 PathCat(path, tempPath);
2092 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2095 for(srcDir : ide.workspace.sourceDirs)
2097 strcpy(path, srcDir);
2098 PathCat(path, tempPath);
2099 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2100 if(codeEditor) break;
2105 if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2106 activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);
2107 if(!activeFrame || !activeFrame.absoluteFile)
2110 codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal, false);
2113 codeEditor.inUseDebug = true;
2116 //watchesInit = true;
2119 void WatchesCodeEditorLinkRelease()
2125 codeEditor.inUseDebug = false;
2126 if(!codeEditor.visible)
2127 codeEditor.Destroy(0);
2133 bool ResolveWatch(Watch wh)
2135 bool result = false;
2148 char watchmsg[MAX_F_STRING];
2149 if(state == stopped && !codeEditor)
2150 wh.value = CopyString($"No source file found for selected frame");
2151 //if(codeEditor && state == stopped || state != stopped)
2154 Module backupPrivateModule;
2155 Context backupContext;
2156 Class backupThisClass;
2160 backupPrivateModule = GetPrivateModule();
2161 backupContext = GetCurrentContext();
2162 backupThisClass = GetThisClass();
2165 SetPrivateModule(codeEditor.privateModule);
2166 SetCurrentContext(codeEditor.globalContext);
2167 SetTopContext(codeEditor.globalContext);
2168 SetGlobalContext(codeEditor.globalContext);
2169 SetGlobalData(&codeEditor.globalData);
2172 exp = ParseExpressionString(wh.expression);
2174 if(exp && !parseError)
2176 char expString[4096];
2178 PrintExpression(exp, expString);
2180 if(GetPrivateModule())
2183 DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2184 ProcessExpressionType(exp);
2186 wh.type = exp.expType;
2189 DebugComputeExpression(exp);
2190 if(ExpressionIsError(exp))
2192 GDBFallBack(exp, expString);
2195 /*if(exp.hasAddress)
2197 char temp[MAX_F_STRING];
2198 sprintf(temp, "0x%x", exp.address);
2199 wh.address = CopyString(temp);
2200 // wh.address = CopyStringf("0x%x", exp.address);
2205 Type dataType = exp.expType;
2208 char temp[MAX_F_STRING];
2209 switch(dataType.kind)
2212 sprintf(temp, "%i", exp.val.c);
2215 sprintf(temp, "%i", exp.val.s);
2220 sprintf(temp, "%i", exp.val.i);
2223 sprintf(temp, "%i", exp.val.i64);
2226 sprintf(temp, "%i", exp.val.p);
2231 long v = (long)exp.val.f;
2232 sprintf(temp, "%i", v);
2237 long v = (long)exp.val.d;
2238 sprintf(temp, "%i", v);
2243 wh.intVal = CopyString(temp);
2244 switch(dataType.kind)
2247 sprintf(temp, "0x%x", exp.val.c);
2250 sprintf(temp, "0x%x", exp.val.s);
2254 sprintf(temp, "0x%x", exp.val.i);
2257 sprintf(temp, "0x%x", exp.val.i64);
2260 sprintf(temp, "0x%x", exp.val.i64);
2263 sprintf(temp, "0x%x", exp.val.p);
2268 long v = (long)exp.val.f;
2269 sprintf(temp, "0x%x", v);
2274 long v = (long)exp.val.d;
2275 sprintf(temp, "0x%x", v);
2280 wh.hexVal = CopyString(temp);
2281 switch(dataType.kind)
2284 sprintf(temp, "0o%o", exp.val.c);
2287 sprintf(temp, "0o%o", exp.val.s);
2291 sprintf(temp, "0o%o", exp.val.i);
2294 sprintf(temp, "0o%o", exp.val.i64);
2297 sprintf(temp, "0o%o", exp.val.i64);
2300 sprintf(temp, "0o%o", exp.val.p);
2305 long v = (long)exp.val.f;
2306 sprintf(temp, "0o%o", v);
2311 long v = (long)exp.val.d;
2312 sprintf(temp, "0o%o", v);
2317 wh.octVal = CopyString(temp);
2320 // WHATS THIS HERE ?
2321 if(exp.type == constantExp && exp.constant)
2322 wh.constant = CopyString(exp.constant);
2328 case symbolErrorExp:
2329 snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2331 case structMemberSymbolErrorExp:
2332 // todo get info as in next case (ExpClassMemberSymbolError)
2333 snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2335 case classMemberSymbolErrorExp:
2338 Expression memberExp = exp.member.exp;
2339 Identifier memberID = exp.member.member;
2340 Type type = memberExp.expType;
2343 _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2346 char string[256] = "";
2348 PrintTypeNoConst(type, string, false, true);
2349 classSym = FindClass(string);
2350 _class = classSym ? classSym.registered : null;
2353 snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2355 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2358 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2361 case memoryErrorExp:
2362 // Need to ensure when set to memoryErrorExp, constant is set
2363 snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2365 case dereferenceErrorExp:
2366 snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2368 case unknownErrorExp:
2369 snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2371 case noDebuggerErrorExp:
2372 snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2374 case debugStateErrorExp:
2375 snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2378 snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2382 // Temporary Code for displaying Strings
2383 if((exp.expType && ((exp.expType.kind == pointerType ||
2384 exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) ||
2385 (wh.type && wh.type.kind == classType && wh.type._class &&
2386 wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2387 !strcmp(wh.type._class.registered.name, "String")))
2390 if(exp.expType.kind != arrayType || exp.hasAddress)
2396 //char temp[MAX_F_STRING * 32];
2398 ExpressionType evalError = dummyExp;
2399 /*if(exp.expType.kind == arrayType)
2400 sprintf(temp, "(char*)0x%x", exp.address);
2402 sprintf(temp, "(char*)%s", exp.constant);*/
2404 //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2405 // address = strtoul(exp.constant, null, 0);
2406 address = _strtoui64(exp.constant, null, 0);
2407 //printf("%x\n", address);
2408 // snprintf(value, sizeof(value), "0x%08x ", address);
2410 if(address > 0xFFFFFFFFLL)
2411 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2413 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2414 value[sizeof(value)-1] = 0;
2417 strcat(value, $"Null string");
2421 len = strlen(value);
2423 while(!string && size > 2)
2425 string = GdbReadMemory(address, size);
2428 if(string && string[0])
2431 if(UTF8Validate(string))
2436 for(c = 0; (ch = string[c]) && c<4096; c++)
2439 value[len++] = '\0';
2444 ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2445 strcat(value, ") (ISO8859-1)");
2452 strcat(value, $"Empty string");
2456 strcat(value, $"Couldn't read memory");
2458 wh.value = CopyString(value);
2461 else if(wh.type && wh.type.kind == classType && wh.type._class &&
2462 wh.type._class.registered && wh.type._class.registered.type == enumClass)
2464 uint64 value = strtoul(exp.constant, null, 0);
2465 Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2466 EnumClassData enumeration = (EnumClassData)enumClass.data;
2468 for(item = enumeration.values.first; item; item = item.next)
2469 if((int)item.data == value)
2472 wh.value = CopyString(item.name);
2474 wh.value = CopyString($"Invalid Enum Value");
2475 result = (bool)atoi(exp.constant);
2477 else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class &&
2478 wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2485 if(exp.constant[0] == '\'')
2487 if((int)((byte *)exp.constant)[1] > 127)
2490 value = UTF8GetChar(exp.constant + 1, &nb);
2491 if(nb < 2) value = exp.constant[1];
2492 signedValue = value;
2496 signedValue = exp.constant[1];
2498 // Precomp Syntax error with boot strap here:
2499 byte b = (byte)(char)signedValue;
2500 value = (unichar) b;
2506 if(wh.type.kind == charType && wh.type.isSigned)
2508 signedValue = (int)(char)strtol(exp.constant, null, 0);
2510 // Precomp Syntax error with boot strap here:
2511 byte b = (byte)(char)signedValue;
2512 value = (unichar) b;
2517 value = (uint)strtoul(exp.constant, null, 0);
2518 signedValue = (int)value;
2522 UTF32toUTF8Len(&value, 1, charString, 5);
2524 snprintf(string, sizeof(string), "\'\\0' (0)");
2525 else if(value == '\t')
2526 snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2527 else if(value == '\n')
2528 snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2529 else if(value == '\r')
2530 snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2531 else if(wh.type.kind == charType && wh.type.isSigned)
2532 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2533 else if(value > 256 || wh.type.kind != charType)
2535 if(value > 0x10FFFF || !GetCharCategory(value))
2536 snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2538 snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2541 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2542 string[sizeof(string)-1] = 0;
2544 wh.value = CopyString(string);
2549 wh.value = CopyString(exp.constant);
2550 result = (bool)atoi(exp.constant);
2556 wh.value = PrintHexUInt64(exp.address);
2557 result = (bool)exp.address;
2561 char tempString[256];
2562 if(exp.member.memberType == propertyMember)
2563 snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2565 snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression,
2566 exp.type.OnGetString(tempString, null, null));
2572 snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2573 if(exp) FreeExpression(exp);
2576 SetPrivateModule(backupPrivateModule);
2577 SetCurrentContext(backupContext);
2578 SetTopContext(backupContext);
2579 SetGlobalContext(backupContext);
2580 SetThisClass(backupThisClass);
2583 // wh.value = CopyString("No source file found for selected frame");
2585 watchmsg[sizeof(watchmsg)-1] = 0;
2587 wh.value = CopyString(watchmsg);
2589 ide.watchesView.UpdateWatch(wh);
2593 void EvaluateWatches()
2595 for(wh : ide.workspace.watches)
2599 char * ::GdbEvaluateExpression(char * expression)
2603 GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2605 ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2609 // to be removed... use GdbReadMemory that returns a byte array instead
2610 char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
2616 printf("GdbReadMemoryString called with size = 0!\n");
2618 // GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
2619 if(GetRuntimePlatform() == win32)
2620 GdbCommand(false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
2622 GdbCommand(false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
2624 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
2628 byte * ::GdbReadMemory(uint64 address, int bytes)
2632 //GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
2633 if(GetRuntimePlatform() == win32)
2634 GdbCommand(false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
2636 GdbCommand(false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
2639 printf("GdbReadMemory called with bytes = 0!\n");
2642 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
2643 else if(eval.result && strcmp(eval.result, "N/A"))
2645 byte * result = new byte[bytes];
2646 byte * string = eval.result;
2650 result[c++] = (byte)strtol(string, &string, 10);
2666 void EventHit(GdbDataStop stopItem)
2668 bool conditionMet = true;
2669 Breakpoint bp = bpHit;
2671 if(!bp && bpRunToCursor)
2676 if(bp.type == user && stopItem.frame.line && bp.line != stopItem.frame.line)
2678 bp.line = stopItem.frame.line;
2679 ide.breakpointsView.UpdateBreakpoint(bp.row);
2680 ide.workspace.Save();
2686 case internalWinMain:
2687 GdbBreakpointsInsert();
2688 if(userBreakOnInternBreak)
2690 userBreakOnInternBreak = false;
2691 // Why was SelectFrame missing here?
2692 SelectFrame(activeFrameLevel);
2693 GoToStackFrameLine(activeFrameLevel, true);
2694 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
2698 GdbExecContinue(false);
2700 case internalModulesLoaded:
2702 GdbInsertInternalBreakpoints();
2703 GdbBreakpointsInsert();
2704 GdbExecContinue(false);
2706 case internalModuleLoad:
2707 GdbBreakpointsInsert();
2708 GdbExecContinue(false);
2713 conditionMet = ResolveWatch(bp.condition);
2715 if((bp.level == -1 || bp.level == frameCount-1) && conditionMet)
2720 ignoreBreakpoints = false;
2721 // Why was SelectFrame missing here?
2722 SelectFrame(activeFrameLevel);
2723 GoToStackFrameLine(activeFrameLevel, true);
2724 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
2730 GdbExecContinue(false);
2734 GdbExecContinue(false);
2735 ide.breakpointsView.UpdateBreakpoint(bp.row);
2740 ide.outputView.debugBox.Logf("Debugger Error: Breakpoint hit could not match breakpoint instance\n");
2744 if(symbols && bpRunToCursor.inserted)
2745 GdbCommand(false, "-break-delete %d", bpRunToCursor.bp.number);
2746 delete bpRunToCursor;
2750 void GdbThreadExit()
2752 if(state != terminated)
2754 ChangeState(terminated);
2755 targetProcessId = 0;
2756 ClearBreakDisplay();
2760 serialSemaphore.Release();
2765 ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
2766 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2768 HideDebuggerViews();
2770 //ChangeState(terminated);
2774 void GdbThreadMain(char * output)
2777 Array<char *> outTokens { minAllocSize = 50 };
2778 Array<char *> subTokens { minAllocSize = 50 };
2779 DebugListItem item { };
2780 DebugListItem item2 { };
2781 bool setWaitingForPID = false;
2783 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
2784 #ifdef GDB_DEBUG_CONSOLE
2785 Log(output); Log("\n");
2787 #ifdef GDB_DEBUG_OUTPUT
2789 int len = strlen(output);
2797 for(c = 0; c < len / 1024; c++)
2799 strncpy(tmp, start, 1024);
2800 ide.outputView.gdbBox.Logf("out: %s\n", tmp);
2803 ide.outputView.gdbBox.Logf("out: %s\n", start);
2807 ide.outputView.gdbBox.Logf("out: %s\n", output);
2811 #ifdef GDB_DEBUG_CONSOLE
2812 strcpy(lastGdbOutput, output);
2814 #ifdef GDB_DEBUG_GUI
2815 if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
2822 if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
2825 ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
2831 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
2833 //if(outTokens.count == 1)
2838 ChangeState(loaded);
2839 targetProcessId = 0;
2840 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2842 if(!strcmp(item.name, "reason"))
2844 char * reason = item.value;
2845 StripQuotes(reason, reason);
2846 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
2849 if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
2851 StripQuotes(item2.value, item2.value);
2852 if(!strcmp(item2.name, "exit-code"))
2853 exitCode = item2.value;
2859 HandleExit(reason, exitCode);
2863 DebuggerProtocolUnknown("Unknown kill reply", item.name);
2866 HandleExit(null, null);
2869 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2871 if(!strcmp(item.name, "bkpt"))
2873 sentBreakInsert = false;
2876 printf("problem\n");
2878 bpItem = GdbDataBreakpoint { };
2879 item.value = StripCurlies(item.value);
2880 TokenizeList(item.value, ',', subTokens);
2881 for(i = 0; i < subTokens.count; i++)
2883 if(TokenizeListItem(subTokens[i], item))
2885 StripQuotes(item.value, item.value);
2886 if(!strcmp(item.name, "number"))
2887 bpItem.number = atoi(item.value);
2888 else if(!strcmp(item.name, "type"))
2889 bpItem.type = CopyString(item.value);
2890 else if(!strcmp(item.name, "disp"))
2891 bpItem.disp = CopyString(item.value);
2892 else if(!strcmp(item.name, "enabled"))
2893 bpItem.enabled = (!strcmpi(item.value, "y"));
2894 else if(!strcmp(item.name, "addr"))
2895 bpItem.addr = CopyString(item.value);
2896 else if(!strcmp(item.name, "func"))
2897 bpItem.func = CopyString(item.value);
2898 else if(!strcmp(item.name, "file"))
2899 bpItem.file = item.value;
2900 else if(!strcmp(item.name, "line"))
2901 bpItem.line = atoi(item.value);
2902 else if(!strcmp(item.name, "at"))
2903 bpItem.at = CopyString(item.value);
2904 else if(!strcmp(item.name, "times"))
2905 bpItem.times = atoi(item.value);
2908 //breakType = bpValidation;
2909 //app.SignalEvent();
2910 subTokens.RemoveAll();
2912 else if(!strcmp(item.name, "BreakpointTable"))
2913 ide.outputView.debugBox.Logf("Debugger Error: Command reply BreakpointTable not handled\n");
2914 else if(!strcmp(item.name, "depth"))
2916 StripQuotes(item.value, item.value);
2917 frameCount = atoi(item.value);
2919 stackFrames.Free(Frame::Free);
2921 else if(!strcmp(item.name, "stack"))
2924 if(stackFrames.count)
2925 ide.callStackView.Logf("...\n");
2928 item.value = StripBrackets(item.value);
2929 TokenizeList(item.value, ',', subTokens);
2930 for(i = 0; i < subTokens.count; i++)
2932 if(TokenizeListItem(subTokens[i], item))
2934 if(!strcmp(item.name, "frame"))
2937 stackFrames.Add(frame);
2938 item.value = StripCurlies(item.value);
2939 ParseFrame(frame, item.value);
2940 if(frame.file && frame.from)
2941 DebuggerProtocolUnknown("Unexpected frame file and from members present", "");
2945 if(activeFrameLevel == -1)
2947 if(ide.projectView.IsModuleInProject(frame.file));
2949 if(frame.level != 0)
2951 //stopItem.frame = frame;
2952 breakType = selectFrame;
2955 activeFrame = frame;
2956 activeFrameLevel = frame.level;
2959 ide.callStackView.Logf("%3d ", frame.level);
2960 if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
2961 ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
2962 else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
2963 ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
2964 else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
2965 ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
2966 else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
2967 ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
2969 ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
2974 ide.callStackView.Logf("%3d ", frame.level);
2979 ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
2983 ide.callStackView.Logf("%s\n", frame.func);
2985 ide.callStackView.Logf($"unknown source\n");
2989 DebuggerProtocolUnknown("Unknown stack content", item.name);
2992 if(activeFrameLevel == -1)
2994 activeFrameLevel = 0;
2995 activeFrame = stackFrames.first;
2997 ide.callStackView.Home();
2999 subTokens.RemoveAll();
3001 /*else if(!strcmp(item.name, "frame"))
3004 item.value = StripCurlies(item.value);
3005 ParseFrame(&frame, item.value);
3007 else if(!strcmp(item.name, "thread-ids"))
3009 ide.threadsView.Clear();
3010 item.value = StripCurlies(item.value);
3011 TokenizeList(item.value, ',', subTokens);
3012 for(i = subTokens.count - 1; ; i--)
3014 if(TokenizeListItem(subTokens[i], item))
3016 if(!strcmp(item.name, "thread-id"))
3019 StripQuotes(item.value, item.value);
3020 value = atoi(item.value);
3021 ide.threadsView.Logf("%3d \n", value);
3024 DebuggerProtocolUnknown("Unknown threads content", item.name);
3029 ide.threadsView.Home();
3031 subTokens.RemoveAll();
3032 //if(!strcmp(outTokens[2], "number-of-threads"))
3034 else if(!strcmp(item.name, "new-thread-id"))
3036 StripQuotes(item.value, item.value);
3037 activeThread = atoi(item.value);
3039 else if(!strcmp(item.name, "value"))
3041 StripQuotes(item.value, item.value);
3042 eval.result = CopyString(item.value);
3043 eval.active = false;
3045 else if(!strcmp(item.name, "addr"))
3047 for(i = 2; i < outTokens.count; i++)
3049 if(TokenizeListItem(outTokens[i], item))
3051 if(!strcmp(item.name, "total-bytes"))
3053 StripQuotes(item.value, item.value);
3054 eval.bytes = atoi(item.value);
3056 else if(!strcmp(item.name, "next-row"))
3058 StripQuotes(item.value, item.value);
3059 eval.nextBlockAddress = _strtoui64(item.value, null, 0);
3061 else if(!strcmp(item.name, "memory"))
3065 //StripQuotes(item.value, item.value);
3066 item.value = StripBrackets(item.value);
3067 // this should be treated as a list...
3068 item.value = StripCurlies(item.value);
3069 TokenizeList(item.value, ',', subTokens);
3070 for(j = 0; j < subTokens.count; j++)
3072 if(TokenizeListItem(subTokens[j], item))
3074 if(!strcmp(item.name, "data"))
3076 item.value = StripBrackets(item.value);
3077 StripQuotes2(item.value, item.value);
3078 eval.result = CopyString(item.value);
3079 eval.active = false;
3083 subTokens.RemoveAll();
3088 else if(!strcmp(item.name, "source-path"))
3092 DebuggerProtocolUnknown("Unknown command reply", item.name);
3095 else if(!strcmp(outTokens[0], "^running"))
3097 waitingForPID = true;
3098 setWaitingForPID = true;
3100 else if(!strcmp(outTokens[0], "^exit"))
3102 ChangeState(terminated);
3103 // ide.outputView.debugBox.Logf("Exit\n");
3104 // ide.Update(null);
3106 serialSemaphore.Release();
3108 else if(!strcmp(outTokens[0], "^error"))
3112 sentBreakInsert = false;
3113 breakpointError = true;
3116 printf("problem\n");
3118 bpItem = GdbDataBreakpoint { };
3121 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3123 if(!strcmp(item.name, "msg"))
3125 StripQuotes(item.value, item.value);
3128 eval.active = false;
3130 if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3131 eval.error = symbolNotFound;
3132 else if(strstr(item.value, "Cannot access memory at address"))
3133 eval.error = memoryCantBeRead;
3135 eval.error = unknown;
3137 else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3140 else if(!strncmp(item.value, "Cannot access memory at address", 31))
3143 else if(!strcmp(item.value, "Cannot find bounds of current function"))
3145 ChangeState(stopped);
3146 gdbHandle.Printf("-exec-continue\n");
3148 else if(!strcmp(item.value, "ptrace: No such process."))
3150 ChangeState(loaded);
3151 targetProcessId = 0;
3153 else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3156 else if(!strcmp(item.value, "You can't do that without a process to debug."))
3158 ChangeState(loaded);
3159 targetProcessId = 0;
3161 else if(strstr(item.value, "No such file or directory."))
3163 ChangeState(loaded);
3164 targetProcessId = 0;
3166 else if(strstr(item.value, "During startup program exited with code "))
3168 ChangeState(loaded);
3169 targetProcessId = 0;
3174 if(strlen(item.value) < MAX_F_STRING)
3177 ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3181 ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3187 DebuggerProtocolUnknown("Unknown error content", item.name);
3190 DebuggerProtocolUnknown("Unknown result-record", outTokens[0]);
3192 outTokens.RemoveAll();
3195 DebuggerProtocolUnknown("Unknown status-async-output", outTokens[0]);
3198 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "=thread-group-created")) //=thread-group-created,id="7611"
3200 else if(!strcmp(outTokens[0], "=thread-created")) //=thread-created,id="1",group-id="7611"
3202 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"
3203 FGODetectLoadedLibraryForAddedProjectIssues(outTokens);
3205 DebuggerProtocolUnknown("Unknown notify-async-output", outTokens[0]);
3206 outTokens.RemoveAll();
3210 if(TokenizeList(output, ',', outTokens))
3212 if(!strcmp(outTokens[0],"*running"))
3214 waitingForPID = true;
3215 setWaitingForPID = true;
3217 else if(!strcmp(outTokens[0], "*stopped"))
3220 ChangeState(stopped);
3222 for(tk = 1; tk < outTokens.count; tk++)
3224 if(TokenizeListItem(outTokens[tk], item))
3226 if(!strcmp(item.name, "reason"))
3228 char * reason = item.value;
3229 StripQuotes(reason, reason);
3230 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3233 if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3236 StripQuotes(item2.value, item2.value);
3237 if(!strcmp(item2.name, "exit-code"))
3238 exitCode = item2.value;
3244 HandleExit(reason, exitCode);
3246 else if(!strcmp(reason, "breakpoint-hit"))
3250 printf("problem\n");
3252 stopItem = GdbDataStop { };
3254 for(i = tk+1; i < outTokens.count; i++)
3256 TokenizeListItem(outTokens[i], item);
3257 StripQuotes(item.value, item.value);
3258 if(!strcmp(item.name, "bkptno"))
3259 stopItem.bkptno = atoi(item.value);
3260 else if(!strcmp(item.name, "thread-id"))
3261 stopItem.threadid = atoi(item.value);
3262 else if(!strcmp(item.name, "frame"))
3264 item.value = StripCurlies(item.value);
3265 ParseFrame(stopItem.frame, item.value);
3268 DebuggerProtocolUnknown("Unknown breakpoint hit item name", item.name);
3273 else if(!strcmp(reason, "end-stepping-range"))
3277 printf("problem\n");
3279 stopItem = GdbDataStop { };
3281 for(i = tk+1; i < outTokens.count; i++)
3283 TokenizeListItem(outTokens[i], item);
3284 StripQuotes(item.value, item.value);
3285 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);
3292 else if(!strcmp(item.name, "reason"))
3294 else if(!strcmp(item.name, "bkptno"))
3297 DebuggerProtocolUnknown("Unknown end of stepping range item name", item.name);
3303 else if(!strcmp(reason, "function-finished"))
3307 printf("problem\n");
3309 stopItem = GdbDataStop { };
3310 stopItem.reason = CopyString(reason);
3312 for(i = tk+1; i < outTokens.count; i++)
3314 TokenizeListItem(outTokens[i], item);
3315 StripQuotes(item.value, item.value);
3316 if(!strcmp(item.name, "thread-id"))
3317 stopItem.threadid = atoi(item.value);
3318 else if(!strcmp(item.name, "frame"))
3320 item.value = StripCurlies(item.value);
3321 ParseFrame(stopItem.frame, item.value);
3323 else if(!strcmp(item.name, "gdb-result-var"))
3324 stopItem.gdbResultVar = CopyString(item.value);
3325 else if(!strcmp(item.name, "return-value"))
3326 stopItem.returnValue = CopyString(item.value);
3328 DebuggerProtocolUnknown("Unknown function finished item name", item.name);
3331 event = functionEnd;
3334 else if(!strcmp(reason, "signal-received"))
3338 printf("problem\n");
3340 stopItem = GdbDataStop { };
3341 stopItem.reason = CopyString(reason);
3343 for(i = tk+1; i < outTokens.count; i++)
3345 TokenizeListItem(outTokens[i], item);
3346 StripQuotes(item.value, item.value);
3347 if(!strcmp(item.name, "signal-name"))
3348 stopItem.name = CopyString(item.value);
3349 else if(!strcmp(item.name, "signal-meaning"))
3350 stopItem.meaning = CopyString(item.value);
3351 else if(!strcmp(item.name, "thread-id"))
3352 stopItem.threadid = atoi(item.value);
3353 else if(!strcmp(item.name, "frame"))
3355 item.value = StripCurlies(item.value);
3356 ParseFrame(stopItem.frame, item.value);
3359 DebuggerProtocolUnknown("Unknown signal reveived item name", item.name);
3361 if(!strcmp(stopItem.name, "SIGTRAP"))
3380 else if(!strcmp(reason, "watchpoint-trigger"))
3381 DebuggerProtocolUnknown("Reason watchpoint trigger not handled", "");
3382 else if(!strcmp(reason, "read-watchpoint-trigger"))
3383 DebuggerProtocolUnknown("Reason read watchpoint trigger not handled", "");
3384 else if(!strcmp(reason, "access-watchpoint-trigger"))
3385 DebuggerProtocolUnknown("Reason access watchpoint trigger not handled", "");
3386 else if(!strcmp(reason, "watchpoint-scope"))
3387 DebuggerProtocolUnknown("Reason watchpoint scope not handled", "");
3388 else if(!strcmp(reason, "location-reached"))
3389 DebuggerProtocolUnknown("Reason location reached not handled", "");
3391 DebuggerProtocolUnknown("Unknown reason", reason);
3399 DebuggerProtocolUnknown("Unknown exec-async-output", outTokens[0]);
3400 outTokens.RemoveAll();
3403 if(!strcmpi(output, "(gdb) "))
3407 char exeFile[MAX_LOCATION];
3408 int oldProcessID = targetProcessId;
3409 GetLastDirectory(targetFile, exeFile);
3413 targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3414 if(targetProcessId || gdbHandle.Peek()) break;
3419 ChangeState(running);
3420 else if(!oldProcessID)
3422 ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3423 // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3424 gdbHandle.Printf("-gdb-exit\n");
3426 ChangeState(terminated); //loaded;
3431 for(bp : ide.workspace.breakpoints)
3432 bp.inserted = false;
3435 bp.inserted = false;
3437 bpRunToCursor.inserted = false;
3439 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3440 ClearBreakDisplay();
3442 #if defined(__unix__)
3443 if(FileExists(progFifoPath)) //fileCreated)
3445 progThread.terminate = true;
3448 fifoFile.CloseInput();
3455 DeleteFile(progFifoPath);
3456 progFifoPath[0] = '\0';
3463 serialSemaphore.Release();
3466 DebuggerProtocolUnknown($"Unknown prompt", output);
3470 if(!strncmp(output, "&\"warning:", 10))
3473 content = strstr(output, "\"");
3474 StripQuotes(content, content);
3475 content = strstr(content, ":");
3481 ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3488 DebuggerProtocolUnknown($"Unknown output", output);
3490 if(!setWaitingForPID)
3491 waitingForPID = false;
3492 setWaitingForPID = false;
3500 // From GDB Output functions
3501 void FGODetectLoadedLibraryForAddedProjectIssues(Array<char *> outTokens)
3503 char path[MAX_LOCATION] = "";
3504 char file[MAX_FILENAME] = "";
3506 DebugListItem item { };
3507 for(token : outTokens)
3509 if(TokenizeListItem(token, item))
3511 if(!strcmp(item.name, "target-name"))
3513 StripQuotes(item.value, path);
3514 MakeSystemPath(path);
3515 GetLastDirectory(path, file);
3517 else if(!strcmp(item.name, "symbols-loaded"))
3519 symbolsLoaded = (atoi(item.value) == 1);
3524 if(path[0] && file[0])
3526 for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
3530 char prjTargetPath[MAX_LOCATION];
3531 char prjTargetFile[MAX_FILENAME];
3532 DirExpression targetDirExp = prj.GetTargetDir(currentCompiler, prj.config, bitDepth);
3533 strcpy(prjTargetPath, prj.topNode.path);
3534 PathCat(prjTargetPath, targetDirExp.dir);
3535 prjTargetFile[0] = '\0';
3536 prj.CatTargetFileName(prjTargetFile, currentCompiler, prj.config);
3537 PathCat(prjTargetPath, prjTargetFile);
3538 MakeSystemPath(prjTargetPath);
3540 match = !fstrcmp(prjTargetFile, file);
3541 if(!match && (dot = strstr(prjTargetFile, ".so.")))
3543 char * dot3 = strstr(dot+4, ".");
3547 match = !fstrcmp(prjTargetFile, file);
3552 match = !fstrcmp(prjTargetFile, file);
3557 // TODO: nice visual feedback to better warn user. use some ide notification system or other means.
3558 /* -- this is disabled because we can't trust gdb's symbols-loaded="0" field for =library-loaded (http://sourceware.org/bugzilla/show_bug.cgi?id=10693)
3560 ide.outputView.debugBox.Logf($"Attention! No symbols for loaded library %s matched to the %s added project.\n", path, prj.topNode.name);
3562 match = !fstrcmp(prjTargetPath, path);
3563 if(!match && (dot = strstr(prjTargetPath, ".so.")))
3565 char * dot3 = strstr(dot+4, ".");
3569 match = !fstrcmp(prjTargetPath, path);
3574 match = !fstrcmp(prjTargetPath, path);
3578 ide.outputView.debugBox.Logf($"Loaded library %s doesn't match the %s target of the %s added project.\n", path, prjTargetPath, prj.topNode.name);
3585 void RunToCursorPrepare(char * absoluteFilePath, char * relativeFilePath, int lineNumber, bool atSameLevel)
3589 //bpRunToCursor.Free();
3590 bpRunToCursor = Breakpoint { };
3593 bpRunToCursor = Breakpoint { };
3595 if(absoluteFilePath)
3596 bpRunToCursor.absoluteFilePath = CopyString(absoluteFilePath);
3597 if(relativeFilePath)
3598 bpRunToCursor.relativeFilePath = CopyString(relativeFilePath);
3599 bpRunToCursor.line = lineNumber;
3600 bpRunToCursor.type = runToCursor;
3601 bpRunToCursor.enabled = true;
3602 bpRunToCursor.condition = null;
3603 bpRunToCursor.ignore = 0;
3604 bpRunToCursor.level = atSameLevel ? frameCount - activeFrameLevel -1 : -1;
3607 ExpressionType ::DebugEvalExpTypeError(char * result)
3613 case symbolNotFound:
3614 return symbolErrorExp;
3615 case memoryCantBeRead:
3616 return memoryErrorExp;
3618 return unknownErrorExp;
3621 char * ::EvaluateExpression(char * expression, ExpressionType * error)
3624 if(ide.projectView && ide.debugger.state == stopped)
3626 result = GdbEvaluateExpression(expression);
3627 *error = DebugEvalExpTypeError(result);
3632 *error = noDebuggerErrorExp;
3637 char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
3640 char * result = GdbReadMemoryString(address, size, format, 1, 1);
3641 if(!result || !strcmp(result, "N/A"))
3642 *error = memoryErrorExp;
3644 *error = DebugEvalExpTypeError(result);
3649 class GdbThread : Thread
3655 static char output[4096];
3656 Array<char> dynamicBuffer { minAllocSize = 4096 };
3657 DualPipe oldGdbHandle = gdbHandle;
3658 incref oldGdbHandle;
3661 while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
3665 result = gdbHandle.Read(output, 1, sizeof(output));
3667 if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
3674 for(c = 0; c<result; c++)
3676 if(output[c] == '\n')
3678 int pos = dynamicBuffer.size;
3679 dynamicBuffer.size += c - start;
3680 memcpy(&dynamicBuffer[pos], output + start, c - start);
3681 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3682 // COMMENTED OUT DUE TO ISSUE #135, FIXED
3683 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
3684 dynamicBuffer.size++;
3685 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
3687 // printf("%s\n", dynamicBuffer.array);
3689 debugger.GdbThreadMain(&dynamicBuffer[0]);
3690 dynamicBuffer.size = 0;
3696 int pos = dynamicBuffer.size;
3697 dynamicBuffer.size += c - start;
3698 memcpy(&dynamicBuffer[pos], output + start, c - start);
3704 printf("Got end of file from GDB!\n");
3708 delete dynamicBuffer;
3709 //if(oldGdbHandle == gdbHandle)
3710 debugger.GdbThreadExit();
3711 delete oldGdbHandle;
3717 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
3718 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
3720 #if defined(__unix__)
3725 #include <sys/types.h>
3730 class ProgramThread : Thread
3736 bool fileCreated = false;
3738 static char output[1000];
3741 /*if(!mkfifo(progFifoPath, mask))
3748 ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
3752 if(FileExists(progFifoPath)) //fileCreated)
3754 fifoFile = FileOpen(progFifoPath, read);
3758 ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
3763 fd = fileno((FILE *)fifoFile.input);
3764 //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
3768 while(!terminate && fifoFile && !fifoFile.Eof())
3771 struct timeval time;
3779 selectResult = select(fd + 1, &rs, null, null, &time);
3780 if(FD_ISSET(fd, &rs))
3782 int result = (int)read(fd, output, sizeof(output)-1);
3783 if(!result || (result < 0 && errno != EAGAIN))
3787 output[result] = '\0';
3788 if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
3791 ide.outputView.debugBox.Log(output);
3800 //fifoFile.CloseInput();
3803 ide.outputView.debugBox.Log("\n");
3807 if(FileExists(progFifoPath)) //fileCreated)
3809 DeleteFile(progFifoPath);
3810 progFifoPath[0] = '\0';
3818 class Argument : struct
3820 Argument prev, next;
3836 class Frame : struct
3845 property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
3847 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3848 char * absoluteFile;
3857 delete absoluteFile;
3858 args.Free(Argument::Free);
3867 class GdbDataStop : struct
3884 char * gdbResultVar;
3894 if(!strcmp(reason, "signal-received"))
3899 else if(!strcmp(reason, "function-finished"))
3901 delete gdbResultVar;
3906 if(frame) frame.Free();
3915 class GdbDataBreakpoint : struct
3924 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
3939 ~GdbDataBreakpoint()
3945 class Breakpoint : struct
3949 char * relativeFilePath;
3950 char * absoluteFilePath;
3959 BreakpointType type;
3962 GdbDataBreakpoint bp;
3964 char * LocationToString()
3966 char location[MAX_LOCATION+20];
3967 snprintf(location, sizeof(location), "%s:%d", relativeFilePath, line);
3968 location[sizeof(location)-1] = 0;
3969 #if defined(__WIN32__)
3970 ChangeCh(location, '/', '\\');
3972 return CopyString(location);
3977 if(relativeFilePath && relativeFilePath[0])
3979 f.Printf(" * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
3981 f.Printf(" ~ %s\n", condition.expression);
3990 delete relativeFilePath;
3991 delete absoluteFilePath;
4001 class Watch : struct
4012 f.Printf(" ~ %s\n", expression);
4036 class DebugListItem : struct
4042 struct DebugEvaluationData
4047 uint64 nextBlockAddress;
4049 DebuggerEvaluationError error;
4052 class CodeLocation : struct
4055 char * absoluteFile;
4058 CodeLocation ::ParseCodeLocation(char * location)
4062 char * colon = null;
4064 char loc[MAX_LOCATION];
4065 strcpy(loc, location);
4066 for(temp = loc; temp = strstr(temp, ":"); temp++)
4074 int line = atoi(colon);
4077 CodeLocation codloc { line = line };
4078 codloc.file = CopyString(loc);
4079 codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
4091 delete absoluteFile;
4101 void GDBFallBack(Expression exp, String expString)
4104 ExpressionType evalError = dummyExp;
4105 result = Debugger::EvaluateExpression(expString, &evalError);
4108 exp.constant = result;
4109 exp.type = constantExp;