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 static bool CheckCommandAvailable(const char * command)
309 bool available = false;
311 char * name = new char[MAX_FILENAME];
312 char * pathVar = new char[maxPathLen];
314 GetEnvironment("PATH", pathVar, maxPathLen);
315 count = TokenizeWith(pathVar, sizeof(paths) / sizeof(char *), paths, pathListSep, false);
316 strcpy(name, command);
320 const char * extensions[3] = { "exe", "com", "bat", null };
321 for(e=0; extensions[e]; e++)
323 ChangeExtension(name, extensions[e], name);
325 for(c=0; c<count; c++)
327 FileListing fl { paths[c] };
330 if(fl.stats.attribs.isFile && !fstrcmp(fl.name, name))
348 // define GdbGetLineSize = 1638400;
349 define GdbGetLineSize = 5638400;
350 #if defined(__unix__)
351 char progFifoPath[MAX_LOCATION];
352 char progFifoDir[MAX_LOCATION];
355 enum DebuggerState { none, prompt, loaded, running, stopped, terminated };
356 enum DebuggerEvent { none, hit, breakEvent, signal, stepEnd, functionEnd, exit };
357 enum DebuggerAction { none, internal, restart, stop, selectFrame }; //, bpValidation
358 enum BreakpointType { none, internalMain, internalWinMain, internalModulesLoaded, user, runToCursor, internalModuleLoad };
359 enum DebuggerEvaluationError { none, symbolNotFound, memoryCantBeRead, unknown };
361 FileDialog debuggerFileDialog { type = selectDir };
363 static DualPipe vgTargetHandle;
364 static File vgLogFile;
365 static char vgLogPath[MAX_LOCATION];
366 static DualPipe gdbHandle;
367 static DebugEvaluationData eval { };
369 static int targetProcessId;
371 static bool gdbReady;
372 static bool breakpointError;
376 Semaphore serialSemaphore { };
381 //bool breakpointsInserted;
383 bool sentBreakInsert;
384 bool ignoreBreakpoints;
385 bool userBreakOnInternBreak;
393 int activeFrameLevel;
404 DebuggerAction breakType;
405 //DebuggerCommand lastCommand; // THE COMPILER COMPILES STUFF THAT DOES NOT EXIST???
407 GdbDataStop stopItem;
408 GdbDataBreakpoint bpItem;
411 List<Breakpoint> sysBPs { };
412 Breakpoint bpRunToCursor;
418 CompilerConfig currentCompiler;
419 ProjectConfig prjConfig;
422 CodeEditor codeEditor;
424 ValgrindLogThread vgLogThread { debugger = this };
425 ValgrindTargetThread vgTargetThread { debugger = this };
426 GdbThread gdbThread { debugger = this };
429 delay = 0.0, userData = this;
433 bool monitor = false;
434 DebuggerEvent curEvent = event;
435 GdbDataStop stopItem = this.stopItem;
441 this.stopItem = null;
444 if(curEvent && curEvent != exit)
447 printf("No stop item\n");
455 Restart(currentCompiler, prjConfig, bitDepth, usingValgrind);
464 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
465 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
466 if(activeFrame.level == activeFrameLevel)
472 // GdbCommand(false, "-break-info %d", bpItem.number);
482 activeThread = stopItem.threadid;
483 GdbCommand(false, "-thread-list-ids");
488 Breakpoint bp = null;
490 for(i : ide.workspace.breakpoints; i.bp && i.bp.number == stopItem.bkptno)
497 for(i : sysBPs; i.bp && i.bp.number == stopItem.bkptno)
503 if(bp && bp.type != user && stopItem && stopItem.frame)
505 // In case the user put a breakpoint where an internal breakpoint is, avoid the confusion...
506 for(i : ide.workspace.breakpoints)
508 if(i.bp && i.line == stopItem.frame.line && !fstrcmp(i.absoluteFilePath, stopItem.frame.absoluteFile))
517 if(!(!userBreakOnInternBreak &&
518 bp && (bp.type == internalMain || bp.type == internalWinMain ||
519 bp.type == internalModulesLoaded || bp.type == internalModuleLoad)))
521 hitThread = stopItem.threadid;
525 signalThread = stopItem.threadid;
537 activeThread = stopItem.threadid;
538 GdbCommand(false, "-thread-list-ids");
540 if(activeFrameLevel > 0)
541 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
543 WatchesCodeEditorLinkInit();
553 ide.outputView.debugBox.Logf($"Signal received: %s - %s\n", stopItem.name, stopItem.meaning);
554 ide.outputView.debugBox.Logf(" %s:%d\n", (s = CopySystemPath(stopItem.frame.file)), stopItem.frame.line);
560 // Why was SelectFrame missing here?
561 SelectFrame(activeFrameLevel);
562 GoToStackFrameLine(activeFrameLevel, curEvent == signal || curEvent == stepEnd /*false*/);
563 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
565 if(curEvent == signal)
566 ide.outputView.Show();
567 if(curEvent == signal || curEvent == breakEvent)
569 if(curEvent == breakEvent)
570 ide.threadsView.Show();
571 ide.callStackView.Show();
573 ide.ShowCodeEditor();
574 if(curEvent == breakEvent)
575 ide.callStackView.Activate();
583 ignoreBreakpoints = false;
594 #ifdef GDB_DEBUG_CONSOLE
595 char lastGdbOutput[GdbGetLineSize];
597 #if defined(__unix__)
598 ProgramThread progThread { };
601 void ChangeState(DebuggerState value)
603 bool same = value == state;
604 // if(same) PrintLn("Debugger::ChangeState -- changing to same state");
606 if(!same && ide) ide.AdjustDebugMenus();
611 // Stop(); // Don't need to call Stop here, because ~ProjectView() will call it explicitly.
613 stackFrames.Free(Frame::Free);
623 waitingForPID = false;
628 sentBreakInsert = false;
629 ignoreBreakpoints = false;
630 userBreakOnInternBreak = false;
633 activeFrameLevel = 0;
650 bpRunToCursor = null;
653 delete currentCompiler;
657 /*GdbThread gdbThread
663 ideProcessId = Process_GetCurrentProcessId();
665 sysBPs.Add(Breakpoint { type = internalMain, enabled = true, level = -1 });
666 #if defined(__WIN32__)
667 sysBPs.Add(Breakpoint { type = internalWinMain, enabled = true, level = -1 });
669 sysBPs.Add(Breakpoint { type = internalModulesLoaded, enabled = true, level = -1 });
670 sysBPs.Add(Breakpoint { type = internalModuleLoad, enabled = true, level = -1 });
682 property bool isActive { get { return state == running || state == stopped; } }
683 property bool isPrepared { get { return state == loaded || state == running || state == stopped; } }
687 GdbExecContinue(true);
695 GdbDebugBreak(false);
707 GdbDebugBreak(false);
721 void Restart(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
729 GdbDebugBreak(false);
736 if(!GdbInit(compiler, config, bitDepth, useValgrind))
744 bool GoToCodeLine(char * location)
747 codloc = CodeLocation::ParseCodeLocation(location);
750 CodeEditor editor = (CodeEditor)ide.OpenFile(codloc.absoluteFile, normal, true, null, no, normal, false);
753 EditBox editBox = editor.editBox;
754 editBox.GoToLineNum(codloc.line - 1);
755 editBox.GoToPosition(editBox.line, codloc.line - 1, 0);
762 bool GoToStackFrameLine(int stackLevel, bool askForLocation)
766 char filePath[MAX_LOCATION];
767 char sourceDir[MAX_LOCATION];
769 CodeEditor editor = null;
770 if(stackLevel == -1) // this (the two lines) is part of that fix that I would not put in for some time
772 for(frame = stackFrames.first; frame; frame = frame.next)
773 if(frame.level == stackLevel)
777 ide.callStackView.Show();
779 if(!frame.absoluteFile && frame.file)
780 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
781 if(!frame.absoluteFile && askForLocation && frame.file)
784 char title[MAX_LOCATION];
785 snprintf(title, sizeof(title), $"Provide source file location for %s", (s = CopySystemPath(frame.file)));
786 title[sizeof(title)-1] = 0;
788 if(SourceDirDialog(title, ide.workspace.projectDir, frame.file, sourceDir))
790 AddSourceDir(sourceDir);
791 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
794 if(frame.absoluteFile)
795 editor = (CodeEditor)ide.OpenFile(frame.absoluteFile, normal, true, null, no, normal, false);
797 if(editor && frame.line)
799 EditBox editBox = editor.editBox;
800 editBox.GoToLineNum(frame.line - 1);
801 editBox.GoToPosition(editBox.line, frame.line - 1, 0);
809 void SelectThread(int thread)
813 if(thread != activeThread)
815 activeFrameLevel = -1;
816 ide.callStackView.Clear();
817 GdbCommand(false, "-thread-select %d", thread);
819 // Why was SelectFrame missing here?
820 SelectFrame(activeFrameLevel);
821 GoToStackFrameLine(activeFrameLevel, true);
822 WatchesCodeEditorLinkRelease();
823 WatchesCodeEditorLinkInit();
827 ide.callStackView.Show();
831 void SelectFrame(int frame)
835 if(frame != activeFrameLevel || !codeEditor || !codeEditor.visible)
837 activeFrameLevel = frame; // there is no active frame number in the gdb reply
838 GdbCommand(false, "-stack-select-frame %d", activeFrameLevel);
839 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
840 if(activeFrame.level == activeFrameLevel)
843 WatchesCodeEditorLinkRelease();
844 WatchesCodeEditorLinkInit();
851 void HandleExit(char * reason, char * code)
853 bool returnedExitCode = false;
854 char verboseExitCode[128];
856 ChangeState(loaded); // this state change seems to be superfluous, might be in case of gdb crash
861 snprintf(verboseExitCode, sizeof(verboseExitCode), $" with exit code %s", code);
862 verboseExitCode[sizeof(verboseExitCode)-1] = 0;
865 verboseExitCode[0] = '\0';
869 // ClearBreakDisplay();
873 for(wh : ide.workspace.watches)
875 if(wh.type) FreeType(wh.type);
878 ide.watchesView.UpdateWatch(wh);
882 #if defined(__unix__)
885 progThread.terminate = true;
888 fifoFile.CloseInput();
898 char program[MAX_LOCATION];
899 GetSystemPathBuffer(program, targetFile);
901 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
902 else if(!strcmp(reason, "exited-normally"))
903 ide.outputView.debugBox.Logf($"The program %s has exited normally%s.\n", program, verboseExitCode);
904 else if(!strcmp(reason, "exited"))
905 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
906 else if(!strcmp(reason, "exited-signalled"))
907 ide.outputView.debugBox.Logf($"The program %s has exited with a signal%s.\n", program, verboseExitCode);
909 ide.outputView.debugBox.Logf($"The program %s has exited (gdb provided an unknown reason)%s.\n", program, verboseExitCode);
914 void Start(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
916 ide.outputView.debugBox.Clear();
921 if(!GdbInit(compiler, config, bitDepth, useValgrind))
929 void StepInto(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
935 if(!GdbInit(compiler, config, bitDepth, useValgrind))
938 ide.outputView.ShowClearSelectTab(debug);
939 ide.outputView.debugBox.Logf($"Starting debug mode\n");
940 userBreakOnInternBreak = true;
949 void StepOver(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool ignoreBkpts)
955 if(!GdbInit(compiler, config, bitDepth, useValgrind))
958 ide.outputView.ShowClearSelectTab(debug);
959 ide.outputView.debugBox.Logf($"Starting debug mode\n");
960 ignoreBreakpoints = ignoreBkpts;
961 userBreakOnInternBreak = true;
965 ignoreBreakpoints = ignoreBkpts;
966 if(ignoreBreakpoints)
967 GdbBreakpointsDelete(true);
973 void StepOut(bool ignoreBkpts)
977 ignoreBreakpoints = ignoreBkpts;
978 if(ignoreBreakpoints)
979 GdbBreakpointsDelete(true);
984 void RunToCursor(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, char * absoluteFilePath, int lineNumber, bool ignoreBkpts, bool atSameLevel)
986 char relativeFilePath[MAX_LOCATION];
987 DebuggerState oldState = state;
988 ignoreBreakpoints = ignoreBkpts;
989 if(!ide.projectView.project.GetRelativePath(absoluteFilePath, relativeFilePath))
990 strcpy(relativeFilePath, absoluteFilePath);
995 Start(compiler, config, bitDepth, useValgrind);
1002 ide.outputView.ShowClearSelectTab(debug);
1003 ide.outputView.debugBox.Logf($"Starting debug mode\n");
1005 RunToCursorPrepare(absoluteFilePath, relativeFilePath, lineNumber, atSameLevel);
1006 sentBreakInsert = true;
1007 GdbCommand(false, "-break-insert %s:%d", relativeFilePath, lineNumber);
1008 bpRunToCursor.bp = bpItem;
1010 bpRunToCursor.inserted = (bpRunToCursor.bp.number != 0);
1011 ValidateBreakpoint(bpRunToCursor);
1018 if(ignoreBreakpoints)
1019 GdbBreakpointsDelete(false);
1023 if(ignoreBreakpoints)
1024 GdbBreakpointsDelete(false);
1025 GdbExecContinue(true);
1031 void GetCallStackCursorLine(bool * error, int * lineCursor, int * lineTopFrame)
1033 if(activeFrameLevel == -1)
1041 *error = signalOn && activeThread == signalThread;
1042 *lineCursor = activeFrameLevel - ((frameCount > 192 && activeFrameLevel > 191) ? frameCount - 192 - 1 : 0) + 1;
1043 *lineTopFrame = activeFrameLevel ? 1 : 0;
1047 int GetMarginIconsLineNumbers(char * fileName, int lines[], bool enabled[], int max, bool * error, int * lineCursor, int * lineTopFrame)
1049 char winFilePath[MAX_LOCATION];
1050 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1052 Iterator<Breakpoint> it { ide.workspace.breakpoints };
1053 while(it.Next() && count < max)
1055 Breakpoint bp = it.data;
1058 if(bp.absoluteFilePath && bp.absoluteFilePath[0] && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1060 lines[count] = bp.line;
1061 enabled[count] = bp.enabled;
1066 if(activeFrameLevel == -1)
1074 *error = signalOn && activeThread == signalThread;
1075 if(activeFrame && activeFrame.absoluteFile && !fstrcmp(absoluteFilePath, activeFrame.absoluteFile))
1076 *lineCursor = activeFrame.line;
1079 if(activeFrame && stopItem && stopItem.frame && activeFrame.level == stopItem.frame.level)
1081 else if(stopItem && stopItem.frame && stopItem.frame.absoluteFile && !fstrcmp(absoluteFilePath, stopItem.frame.absoluteFile))
1082 *lineTopFrame = stopItem.frame.line;
1086 if(*lineTopFrame == *lineCursor && *lineTopFrame)
1092 void ChangeWatch(DataRow row, char * expression)
1094 Watch wh = (Watch)row.tag;
1097 delete wh.expression;
1099 wh.expression = CopyString(expression);
1102 Iterator<Watch> it { ide.workspace.watches };
1104 ide.workspace.watches.Delete(it.pointer);
1110 row.tag = (int64)wh;
1111 ide.workspace.watches.Add(wh);
1113 wh.expression = CopyString(expression);
1115 ide.workspace.Save();
1116 //if(expression && state == stopped)
1121 void MoveIcons(char * fileName, int lineNumber, int move, bool start)
1123 char winFilePath[MAX_LOCATION];
1124 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1127 for(bpLink = ide.workspace.breakpoints.first; bpLink; bpLink = next)
1129 Breakpoint bp = (Breakpoint)bpLink.data;
1132 if(bp.type == user && bp.absoluteFilePath && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1134 if(bp.line > lineNumber || (bp.line == lineNumber && start))
1136 if(move < 0 && (bp.line < lineNumber - move))
1137 ide.workspace.RemoveBreakpoint(bp);
1141 ide.breakpointsView.UpdateBreakpoint(bp.row);
1142 ide.workspace.Save();
1148 // moving code cursors is futile, on next step, stop, hit, cursors will be offset anyways
1151 bool SourceDirDialog(char * title, char * startDir, char * test, char * sourceDir)
1155 String srcDir = null;
1157 debuggerFileDialog.text = title;
1158 debuggerFileDialog.currentDirectory = startDir;
1159 debuggerFileDialog.master = ide;
1161 while(debuggerFileDialog.Modal())
1163 strcpy(sourceDir, debuggerFileDialog.filePath);
1164 if(!fstrcmp(ide.workspace.projectDir, sourceDir) &&
1165 MessageBox { type = yesNo, master = ide,
1166 contents = $"This is the project directory.\nWould you like to try again?",
1167 text = $"Invalid Source Directory" }.Modal() == no)
1171 for(dir : ide.workspace.sourceDirs)
1173 if(!fstrcmp(dir, sourceDir))
1181 MessageBox { type = yesNo, master = ide,
1182 contents = $"This source directory is already specified.\nWould you like to try again?",
1183 text = $"Invalid Source Directory" }.Modal() == no)
1189 char file[MAX_LOCATION];
1190 strcpy(file, sourceDir);
1191 PathCat(file, test);
1192 result = FileExists(file);
1194 MessageBox { type = yesNo, master = ide,
1195 contents = $"Unable to locate source file.\nWould you like to try again?",
1196 text = $"Invalid Source Directory" }.Modal() == no)
1210 void AddSourceDir(char * sourceDir)
1212 ide.workspace.sourceDirs.Add(CopyString(sourceDir));
1213 ide.workspace.Save();
1217 DebuggerState oldState = state;
1222 GdbDebugBreak(true);
1225 GdbCommand(false, "-environment-directory \"%s\"", sourceDir);
1228 if(oldState == running)
1229 GdbExecContinue(false);
1233 void ToggleBreakpoint(char * fileName, int lineNumber, Project prj)
1235 char winFilePath[MAX_LOCATION];
1236 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1237 char absolutePath[MAX_LOCATION];
1238 char relativePath[MAX_LOCATION];
1239 char sourceDir[MAX_LOCATION];
1240 Breakpoint bp = null;
1242 strcpy(absolutePath, absoluteFilePath);
1243 for(i : ide.workspace.breakpoints; i.type == user && i.absoluteFilePath && !fstrcmp(i.absoluteFilePath, absolutePath) && i.line == lineNumber)
1252 ide.workspace.RemoveBreakpoint(bp);
1260 // FIXED: This is how it should have been... Source locations are only for files not in project
1261 // if(IsPathInsideOf(absolutePath, ide.workspace.projectDir))
1262 // MakePathRelative(absolutePath, ide.workspace.projectDir, relativePath);
1263 bool result = false;
1265 result = prj.GetRelativePath(absolutePath, relativePath);
1267 result = ide.projectView.project.GetRelativePath(absolutePath, relativePath);
1268 //if(ide.projectView.project.GetRelativePath(absolutePath, relativePath));
1272 char title[MAX_LOCATION];
1273 char directory[MAX_LOCATION];
1274 StripLastDirectory(absolutePath, directory);
1275 snprintf(title, sizeof(title), $"Provide source files location directory for %s", absolutePath);
1276 title[sizeof(title)-1] = 0;
1279 String srcDir = null;
1280 for(dir : ide.workspace.sourceDirs)
1282 if(IsPathInsideOf(absolutePath, dir))
1284 MakePathRelative(absoluteFilePath, dir, relativePath);
1292 if(SourceDirDialog(title, directory, null, sourceDir))
1294 if(IsPathInsideOf(absolutePath, sourceDir))
1296 AddSourceDir(sourceDir);
1297 MakePathRelative(absoluteFilePath, sourceDir, relativePath);
1300 else if(MessageBox { type = yesNo, master = ide,
1301 contents = $"You must provide a valid source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1302 text = $"Invalid Source Directory" }.Modal() == no)
1305 else if(MessageBox { type = yesNo, master = ide,
1306 contents = $"You must provide a source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1307 text = $"No Source Directory Provided" }.Modal() == no)
1311 ide.workspace.bpCount++;
1312 bp = { line = lineNumber, type = user, enabled = true, level = -1 };
1313 ide.workspace.breakpoints.Add(bp);
1314 bp.absoluteFilePath = CopyString(absolutePath);
1315 bp.relativeFilePath = CopyString(relativePath);
1316 ide.breakpointsView.AddBreakpoint(bp);
1321 DebuggerState oldState = state;
1326 GdbDebugBreak(true);
1331 sentBreakInsert = true;
1332 GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, bp.line);
1335 bp.inserted = (bp.bp && bp.bp.number != 0);
1336 ValidateBreakpoint(bp);
1340 if(oldState == running)
1341 GdbExecContinue(false);
1344 ide.workspace.Save();
1347 void UpdateRemovedBreakpoint(Breakpoint bp)
1349 if(targeted && bp.inserted)
1351 DebuggerState oldState = state;
1356 GdbDebugBreak(true);
1360 GdbCommand(false, "-break-delete %d", bp.bp.number);
1363 if(oldState == running)
1364 GdbExecContinue(false);
1370 void ParseFrame(Frame frame, char * string)
1373 Array<char *> frameTokens { minAllocSize = 50 };
1374 Array<char *> argsTokens { minAllocSize = 50 };
1375 Array<char *> argumentTokens { minAllocSize = 50 };
1376 DebugListItem item { };
1379 TokenizeList(string, ',', frameTokens);
1380 for(i = 0; i < frameTokens.count; i++)
1382 if(TokenizeListItem(frameTokens[i], item))
1384 StripQuotes(item.value, item.value);
1385 if(!strcmp(item.name, "level"))
1386 frame.level = atoi(item.value);
1387 else if(!strcmp(item.name, "addr"))
1388 frame.addr = CopyString(item.value);
1389 else if(!strcmp(item.name, "func"))
1390 frame.func = CopyString(item.value);
1391 else if(!strcmp(item.name, "args"))
1393 if(!strcmp(item.value, "[]"))
1394 frame.argsCount = 0;
1397 item.value = StripBrackets(item.value);
1398 TokenizeList(item.value, ',', argsTokens);
1399 for(j = 0; j < argsTokens.count; j++)
1401 argsTokens[j] = StripCurlies(argsTokens[j]);
1402 TokenizeList(argsTokens[j], ',', argumentTokens);
1403 for(k = 0; k < argumentTokens.count; k++)
1406 frame.args.Add(arg);
1407 if(TokenizeListItem(argumentTokens[k], item))
1409 if(!strcmp(item.name, "name"))
1411 StripQuotes(item.value, item.value);
1412 arg.name = CopyString(item.value);
1414 else if(!strcmp(item.name, "value"))
1416 StripQuotes(item.value, item.value);
1417 arg.value = CopyString(item.value);
1420 DebuggerProtocolUnknown("Unknown frame args item name", item.name);
1423 DebuggerProtocolUnknown("Bad frame args item", "");
1425 argumentTokens.RemoveAll();
1427 frame.argsCount = argsTokens.count;
1428 argsTokens.RemoveAll();
1431 else if(!strcmp(item.name, "from"))
1432 frame.from = item.value;
1433 else if(!strcmp(item.name, "file"))
1435 frame.file = item.value;
1436 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1438 else if(!strcmp(item.name, "line"))
1439 frame.line = atoi(item.value);
1440 else if(!strcmp(item.name, "fullname"))
1442 // GDB 6.3 on OS X is giving "fullname" and "dir", all in absolute, but file name only in 'file'
1443 String path = ide.workspace.GetPathWorkspaceRelativeOrAbsolute(item.value);
1444 if(strcmp(frame.file, path))
1447 delete frame.absoluteFile;
1448 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
1453 DebuggerProtocolUnknown("Unknown frame member name", item.name);
1456 DebuggerProtocolUnknown("Bad frame", "");
1461 delete argumentTokens;
1465 void ShowDebuggerViews()
1467 ide.outputView.Show();
1468 ide.outputView.SelectTab(debug);
1469 ide.threadsView.Show();
1470 ide.callStackView.Show();
1471 ide.watchesView.Show();
1475 void HideDebuggerViews()
1477 ide.RepositionWindows(true);
1480 void ::GdbCommand(bool focus, char * format, ...)
1484 // TODO: Improve this limit
1485 static char string[MAX_F_STRING*4];
1487 va_start(args, format);
1488 vsnprintf(string, sizeof(string), format, args);
1489 string[sizeof(string)-1] = 0;
1493 ide.debugger.serialSemaphore.TryWait();
1496 #ifdef GDB_DEBUG_CONSOLE
1497 Log(string); Log("\n");
1499 #ifdef GDB_DEBUG_OUTPUT
1500 ide.outputView.gdbBox.Logf("cmd: %s\n", string);
1502 #ifdef GDB_DEBUG_GUI
1504 ide.gdbDialog.AddCommand(string);
1506 strcat(string,"\n");
1507 gdbHandle.Puts(string);
1510 Process_ShowWindows(targetProcessId);
1513 ide.debugger.serialSemaphore.Wait();
1518 bool ValidateBreakpoint(Breakpoint bp)
1520 if(modules && bp.bp)
1522 if(bp.bp.line != bp.line)
1527 ide.outputView.debugBox.Logf("WOULD HAVE -- Invalid breakpoint disabled: %s:%d\n", bp.relativeFilePath, bp.line);
1531 //GdbCommand(false, "-break-delete %d", bp.bp.number);
1532 //bp.inserted = false;
1534 //bp.enabled = false;
1539 ide.outputView.debugBox.Logf("Debugger Error: ValidateBreakpoint error\n");
1540 bp.line = bp.bp.line;
1547 static void GdbInsertInternalBreakpoints()
1551 //if(!breakpointsInserted)
1553 DirExpression objDir = ide.project.GetObjDir(currentCompiler, prjConfig, bitDepth);
1558 if(bp.type == internalMain)
1560 sentBreakInsert = true;
1561 GdbCommand(false, "-break-insert main");
1564 bp.inserted = (bp.bp && bp.bp.number != 0);
1566 #if defined(__WIN32__)
1567 else if(bp.type == internalWinMain)
1569 sentBreakInsert = true;
1570 GdbCommand(false, "-break-insert WinMain");
1573 bp.inserted = (bp.bp && bp.bp.number != 0);
1576 else if(bp.type == internalModulesLoaded)
1578 char path[MAX_LOCATION];
1579 char name[MAX_LOCATION];
1580 char fixedModuleName[MAX_FILENAME];
1583 bool moduleLoadBlock = false;
1585 ReplaceSpaces(fixedModuleName, ide.project.moduleName);
1586 snprintf(name, sizeof(name),"%s.main.ec", fixedModuleName);
1587 name[sizeof(name)-1] = 0;
1588 strcpy(path, ide.workspace.projectDir);
1589 PathCatSlash(path, objDir.dir);
1590 PathCatSlash(path, name);
1591 f = FileOpen(path, read);
1594 for(lineNumber = 1; !f.Eof(); lineNumber++)
1596 if(f.GetLine(line, sizeof(line) - 1))
1598 bool moduleLoadLine;
1599 TrimLSpaces(line, line);
1600 moduleLoadLine = !strncmp(line, "eModule_Load", strlen("eModule_Load"));
1601 if(!moduleLoadBlock && moduleLoadLine)
1602 moduleLoadBlock = true;
1603 else if(moduleLoadBlock && !moduleLoadLine && strlen(line) > 0)
1609 char relative[MAX_LOCATION];
1610 bp.absoluteFilePath = CopyString(path);
1611 MakePathRelative(path, ide.workspace.projectDir, relative);
1612 delete bp.relativeFilePath;
1613 bp.relativeFilePath = CopyString(relative);
1614 bp.line = lineNumber;
1615 sentBreakInsert = true;
1616 GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, lineNumber);
1619 bp.inserted = (bp.bp && bp.bp.number != 0);
1624 else if(bp.type == internalModuleLoad && modules)
1626 Project ecerePrj = null;
1627 for(p : ide.workspace.projects)
1629 if(!strcmp(p.topNode.name, "ecere.epj"))
1637 ProjectNode node = ecerePrj.topNode.Find("instance.c", false);
1640 char path[MAX_LOCATION];
1641 char relative[MAX_LOCATION];
1642 node.GetFullFilePath(path);
1643 bp.absoluteFilePath = CopyString(path);
1644 MakePathRelative(path, ecerePrj.topNode.path, relative);
1645 delete bp.relativeFilePath;
1646 bp.relativeFilePath = CopyString(relative);
1647 sentBreakInsert = true;
1648 GdbCommand(false, "-break-insert %s:InternalModuleLoadBreakpoint", bp.relativeFilePath);
1651 bp.inserted = (bp.bp && bp.bp.number != 0);
1662 void GdbBreakpointsInsert()
1666 //if(!breakpointsInserted)
1668 //if(!ignoreBreakpoints)
1669 //breakpointsInserted = true;
1670 for(bp : ide.workspace.breakpoints)
1672 if(!bp.inserted && bp.type == user)
1674 if(!ignoreBreakpoints && bp.enabled)
1676 sentBreakInsert = true;
1677 breakpointError = false;
1678 GdbCommand(false, "-break-insert %s:%d", bp.relativeFilePath, bp.line);
1679 // Improve, GdbCommand should return a success value?
1682 char fileName[MAX_FILENAME];
1683 breakpointError = false;
1684 GetLastDirectory(bp.relativeFilePath, fileName);
1685 sentBreakInsert = true;
1686 GdbCommand(false, "-break-insert %s:%d", fileName, bp.line);
1690 bp.inserted = (bp.bp && bp.bp.number != 0);
1693 ValidateBreakpoint(bp);
1699 printf("problem\n");
1701 bp.bp = GdbDataBreakpoint { };
1705 if(bpRunToCursor && !bpRunToCursor.inserted)
1707 sentBreakInsert = true;
1708 GdbCommand(false, "-break-insert %s:%d", bpRunToCursor.relativeFilePath, bpRunToCursor.line);
1709 bpRunToCursor.bp = bpItem;
1711 bpRunToCursor.inserted = (bpRunToCursor.bp && bpRunToCursor.bp.number != 0);
1712 ValidateBreakpoint(bpRunToCursor);
1718 void GdbBreakpointsDelete(bool deleteRunToCursor)
1720 //breakpointsInserted = false;
1726 GdbCommand(false, "-break-delete %d", bp.bp.number);
1727 bp.inserted = false;
1729 //check here (reply form -break-delete, returns bpitem?)
1732 for(bp : ide.workspace.breakpoints)
1735 GdbCommand(false, "-break-delete %d", bp.bp.number);
1736 bp.inserted = false;
1738 //check here (reply form -break-delete, returns bpitem?)
1741 if(deleteRunToCursor && bpRunToCursor)
1743 GdbCommand(false, "-break-delete %d", bpRunToCursor.bp.number);
1744 bpRunToCursor.inserted = false;
1745 bpRunToCursor.bp = bpItem;
1746 //check here (reply form -break-delete, returns bpitem?)
1755 stackFrames.Free(Frame::Free);
1756 GdbCommand(false, "-stack-info-depth");
1758 GdbCommand(false, "-stack-info-depth 192");
1759 if(frameCount && frameCount <= 192)
1760 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 191));
1763 GdbCommand(false, "-stack-list-frames 0 %d", Min(frameCount-1, 95));
1764 GdbCommand(false, "-stack-list-frames %d %d", Max(frameCount - 96, 96), frameCount - 1);
1766 GdbCommand(false, "");
1773 char escaped[MAX_LOCATION];
1774 strescpy(escaped, targetFile);
1775 GdbCommand(false, "file \"%s\"", escaped); //GDB/MI Missing Implementation -symbol-file, -target-attach
1782 const char *vgdbCommand = "/usr/bin/vgdb"; // TODO: vgdb command config option
1783 //GdbCommand(false, "-target-select remote | %s --pid=%d", "vgdb", targetProcessId);
1784 printf("target remote | %s --pid=%d\n", vgdbCommand, targetProcessId);
1785 GdbCommand(false, "target remote | %s --pid=%d", vgdbCommand, targetProcessId); // TODO: vgdb command config option
1788 for(prj : ide.workspace.projects)
1790 if(prj == ide.workspace.projects.firstIterator.data)
1792 GdbCommand(false, "-environment-directory \"%s\"", prj.topNode.path);
1794 for(dir : ide.workspace.sourceDirs)
1796 GdbCommand(false, "-environment-directory \"%s\"", dir);
1799 GdbInsertInternalBreakpoints();
1805 void GdbTargetRelease()
1809 GdbBreakpointsDelete(true);
1810 GdbCommand(false, "file"); //GDB/MI Missing Implementation -target-detach
1816 void GdbDebugBreak(bool internal)
1821 breakType = DebuggerAction::internal;
1823 if(ide) ide.Update(null);
1825 if(Process_Break(targetProcessId)) //GdbCommand(false, "-exec-interrupt");
1826 serialSemaphore.Wait();
1829 ChangeState(loaded);
1830 targetProcessId = 0;
1835 ide.outputView.debugBox.Logf("Debugger Error: GdbDebugBreak with not target id should never happen\n");
1842 ShowDebuggerViews();
1844 GdbCommand(true, "-exec-continue");
1846 GdbCommand(true, "-exec-run");
1849 void GdbExecContinue(bool focus)
1852 GdbCommand(focus, "-exec-continue");
1858 GdbCommand(true, "-exec-next");
1864 GdbCommand(true, "-exec-step");
1867 void GdbExecFinish()
1870 GdbCommand(true, "-exec-finish");
1873 void GdbExecCommon()
1875 ClearBreakDisplay();
1876 GdbInsertInternalBreakpoints();
1877 GdbBreakpointsInsert();
1880 #ifdef GDB_DEBUG_GUI
1881 void SendGDBCommand(char * command)
1883 DebuggerState oldState = state;
1888 GdbDebugBreak(true);
1891 GdbCommand(false, command);
1894 if(oldState == running)
1895 GdbExecContinue(false);
1899 void ClearBreakDisplay()
1902 activeFrameLevel = -1;
1913 stackFrames.Free(Frame::Free);
1914 WatchesCodeEditorLinkRelease();
1915 ide.callStackView.Clear();
1916 ide.threadsView.Clear();
1923 GdbCommand(false, "-interpreter-exec console \"kill\""); // should use -exec-abort -- GDB/MI implementation incomplete
1927 bool GdbInit(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
1930 char oldDirectory[MAX_LOCATION];
1931 char tempPath[MAX_LOCATION];
1932 char command[MAX_F_STRING*4];
1933 bool vgFullLeakCheck = ide.workspace.vgFullLeakCheck;
1934 Project project = ide.project;
1935 DirExpression targetDirExp = project.GetTargetDir(compiler, config, bitDepth);
1936 PathBackup pathBackup { };
1938 if(currentCompiler != compiler)
1940 delete currentCompiler;
1941 currentCompiler = compiler;
1942 incref currentCompiler;
1945 this.bitDepth = bitDepth;
1946 usingValgrind = useValgrind;
1948 ChangeState(loaded);
1950 sentBreakInsert = false;
1951 breakpointError = false;
1955 //breakpointsInserted = false;
1957 ide.outputView.ShowClearSelectTab(debug);
1958 ide.outputView.debugBox.Logf($"Starting debug mode\n");
1960 #ifdef GDB_DEBUG_CONSOLE
1961 Log("Starting GDB"); Log("\n");
1963 #ifdef GDB_DEBUG_OUTPUT
1964 ide.outputView.gdbBox.Logf("run: Starting GDB\n");
1967 strcpy(tempPath, ide.workspace.projectDir);
1968 PathCatSlash(tempPath, targetDirExp.dir);
1970 targetDir = CopyString(tempPath);
1971 project.CatTargetFileName(tempPath, compiler, config);
1973 targetFile = CopyString(tempPath);
1975 GetWorkingDir(oldDirectory, MAX_LOCATION);
1976 if(ide.workspace.debugDir && ide.workspace.debugDir[0])
1978 char temp[MAX_LOCATION];
1979 strcpy(temp, ide.workspace.projectDir);
1980 PathCatSlash(temp, ide.workspace.debugDir);
1981 ChangeWorkingDir(temp);
1984 ChangeWorkingDir(ide.workspace.projectDir);
1986 ide.SetPath(true, compiler, config, bitDepth);
1988 // TODO: This pollutes the environment, but at least it works
1989 // It shouldn't really affect the IDE as the PATH gets restored and other variables set for testing will unlikely cause problems
1990 // What is the proper solution for this? DualPipeOpenEnv?
1991 // gdb set environment commands don't seem to take effect
1992 for(e : ide.workspace.environmentVars)
1994 SetEnvironment(e.name, e.string);
1999 char * clArgs = ide.workspace.commandLineArgs;
2000 const char *valgrindCommand = "valgrind"; // TODO: valgrind command config option //TODO: valgrind options
2001 vgLogFile = CreateTemporaryFile(vgLogPath, "ecereidevglog");
2005 vgLogThread.Create();
2009 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't open temporary log file for Valgrind output\n");
2012 if(result && !CheckCommandAvailable(valgrindCommand))
2014 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for Valgrind is not available.\n", valgrindCommand);
2019 sprintf(command, "%s --vgdb=yes --vgdb-error=0 --log-file=%s%s %s%s%s",
2020 valgrindCommand, vgLogPath, vgFullLeakCheck ? " --leak-check=full" : "", targetFile, clArgs ? " " : "", clArgs ? clArgs : "");
2021 vgTargetHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
2024 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start Valgrind\n");
2030 incref vgTargetHandle;
2031 vgTargetThread.Create();
2033 targetProcessId = vgTargetHandle.GetProcessID();
2034 waitingForPID = false;
2035 if(!targetProcessId)
2037 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get Valgrind process ID\n");
2044 serialSemaphore.Wait();
2052 (compiler.targetPlatform == win32 && bitDepth == 64) ? "x86_64-w64-mingw32-gdb" :
2053 (compiler.targetPlatform == win32 && bitDepth == 32) ? "i686-w64-mingw32-gdb" :
2055 if(!CheckCommandAvailable(command))
2057 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for GDB is not available.\n", command);
2062 strcat(command, " -n -silent --interpreter=mi2"); //-async //\"%s\"
2064 gdbHandle = DualPipeOpen(PipeOpenMode { output = 1, error = 2, input = 1 }, command);
2067 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start GDB\n");
2077 gdbProcessId = gdbHandle.GetProcessID();
2080 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get GDB process ID\n");
2087 serialSemaphore.Wait();
2092 //ChangeState(terminated);
2098 #if defined(__unix__)
2100 CreateTemporaryDir(progFifoDir, "ecereide");
2101 strcpy(progFifoPath, progFifoDir);
2102 PathCat(progFifoPath, "ideprogfifo");
2103 if(!mkfifo(progFifoPath, 0600))
2105 //fileCreated = true;
2110 ide.outputView.debugBox.Logf(createFIFOMsg, progFifoPath);
2117 progThread.terminate = false;
2118 progThread.Create();
2122 #if defined(__WIN32__)
2123 GdbCommand(false, "-gdb-set new-console on");
2126 GdbCommand(false, "-gdb-set verbose off");
2127 //GdbCommand(false, "-gdb-set exec-done-display on");
2128 GdbCommand(false, "-gdb-set step-mode off");
2129 GdbCommand(false, "-gdb-set unwindonsignal on");
2130 //GdbCommand(false, "-gdb-set shell on");
2131 GdbCommand(false, "set print elements 992");
2132 GdbCommand(false, "-gdb-set backtrace limit 100000");
2134 #if defined(__unix__)
2136 GdbCommand(false, "-inferior-tty-set %s", progFifoPath);
2140 GdbCommand(false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
2142 for(e : ide.workspace.environmentVars)
2144 GdbCommand(false, "set environment %s=%s", e.name, e.string);
2149 ChangeWorkingDir(oldDirectory);
2155 delete targetDirExp;
2161 if(gdbHandle && gdbProcessId)
2163 GdbCommand(false, "-gdb-exit");
2178 ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
2182 for(bp : ide.workspace.breakpoints)
2183 bp.inserted = false;
2185 bp.inserted = false;
2187 bpRunToCursor.inserted = false;
2189 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2190 ClearBreakDisplay();
2193 #if defined(__unix__)
2194 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
2196 progThread.terminate = true;
2199 fifoFile.CloseInput();
2205 DeleteFile(progFifoPath);
2206 progFifoPath[0] = '\0';
2212 void WatchesCodeEditorLinkInit()
2215 char tempPath[MAX_LOCATION];
2216 char path[MAX_LOCATION];
2218 //void MakeFilePathProjectRelative(char * path, char * relativePath)
2219 if(!ide.projectView.project.GetRelativePath(activeFrame.file, tempPath))
2220 strcpy(tempPath, activeFrame.file);
2222 strcpy(path, ide.workspace.projectDir);
2223 PathCat(path, tempPath);
2224 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2227 for(srcDir : ide.workspace.sourceDirs)
2229 strcpy(path, srcDir);
2230 PathCat(path, tempPath);
2231 codeEditor = (CodeEditor)ide.OpenFile(path, Normal, false, null, no, normal, false);
2232 if(codeEditor) break;
2237 if(activeFrame && !activeFrame.absoluteFile && activeFrame.file)
2238 activeFrame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(activeFrame.file);
2239 if(!activeFrame || !activeFrame.absoluteFile)
2242 codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, normal, false, null, no, normal, false);
2245 codeEditor.inUseDebug = true;
2248 //watchesInit = true;
2251 void WatchesCodeEditorLinkRelease()
2257 codeEditor.inUseDebug = false;
2258 if(!codeEditor.visible)
2259 codeEditor.Destroy(0);
2265 bool ResolveWatch(Watch wh)
2267 bool result = false;
2280 char watchmsg[MAX_F_STRING];
2281 if(state == stopped && !codeEditor)
2282 wh.value = CopyString($"No source file found for selected frame");
2283 //if(codeEditor && state == stopped || state != stopped)
2286 Module backupPrivateModule;
2287 Context backupContext;
2288 Class backupThisClass;
2292 backupPrivateModule = GetPrivateModule();
2293 backupContext = GetCurrentContext();
2294 backupThisClass = GetThisClass();
2297 SetPrivateModule(codeEditor.privateModule);
2298 SetCurrentContext(codeEditor.globalContext);
2299 SetTopContext(codeEditor.globalContext);
2300 SetGlobalContext(codeEditor.globalContext);
2301 SetGlobalData(&codeEditor.globalData);
2304 exp = ParseExpressionString(wh.expression);
2306 if(exp && !parseError)
2308 char expString[4096];
2310 PrintExpression(exp, expString);
2312 if(GetPrivateModule())
2315 DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2316 ProcessExpressionType(exp);
2318 wh.type = exp.expType;
2321 DebugComputeExpression(exp);
2322 if(ExpressionIsError(exp))
2324 GDBFallBack(exp, expString);
2327 /*if(exp.hasAddress)
2329 char temp[MAX_F_STRING];
2330 sprintf(temp, "0x%x", exp.address);
2331 wh.address = CopyString(temp);
2332 // wh.address = CopyStringf("0x%x", exp.address);
2337 Type dataType = exp.expType;
2340 char temp[MAX_F_STRING];
2341 switch(dataType.kind)
2344 sprintf(temp, "%i", exp.val.c);
2347 sprintf(temp, "%i", exp.val.s);
2352 sprintf(temp, "%i", exp.val.i);
2355 sprintf(temp, "%i", exp.val.i64);
2358 sprintf(temp, "%i", exp.val.p);
2363 long v = (long)exp.val.f;
2364 sprintf(temp, "%i", v);
2369 long v = (long)exp.val.d;
2370 sprintf(temp, "%i", v);
2375 wh.intVal = CopyString(temp);
2376 switch(dataType.kind)
2379 sprintf(temp, "0x%x", exp.val.c);
2382 sprintf(temp, "0x%x", exp.val.s);
2386 sprintf(temp, "0x%x", exp.val.i);
2389 sprintf(temp, "0x%x", exp.val.i64);
2392 sprintf(temp, "0x%x", exp.val.i64);
2395 sprintf(temp, "0x%x", exp.val.p);
2400 long v = (long)exp.val.f;
2401 sprintf(temp, "0x%x", v);
2406 long v = (long)exp.val.d;
2407 sprintf(temp, "0x%x", v);
2412 wh.hexVal = CopyString(temp);
2413 switch(dataType.kind)
2416 sprintf(temp, "0o%o", exp.val.c);
2419 sprintf(temp, "0o%o", exp.val.s);
2423 sprintf(temp, "0o%o", exp.val.i);
2426 sprintf(temp, "0o%o", exp.val.i64);
2429 sprintf(temp, "0o%o", exp.val.i64);
2432 sprintf(temp, "0o%o", exp.val.p);
2437 long v = (long)exp.val.f;
2438 sprintf(temp, "0o%o", v);
2443 long v = (long)exp.val.d;
2444 sprintf(temp, "0o%o", v);
2449 wh.octVal = CopyString(temp);
2452 // WHATS THIS HERE ?
2453 if(exp.type == constantExp && exp.constant)
2454 wh.constant = CopyString(exp.constant);
2460 case symbolErrorExp:
2461 snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2463 case structMemberSymbolErrorExp:
2464 // todo get info as in next case (ExpClassMemberSymbolError)
2465 snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2467 case classMemberSymbolErrorExp:
2470 Expression memberExp = exp.member.exp;
2471 Identifier memberID = exp.member.member;
2472 Type type = memberExp.expType;
2475 _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2478 char string[256] = "";
2480 PrintTypeNoConst(type, string, false, true);
2481 classSym = FindClass(string);
2482 _class = classSym ? classSym.registered : null;
2485 snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2487 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2490 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2493 case memoryErrorExp:
2494 // Need to ensure when set to memoryErrorExp, constant is set
2495 snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2497 case dereferenceErrorExp:
2498 snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2500 case unknownErrorExp:
2501 snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2503 case noDebuggerErrorExp:
2504 snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2506 case debugStateErrorExp:
2507 snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2510 snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2514 // Temporary Code for displaying Strings
2515 if((exp.expType && ((exp.expType.kind == pointerType ||
2516 exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) ||
2517 (wh.type && wh.type.kind == classType && wh.type._class &&
2518 wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2519 !strcmp(wh.type._class.registered.name, "String")))
2522 if(exp.expType.kind != arrayType || exp.hasAddress)
2528 //char temp[MAX_F_STRING * 32];
2530 ExpressionType evalError = dummyExp;
2531 /*if(exp.expType.kind == arrayType)
2532 sprintf(temp, "(char*)0x%x", exp.address);
2534 sprintf(temp, "(char*)%s", exp.constant);*/
2536 //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2537 // address = strtoul(exp.constant, null, 0);
2538 address = _strtoui64(exp.constant, null, 0);
2539 //printf("%x\n", address);
2540 // snprintf(value, sizeof(value), "0x%08x ", address);
2542 if(address > 0xFFFFFFFFLL)
2543 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2545 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2546 value[sizeof(value)-1] = 0;
2549 strcat(value, $"Null string");
2553 len = strlen(value);
2555 while(!string && size > 2)
2557 string = GdbReadMemory(address, size);
2560 if(string && string[0])
2563 if(UTF8Validate(string))
2568 for(c = 0; (ch = string[c]) && c<4096; c++)
2571 value[len++] = '\0';
2576 ISO8859_1toUTF8(string, value + len, 4096 - len - 30);
2577 strcat(value, ") (ISO8859-1)");
2584 strcat(value, $"Empty string");
2588 strcat(value, $"Couldn't read memory");
2590 wh.value = CopyString(value);
2593 else if(wh.type && wh.type.kind == classType && wh.type._class &&
2594 wh.type._class.registered && wh.type._class.registered.type == enumClass)
2596 uint64 value = strtoul(exp.constant, null, 0);
2597 Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
2598 EnumClassData enumeration = (EnumClassData)enumClass.data;
2600 for(item = enumeration.values.first; item; item = item.next)
2601 if((int)item.data == value)
2604 wh.value = CopyString(item.name);
2606 wh.value = CopyString($"Invalid Enum Value");
2607 result = (bool)atoi(exp.constant);
2609 else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class &&
2610 wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
2617 if(exp.constant[0] == '\'')
2619 if((int)((byte *)exp.constant)[1] > 127)
2622 value = UTF8GetChar(exp.constant + 1, &nb);
2623 if(nb < 2) value = exp.constant[1];
2624 signedValue = value;
2628 signedValue = exp.constant[1];
2630 // Precomp Syntax error with boot strap here:
2631 byte b = (byte)(char)signedValue;
2632 value = (unichar) b;
2638 if(wh.type.kind == charType && wh.type.isSigned)
2640 signedValue = (int)(char)strtol(exp.constant, null, 0);
2642 // Precomp Syntax error with boot strap here:
2643 byte b = (byte)(char)signedValue;
2644 value = (unichar) b;
2649 value = (uint)strtoul(exp.constant, null, 0);
2650 signedValue = (int)value;
2654 UTF32toUTF8Len(&value, 1, charString, 5);
2656 snprintf(string, sizeof(string), "\'\\0' (0)");
2657 else if(value == '\t')
2658 snprintf(string, sizeof(string), "\'\\t' (%d)", value);
2659 else if(value == '\n')
2660 snprintf(string, sizeof(string), "\'\\n' (%d)", value);
2661 else if(value == '\r')
2662 snprintf(string, sizeof(string), "\'\\r' (%d)", value);
2663 else if(wh.type.kind == charType && wh.type.isSigned)
2664 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
2665 else if(value > 256 || wh.type.kind != charType)
2667 if(value > 0x10FFFF || !GetCharCategory(value))
2668 snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
2670 snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
2673 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
2674 string[sizeof(string)-1] = 0;
2676 wh.value = CopyString(string);
2681 wh.value = CopyString(exp.constant);
2682 result = (bool)atoi(exp.constant);
2688 wh.value = PrintHexUInt64(exp.address);
2689 result = (bool)exp.address;
2693 char tempString[256];
2694 if(exp.member.memberType == propertyMember)
2695 snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
2697 snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression,
2698 exp.type.OnGetString(tempString, null, null));
2704 snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
2705 if(exp) FreeExpression(exp);
2708 SetPrivateModule(backupPrivateModule);
2709 SetCurrentContext(backupContext);
2710 SetTopContext(backupContext);
2711 SetGlobalContext(backupContext);
2712 SetThisClass(backupThisClass);
2715 // wh.value = CopyString("No source file found for selected frame");
2717 watchmsg[sizeof(watchmsg)-1] = 0;
2719 wh.value = CopyString(watchmsg);
2721 ide.watchesView.UpdateWatch(wh);
2725 void EvaluateWatches()
2727 for(wh : ide.workspace.watches)
2731 char * ::GdbEvaluateExpression(char * expression)
2735 GdbCommand(false, "-data-evaluate-expression \"%s\"", expression);
2737 ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
2741 // to be removed... use GdbReadMemory that returns a byte array instead
2742 char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
2748 printf("GdbReadMemoryString called with size = 0!\n");
2750 // GdbCommand(false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
2751 if(GetRuntimePlatform() == win32)
2752 GdbCommand(false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
2754 GdbCommand(false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
2756 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
2760 byte * ::GdbReadMemory(uint64 address, int bytes)
2764 //GdbCommand(false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
2765 if(GetRuntimePlatform() == win32)
2766 GdbCommand(false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
2768 GdbCommand(false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
2771 printf("GdbReadMemory called with bytes = 0!\n");
2774 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
2775 else if(eval.result && strcmp(eval.result, "N/A"))
2777 byte * result = new byte[bytes];
2778 byte * string = eval.result;
2782 result[c++] = (byte)strtol(string, &string, 10);
2798 void EventHit(GdbDataStop stopItem)
2800 bool conditionMet = true;
2801 Breakpoint bp = bpHit;
2803 if(!bp && bpRunToCursor)
2808 if(bp.type == user && stopItem.frame.line && bp.line != stopItem.frame.line)
2810 bp.line = stopItem.frame.line;
2811 ide.breakpointsView.UpdateBreakpoint(bp.row);
2812 ide.workspace.Save();
2818 case internalWinMain:
2819 GdbBreakpointsInsert();
2820 if(userBreakOnInternBreak)
2822 userBreakOnInternBreak = false;
2823 // Why was SelectFrame missing here?
2824 SelectFrame(activeFrameLevel);
2825 GoToStackFrameLine(activeFrameLevel, true);
2826 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
2830 GdbExecContinue(false);
2832 case internalModulesLoaded:
2834 GdbInsertInternalBreakpoints();
2835 GdbBreakpointsInsert();
2836 GdbExecContinue(false);
2838 case internalModuleLoad:
2839 GdbBreakpointsInsert();
2840 GdbExecContinue(false);
2845 conditionMet = ResolveWatch(bp.condition);
2847 if((bp.level == -1 || bp.level == frameCount-1) && conditionMet)
2852 ignoreBreakpoints = false;
2853 // Why was SelectFrame missing here?
2854 SelectFrame(activeFrameLevel);
2855 GoToStackFrameLine(activeFrameLevel, true);
2856 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
2862 GdbExecContinue(false);
2866 GdbExecContinue(false);
2867 ide.breakpointsView.UpdateBreakpoint(bp.row);
2872 ide.outputView.debugBox.Logf("Debugger Error: Breakpoint hit could not match breakpoint instance\n");
2876 if(symbols && bpRunToCursor.inserted)
2877 GdbCommand(false, "-break-delete %d", bpRunToCursor.bp.number);
2878 delete bpRunToCursor;
2882 void ValgrindTargetThreadExit()
2884 ide.outputView.debugBox.Logf($"ValgrindTargetThreadExit\n");
2887 vgTargetHandle.Wait();
2888 delete vgTargetHandle;
2890 HandleExit(null, null);
2893 void GdbThreadExit()
2895 if(state != terminated)
2897 ChangeState(terminated);
2898 targetProcessId = 0;
2899 ClearBreakDisplay();
2905 serialSemaphore.Release();
2910 ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
2911 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2913 HideDebuggerViews();
2915 //ChangeState(terminated);
2919 void GdbThreadMain(char * output)
2922 Array<char *> outTokens { minAllocSize = 50 };
2923 Array<char *> subTokens { minAllocSize = 50 };
2924 DebugListItem item { };
2925 DebugListItem item2 { };
2926 bool setWaitingForPID = false;
2928 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
2929 #ifdef GDB_DEBUG_CONSOLE
2930 Log(output); Log("\n");
2932 #ifdef GDB_DEBUG_OUTPUT
2934 int len = strlen(output);
2942 for(c = 0; c < len / 1024; c++)
2944 strncpy(tmp, start, 1024);
2945 ide.outputView.gdbBox.Logf("out: %s\n", tmp);
2948 ide.outputView.gdbBox.Logf("out: %s\n", start);
2952 ide.outputView.gdbBox.Logf("out: %s\n", output);
2956 #ifdef GDB_DEBUG_CONSOLE
2957 strcpy(lastGdbOutput, output);
2959 #ifdef GDB_DEBUG_GUI
2960 if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
2967 if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
2970 ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
2976 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
2978 //if(outTokens.count == 1)
2983 ChangeState(loaded);
2984 targetProcessId = 0;
2985 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
2987 if(!strcmp(item.name, "reason"))
2989 char * reason = item.value;
2990 StripQuotes(reason, reason);
2991 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
2994 if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
2996 StripQuotes(item2.value, item2.value);
2997 if(!strcmp(item2.name, "exit-code"))
2998 exitCode = item2.value;
3004 HandleExit(reason, exitCode);
3008 DebuggerProtocolUnknown("Unknown kill reply", item.name);
3011 HandleExit(null, null);
3014 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3016 if(!strcmp(item.name, "bkpt"))
3018 sentBreakInsert = false;
3021 printf("problem\n");
3023 bpItem = GdbDataBreakpoint { };
3024 item.value = StripCurlies(item.value);
3025 TokenizeList(item.value, ',', subTokens);
3026 for(i = 0; i < subTokens.count; i++)
3028 if(TokenizeListItem(subTokens[i], item))
3030 StripQuotes(item.value, item.value);
3031 if(!strcmp(item.name, "number"))
3032 bpItem.number = atoi(item.value);
3033 else if(!strcmp(item.name, "type"))
3034 bpItem.type = CopyString(item.value);
3035 else if(!strcmp(item.name, "disp"))
3036 bpItem.disp = CopyString(item.value);
3037 else if(!strcmp(item.name, "enabled"))
3038 bpItem.enabled = (!strcmpi(item.value, "y"));
3039 else if(!strcmp(item.name, "addr"))
3040 bpItem.addr = CopyString(item.value);
3041 else if(!strcmp(item.name, "func"))
3042 bpItem.func = CopyString(item.value);
3043 else if(!strcmp(item.name, "file"))
3044 bpItem.file = item.value;
3045 else if(!strcmp(item.name, "line"))
3046 bpItem.line = atoi(item.value);
3047 else if(!strcmp(item.name, "at"))
3048 bpItem.at = CopyString(item.value);
3049 else if(!strcmp(item.name, "times"))
3050 bpItem.times = atoi(item.value);
3053 //breakType = bpValidation;
3054 //app.SignalEvent();
3055 subTokens.RemoveAll();
3057 else if(!strcmp(item.name, "BreakpointTable"))
3058 ide.outputView.debugBox.Logf("Debugger Error: Command reply BreakpointTable not handled\n");
3059 else if(!strcmp(item.name, "depth"))
3061 StripQuotes(item.value, item.value);
3062 frameCount = atoi(item.value);
3064 stackFrames.Free(Frame::Free);
3066 else if(!strcmp(item.name, "stack"))
3069 if(stackFrames.count)
3070 ide.callStackView.Logf("...\n");
3073 item.value = StripBrackets(item.value);
3074 TokenizeList(item.value, ',', subTokens);
3075 for(i = 0; i < subTokens.count; i++)
3077 if(TokenizeListItem(subTokens[i], item))
3079 if(!strcmp(item.name, "frame"))
3082 stackFrames.Add(frame);
3083 item.value = StripCurlies(item.value);
3084 ParseFrame(frame, item.value);
3085 if(frame.file && frame.from)
3086 DebuggerProtocolUnknown("Unexpected frame file and from members present", "");
3090 if(activeFrameLevel == -1)
3092 if(ide.projectView.IsModuleInProject(frame.file));
3094 if(frame.level != 0)
3096 //stopItem.frame = frame;
3097 breakType = selectFrame;
3100 activeFrame = frame;
3101 activeFrameLevel = frame.level;
3104 ide.callStackView.Logf("%3d ", frame.level);
3105 if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
3106 ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
3107 else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
3108 ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
3109 else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
3110 ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
3111 else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
3112 ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
3114 ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
3119 ide.callStackView.Logf("%3d ", frame.level);
3124 ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
3128 ide.callStackView.Logf("%s\n", frame.func);
3130 ide.callStackView.Logf($"unknown source\n");
3134 DebuggerProtocolUnknown("Unknown stack content", item.name);
3137 if(activeFrameLevel == -1)
3139 activeFrameLevel = 0;
3140 activeFrame = stackFrames.first;
3142 ide.callStackView.Home();
3144 subTokens.RemoveAll();
3146 /*else if(!strcmp(item.name, "frame"))
3149 item.value = StripCurlies(item.value);
3150 ParseFrame(&frame, item.value);
3152 else if(!strcmp(item.name, "thread-ids"))
3154 ide.threadsView.Clear();
3155 item.value = StripCurlies(item.value);
3156 TokenizeList(item.value, ',', subTokens);
3157 for(i = subTokens.count - 1; ; i--)
3159 if(TokenizeListItem(subTokens[i], item))
3161 if(!strcmp(item.name, "thread-id"))
3164 StripQuotes(item.value, item.value);
3165 value = atoi(item.value);
3166 ide.threadsView.Logf("%3d \n", value);
3169 DebuggerProtocolUnknown("Unknown threads content", item.name);
3174 ide.threadsView.Home();
3176 subTokens.RemoveAll();
3177 //if(!strcmp(outTokens[2], "number-of-threads"))
3179 else if(!strcmp(item.name, "new-thread-id"))
3181 StripQuotes(item.value, item.value);
3182 activeThread = atoi(item.value);
3184 else if(!strcmp(item.name, "value"))
3186 StripQuotes(item.value, item.value);
3187 eval.result = CopyString(item.value);
3188 eval.active = false;
3190 else if(!strcmp(item.name, "addr"))
3192 for(i = 2; i < outTokens.count; i++)
3194 if(TokenizeListItem(outTokens[i], item))
3196 if(!strcmp(item.name, "total-bytes"))
3198 StripQuotes(item.value, item.value);
3199 eval.bytes = atoi(item.value);
3201 else if(!strcmp(item.name, "next-row"))
3203 StripQuotes(item.value, item.value);
3204 eval.nextBlockAddress = _strtoui64(item.value, null, 0);
3206 else if(!strcmp(item.name, "memory"))
3210 //StripQuotes(item.value, item.value);
3211 item.value = StripBrackets(item.value);
3212 // this should be treated as a list...
3213 item.value = StripCurlies(item.value);
3214 TokenizeList(item.value, ',', subTokens);
3215 for(j = 0; j < subTokens.count; j++)
3217 if(TokenizeListItem(subTokens[j], item))
3219 if(!strcmp(item.name, "data"))
3221 item.value = StripBrackets(item.value);
3222 StripQuotes2(item.value, item.value);
3223 eval.result = CopyString(item.value);
3224 eval.active = false;
3228 subTokens.RemoveAll();
3233 else if(!strcmp(item.name, "source-path"))
3237 DebuggerProtocolUnknown("Unknown command reply", item.name);
3240 else if(!strcmp(outTokens[0], "^running"))
3242 waitingForPID = true;
3243 setWaitingForPID = true;
3245 else if(!strcmp(outTokens[0], "^exit"))
3247 ChangeState(terminated);
3248 // ide.outputView.debugBox.Logf("Exit\n");
3249 // ide.Update(null);
3251 serialSemaphore.Release();
3253 else if(!strcmp(outTokens[0], "^error"))
3257 sentBreakInsert = false;
3258 breakpointError = true;
3261 printf("problem\n");
3263 bpItem = GdbDataBreakpoint { };
3266 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3268 if(!strcmp(item.name, "msg"))
3270 StripQuotes(item.value, item.value);
3273 eval.active = false;
3275 if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3276 eval.error = symbolNotFound;
3277 else if(strstr(item.value, "Cannot access memory at address"))
3278 eval.error = memoryCantBeRead;
3280 eval.error = unknown;
3282 else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3285 else if(!strncmp(item.value, "Cannot access memory at address", 31))
3288 else if(!strcmp(item.value, "Cannot find bounds of current function"))
3290 ChangeState(stopped);
3291 gdbHandle.Printf("-exec-continue\n");
3293 else if(!strcmp(item.value, "ptrace: No such process."))
3295 ChangeState(loaded);
3296 targetProcessId = 0;
3298 else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3301 else if(!strcmp(item.value, "You can't do that without a process to debug."))
3303 ChangeState(loaded);
3304 targetProcessId = 0;
3306 else if(strstr(item.value, "No such file or directory."))
3308 ChangeState(loaded);
3309 targetProcessId = 0;
3311 else if(strstr(item.value, "During startup program exited with code "))
3313 ChangeState(loaded);
3314 targetProcessId = 0;
3319 if(strlen(item.value) < MAX_F_STRING)
3322 ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3326 ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3332 DebuggerProtocolUnknown("Unknown error content", item.name);
3335 DebuggerProtocolUnknown("Unknown result-record", outTokens[0]);
3337 outTokens.RemoveAll();
3340 DebuggerProtocolUnknown("Unknown status-async-output", outTokens[0]);
3343 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "=thread-group-created")) //=thread-group-created,id="7611"
3345 else if(!strcmp(outTokens[0], "=thread-created")) //=thread-created,id="1",group-id="7611"
3347 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"
3348 FGODetectLoadedLibraryForAddedProjectIssues(outTokens);
3350 DebuggerProtocolUnknown("Unknown notify-async-output", outTokens[0]);
3351 outTokens.RemoveAll();
3355 if(TokenizeList(output, ',', outTokens))
3357 if(!strcmp(outTokens[0],"*running"))
3359 waitingForPID = true;
3360 setWaitingForPID = true;
3362 else if(!strcmp(outTokens[0], "*stopped"))
3365 ChangeState(stopped);
3367 for(tk = 1; tk < outTokens.count; tk++)
3369 if(TokenizeListItem(outTokens[tk], item))
3371 if(!strcmp(item.name, "reason"))
3373 char * reason = item.value;
3374 StripQuotes(reason, reason);
3375 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3378 if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3381 StripQuotes(item2.value, item2.value);
3382 if(!strcmp(item2.name, "exit-code"))
3383 exitCode = item2.value;
3389 HandleExit(reason, exitCode);
3391 else if(!strcmp(reason, "breakpoint-hit"))
3395 printf("problem\n");
3397 stopItem = GdbDataStop { };
3399 for(i = tk+1; i < outTokens.count; i++)
3401 TokenizeListItem(outTokens[i], item);
3402 StripQuotes(item.value, item.value);
3403 if(!strcmp(item.name, "bkptno"))
3404 stopItem.bkptno = atoi(item.value);
3405 else if(!strcmp(item.name, "thread-id"))
3406 stopItem.threadid = atoi(item.value);
3407 else if(!strcmp(item.name, "frame"))
3409 item.value = StripCurlies(item.value);
3410 ParseFrame(stopItem.frame, item.value);
3413 DebuggerProtocolUnknown("Unknown breakpoint hit item name", item.name);
3418 else if(!strcmp(reason, "end-stepping-range"))
3422 printf("problem\n");
3424 stopItem = GdbDataStop { };
3426 for(i = tk+1; i < outTokens.count; i++)
3428 TokenizeListItem(outTokens[i], item);
3429 StripQuotes(item.value, item.value);
3430 if(!strcmp(item.name, "thread-id"))
3431 stopItem.threadid = atoi(item.value);
3432 else if(!strcmp(item.name, "frame"))
3434 item.value = StripCurlies(item.value);
3435 ParseFrame(stopItem.frame, item.value);
3437 else if(!strcmp(item.name, "reason"))
3439 else if(!strcmp(item.name, "bkptno"))
3442 DebuggerProtocolUnknown("Unknown end of stepping range item name", item.name);
3448 else if(!strcmp(reason, "function-finished"))
3452 printf("problem\n");
3454 stopItem = GdbDataStop { };
3455 stopItem.reason = CopyString(reason);
3457 for(i = tk+1; i < outTokens.count; i++)
3459 TokenizeListItem(outTokens[i], item);
3460 StripQuotes(item.value, item.value);
3461 if(!strcmp(item.name, "thread-id"))
3462 stopItem.threadid = atoi(item.value);
3463 else if(!strcmp(item.name, "frame"))
3465 item.value = StripCurlies(item.value);
3466 ParseFrame(stopItem.frame, item.value);
3468 else if(!strcmp(item.name, "gdb-result-var"))
3469 stopItem.gdbResultVar = CopyString(item.value);
3470 else if(!strcmp(item.name, "return-value"))
3471 stopItem.returnValue = CopyString(item.value);
3473 DebuggerProtocolUnknown("Unknown function finished item name", item.name);
3476 event = functionEnd;
3479 else if(!strcmp(reason, "signal-received"))
3483 printf("problem\n");
3485 stopItem = GdbDataStop { };
3486 stopItem.reason = CopyString(reason);
3488 for(i = tk+1; i < outTokens.count; i++)
3490 TokenizeListItem(outTokens[i], item);
3491 StripQuotes(item.value, item.value);
3492 if(!strcmp(item.name, "signal-name"))
3493 stopItem.name = CopyString(item.value);
3494 else if(!strcmp(item.name, "signal-meaning"))
3495 stopItem.meaning = CopyString(item.value);
3496 else if(!strcmp(item.name, "thread-id"))
3497 stopItem.threadid = atoi(item.value);
3498 else if(!strcmp(item.name, "frame"))
3500 item.value = StripCurlies(item.value);
3501 ParseFrame(stopItem.frame, item.value);
3504 DebuggerProtocolUnknown("Unknown signal reveived item name", item.name);
3506 if(!strcmp(stopItem.name, "SIGTRAP"))
3525 else if(!strcmp(reason, "watchpoint-trigger"))
3526 DebuggerProtocolUnknown("Reason watchpoint trigger not handled", "");
3527 else if(!strcmp(reason, "read-watchpoint-trigger"))
3528 DebuggerProtocolUnknown("Reason read watchpoint trigger not handled", "");
3529 else if(!strcmp(reason, "access-watchpoint-trigger"))
3530 DebuggerProtocolUnknown("Reason access watchpoint trigger not handled", "");
3531 else if(!strcmp(reason, "watchpoint-scope"))
3532 DebuggerProtocolUnknown("Reason watchpoint scope not handled", "");
3533 else if(!strcmp(reason, "location-reached"))
3534 DebuggerProtocolUnknown("Reason location reached not handled", "");
3536 DebuggerProtocolUnknown("Unknown reason", reason);
3548 DebuggerProtocolUnknown("Unknown exec-async-output", outTokens[0]);
3549 outTokens.RemoveAll();
3552 if(!strcmpi(output, "(gdb) "))
3556 char exeFile[MAX_LOCATION];
3557 int oldProcessID = targetProcessId;
3558 GetLastDirectory(targetFile, exeFile);
3560 while(!targetProcessId/*true*/)
3562 targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3563 if(targetProcessId || gdbHandle.Peek()) break;
3568 ChangeState(running);
3569 else if(!oldProcessID)
3571 ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3572 // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3573 gdbHandle.Printf("-gdb-exit\n");
3575 ChangeState(terminated); //loaded;
3580 for(bp : ide.workspace.breakpoints)
3581 bp.inserted = false;
3584 bp.inserted = false;
3586 bpRunToCursor.inserted = false;
3588 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3589 ClearBreakDisplay();
3591 #if defined(__unix__)
3592 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
3594 progThread.terminate = true;
3597 fifoFile.CloseInput();
3604 DeleteFile(progFifoPath);
3605 progFifoPath[0] = '\0';
3612 serialSemaphore.Release();
3615 DebuggerProtocolUnknown($"Unknown prompt", output);
3619 if(!strncmp(output, "&\"warning:", 10))
3622 content = strstr(output, "\"");
3623 StripQuotes(content, content);
3624 content = strstr(content, ":");
3630 ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
3637 DebuggerProtocolUnknown($"Unknown output", output);
3639 if(!setWaitingForPID)
3640 waitingForPID = false;
3641 setWaitingForPID = false;
3649 // From GDB Output functions
3650 void FGODetectLoadedLibraryForAddedProjectIssues(Array<char *> outTokens)
3652 char path[MAX_LOCATION] = "";
3653 char file[MAX_FILENAME] = "";
3655 DebugListItem item { };
3656 for(token : outTokens)
3658 if(TokenizeListItem(token, item))
3660 if(!strcmp(item.name, "target-name"))
3662 StripQuotes(item.value, path);
3663 MakeSystemPath(path);
3664 GetLastDirectory(path, file);
3666 else if(!strcmp(item.name, "symbols-loaded"))
3668 symbolsLoaded = (atoi(item.value) == 1);
3673 if(path[0] && file[0])
3675 for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
3679 char prjTargetPath[MAX_LOCATION];
3680 char prjTargetFile[MAX_FILENAME];
3681 DirExpression targetDirExp = prj.GetTargetDir(currentCompiler, prj.config, bitDepth);
3682 strcpy(prjTargetPath, prj.topNode.path);
3683 PathCat(prjTargetPath, targetDirExp.dir);
3684 prjTargetFile[0] = '\0';
3685 prj.CatTargetFileName(prjTargetFile, currentCompiler, prj.config);
3686 PathCat(prjTargetPath, prjTargetFile);
3687 MakeSystemPath(prjTargetPath);
3689 match = !fstrcmp(prjTargetFile, file);
3690 if(!match && (dot = strstr(prjTargetFile, ".so.")))
3692 char * dot3 = strstr(dot+4, ".");
3696 match = !fstrcmp(prjTargetFile, file);
3701 match = !fstrcmp(prjTargetFile, file);
3706 // TODO: nice visual feedback to better warn user. use some ide notification system or other means.
3707 /* -- 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)
3709 ide.outputView.debugBox.Logf($"Attention! No symbols for loaded library %s matched to the %s added project.\n", path, prj.topNode.name);
3711 match = !fstrcmp(prjTargetPath, path);
3712 if(!match && (dot = strstr(prjTargetPath, ".so.")))
3714 char * dot3 = strstr(dot+4, ".");
3718 match = !fstrcmp(prjTargetPath, path);
3723 match = !fstrcmp(prjTargetPath, path);
3727 ide.outputView.debugBox.Logf($"Loaded library %s doesn't match the %s target of the %s added project.\n", path, prjTargetPath, prj.topNode.name);
3734 void RunToCursorPrepare(char * absoluteFilePath, char * relativeFilePath, int lineNumber, bool atSameLevel)
3738 //bpRunToCursor.Free();
3739 bpRunToCursor = Breakpoint { };
3742 bpRunToCursor = Breakpoint { };
3744 if(absoluteFilePath)
3745 bpRunToCursor.absoluteFilePath = CopyString(absoluteFilePath);
3746 if(relativeFilePath)
3747 bpRunToCursor.relativeFilePath = CopyString(relativeFilePath);
3748 bpRunToCursor.line = lineNumber;
3749 bpRunToCursor.type = runToCursor;
3750 bpRunToCursor.enabled = true;
3751 bpRunToCursor.condition = null;
3752 bpRunToCursor.ignore = 0;
3753 bpRunToCursor.level = atSameLevel ? frameCount - activeFrameLevel -1 : -1;
3756 ExpressionType ::DebugEvalExpTypeError(char * result)
3762 case symbolNotFound:
3763 return symbolErrorExp;
3764 case memoryCantBeRead:
3765 return memoryErrorExp;
3767 return unknownErrorExp;
3770 char * ::EvaluateExpression(char * expression, ExpressionType * error)
3773 if(ide.projectView && ide.debugger.state == stopped)
3775 result = GdbEvaluateExpression(expression);
3776 *error = DebugEvalExpTypeError(result);
3781 *error = noDebuggerErrorExp;
3786 char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
3789 char * result = GdbReadMemoryString(address, size, format, 1, 1);
3790 if(!result || !strcmp(result, "N/A"))
3791 *error = memoryErrorExp;
3793 *error = DebugEvalExpTypeError(result);
3798 class ValgrindLogThread : Thread
3804 static char output[4096];
3805 Array<char> dynamicBuffer { minAllocSize = 4096 };
3806 File oldValgrindHandle = vgLogFile;
3807 incref oldValgrindHandle;
3810 while(debugger.state != terminated && vgLogFile)
3814 result = vgLogFile.Read(output, 1, sizeof(output));
3816 if(debugger.state == terminated || !vgLogFile/* || vgLogFile.Eof()*/)
3823 for(c = 0; c<result; c++)
3825 if(output[c] == '\n')
3827 int pos = dynamicBuffer.size;
3828 dynamicBuffer.size += c - start;
3829 memcpy(&dynamicBuffer[pos], output + start, c - start);
3830 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3831 // COMMENTED OUT DUE TO ISSUE #135, FIXED
3832 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
3833 dynamicBuffer.size++;
3834 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
3836 // printf("%s\n", dynamicBuffer.array);
3838 if(strstr(&dynamicBuffer[0], "vgdb me"))
3839 debugger.serialSemaphore.Release();
3840 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
3841 dynamicBuffer.size = 0;
3847 int pos = dynamicBuffer.size;
3848 dynamicBuffer.size += c - start;
3849 memcpy(&dynamicBuffer[pos], output + start, c - start);
3852 else if(debugger.state == stopped)
3855 printf("Got end of file from GDB!\n");
3862 delete dynamicBuffer;
3863 ide.outputView.debugBox.Logf($"ValgrindLogThreadExit\n");
3864 //if(oldValgrindHandle == vgLogFile)
3865 debugger.GdbThreadExit/*ValgrindLogThreadExit*/();
3866 delete oldValgrindHandle;
3872 class ValgrindTargetThread : Thread
3878 static char output[4096];
3879 Array<char> dynamicBuffer { minAllocSize = 4096 };
3880 DualPipe oldValgrindHandle = vgTargetHandle;
3881 incref oldValgrindHandle;
3884 while(debugger.state != terminated && vgTargetHandle && !vgTargetHandle.Eof())
3888 result = vgTargetHandle.Read(output, 1, sizeof(output));
3890 if(debugger.state == terminated || !vgTargetHandle || vgTargetHandle.Eof())
3897 for(c = 0; c<result; c++)
3899 if(output[c] == '\n')
3901 int pos = dynamicBuffer.size;
3902 dynamicBuffer.size += c - start;
3903 memcpy(&dynamicBuffer[pos], output + start, c - start);
3904 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3905 // COMMENTED OUT DUE TO ISSUE #135, FIXED
3906 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
3907 dynamicBuffer.size++;
3908 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
3910 // printf("%s\n", dynamicBuffer.array);
3912 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
3914 dynamicBuffer.size = 0;
3920 int pos = dynamicBuffer.size;
3921 dynamicBuffer.size += c - start;
3922 memcpy(&dynamicBuffer[pos], output + start, c - start);
3928 printf("Got end of file from GDB!\n");
3932 delete dynamicBuffer;
3933 //if(oldValgrindHandle == vgTargetHandle)
3934 debugger.ValgrindTargetThreadExit();
3935 delete oldValgrindHandle;
3941 class GdbThread : Thread
3947 static char output[4096];
3948 Array<char> dynamicBuffer { minAllocSize = 4096 };
3949 DualPipe oldGdbHandle = gdbHandle;
3950 incref oldGdbHandle;
3953 while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
3957 result = gdbHandle.Read(output, 1, sizeof(output));
3959 if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
3966 for(c = 0; c<result; c++)
3968 if(output[c] == '\n')
3970 int pos = dynamicBuffer.size;
3971 dynamicBuffer.size += c - start;
3972 memcpy(&dynamicBuffer[pos], output + start, c - start);
3973 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
3974 // COMMENTED OUT DUE TO ISSUE #135, FIXED
3975 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
3976 dynamicBuffer.size++;
3977 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
3979 // printf("%s\n", dynamicBuffer.array);
3981 debugger.GdbThreadMain(&dynamicBuffer[0]);
3982 dynamicBuffer.size = 0;
3988 int pos = dynamicBuffer.size;
3989 dynamicBuffer.size += c - start;
3990 memcpy(&dynamicBuffer[pos], output + start, c - start);
3996 printf("Got end of file from GDB!\n");
4000 delete dynamicBuffer;
4001 //if(oldGdbHandle == gdbHandle)
4002 debugger.GdbThreadExit();
4003 delete oldGdbHandle;
4009 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
4010 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
4012 #if defined(__unix__)
4017 #include <sys/types.h>
4022 class ProgramThread : Thread
4028 bool fileCreated = false;
4030 static char output[1000];
4033 /*if(!mkfifo(progFifoPath, mask))
4040 ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
4044 if(FileExists(progFifoPath)) //fileCreated)
4046 fifoFile = FileOpen(progFifoPath, read);
4050 ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
4055 fd = fileno((FILE *)fifoFile.input);
4056 //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
4060 while(!terminate && fifoFile && !fifoFile.Eof())
4063 struct timeval time;
4071 selectResult = select(fd + 1, &rs, null, null, &time);
4072 if(FD_ISSET(fd, &rs))
4074 int result = (int)read(fd, output, sizeof(output)-1);
4075 if(!result || (result < 0 && errno != EAGAIN))
4079 output[result] = '\0';
4080 if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
4083 ide.outputView.debugBox.Log(output);
4092 //fifoFile.CloseInput();
4095 ide.outputView.debugBox.Log("\n");
4099 if(FileExists(progFifoPath)) //fileCreated)
4101 DeleteFile(progFifoPath);
4102 progFifoPath[0] = '\0';
4110 class Argument : struct
4112 Argument prev, next;
4128 class Frame : struct
4137 property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
4139 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4140 char * absoluteFile;
4149 delete absoluteFile;
4150 args.Free(Argument::Free);
4159 class GdbDataStop : struct
4176 char * gdbResultVar;
4186 if(!strcmp(reason, "signal-received"))
4191 else if(!strcmp(reason, "function-finished"))
4193 delete gdbResultVar;
4198 if(frame) frame.Free();
4207 class GdbDataBreakpoint : struct
4216 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4231 ~GdbDataBreakpoint()
4237 class Breakpoint : struct
4241 char * relativeFilePath;
4242 char * absoluteFilePath;
4251 BreakpointType type;
4254 GdbDataBreakpoint bp;
4256 char * LocationToString()
4258 char location[MAX_LOCATION+20];
4259 snprintf(location, sizeof(location), "%s:%d", relativeFilePath, line);
4260 location[sizeof(location)-1] = 0;
4261 #if defined(__WIN32__)
4262 ChangeCh(location, '/', '\\');
4264 return CopyString(location);
4269 if(relativeFilePath && relativeFilePath[0])
4271 f.Printf(" * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, relativeFilePath);
4273 f.Printf(" ~ %s\n", condition.expression);
4282 delete relativeFilePath;
4283 delete absoluteFilePath;
4293 class Watch : struct
4304 f.Printf(" ~ %s\n", expression);
4328 class DebugListItem : struct
4334 struct DebugEvaluationData
4339 uint64 nextBlockAddress;
4341 DebuggerEvaluationError error;
4344 class CodeLocation : struct
4347 char * absoluteFile;
4350 CodeLocation ::ParseCodeLocation(char * location)
4354 char * colon = null;
4356 char loc[MAX_LOCATION];
4357 strcpy(loc, location);
4358 for(temp = loc; temp = strstr(temp, ":"); temp++)
4366 int line = atoi(colon);
4369 CodeLocation codloc { line = line };
4370 codloc.file = CopyString(loc);
4371 codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
4383 delete absoluteFile;
4393 void GDBFallBack(Expression exp, String expString)
4396 ExpressionType evalError = dummyExp;
4397 result = Debugger::EvaluateExpression(expString, &evalError);
4400 exp.constant = result;
4401 exp.type = constantExp;