2 public import static "ecere"
3 public import static "ec"
15 #define GDB_DEBUG_CONSOLE
19 extern char * strrchr(const char * s, int c);
22 #define strlen _strlen
26 #include <string.h> // For memchr
34 #include <sys/time.h> // Required on Apple...
48 sprintf(s[0], "%04d", now.year);
49 sprintf(s[1], "%02d", now.month+1);
50 sprintf(s[2], "%02d", now.day);
51 sprintf(s[3], "%02d", now.hour);
52 sprintf(s[4], "%02d", now.minute);
53 sprintf(s[5], "%02d", now.second);
54 time = PrintString("*", s[0], s[1], s[2], "-", s[3], s[4], s[5], "*");
60 // use =0 to disable printing of specific channels
62 static enum dplchan { none, gdbProtoIgnored=0/*1*/, gdbProtoUnknown=2, gdbOutput=3/*3*/, gdbCommand=4/*4*/, debuggerCall=0/*5*/, debuggerProblem=6,
63 debuggerUserAction=7,debuggerState=8, debuggerBreakpoints=9, debuggerWatches=0/*10*/, debuggerTemp=0 };
65 static enum dplchan { none, gdbProtoIgnored=0, gdbProtoUnknown=0, gdbOutput=0, gdbCommand=0, debuggerCall=0, debuggerProblem=0,
66 debuggerUserAction=0,debuggerState=0, debuggerBreakpoints=0, debuggerWatches=0, debuggerTemp=0 };
68 static char * _dpct[] = {
70 "GDB Protocol Ignored",
71 "GDB Protocol ***Unknown***",
75 "Debugger ***Problem***",
76 "Debugger::ChangeUserAction",
77 "Debugger::ChangeState",
80 "-----> Temporary Message",
84 // TODO if(strlen(item.value) < MAX_F_STRING)
88 #define _dpl2(...) __dpl2(__FILE__, __LINE__, ##__VA_ARGS__)
92 static void __dpl2(char * file, int line, char ** channels, int channel, int indent, typed_object object, ...)
94 bool chan = channel && channels && channels[channel];
97 char string[MAX_F_STRING];
99 char * time = PrintNow();
101 //ide.outputView.debugBox.Logf();
102 Logf("%s %s:% 5d: %s%s", time, file, line, chan ? channels[channel] : "", chan && channels[channel][0] ? ": " : "");
103 va_start(args, object);
104 len = PrintStdArgsToBuffer(string, sizeof(string), object, args);
112 #define _dpl(...) __dpl(__FILE__, __LINE__, ##__VA_ARGS__)
113 static void __dpl(char * file, int line, int indent, char * format, ...)
116 char string[MAX_F_STRING];
118 char * time = PrintNow();
119 //static File f = null;
120 va_start(args, format);
121 vsnprintf(string, sizeof(string), format, args);
122 string[sizeof(string)-1] = 0;
125 char * time = PrintNow();
127 logName = PrintString(time, ".log");
129 f = FileOpen(logName, write);
132 /*f.Printf("%s %s:% 5d: ", time, file, line);
133 for(c = 0; c<indent; c++)
135 f.Printf("%s\n", string);*/
136 Logf("%s %s:% 5d: ", time, file, line);
137 for(c = 0; c<indent; c++)
139 Logf("%s\n", string);
144 public char * StripQuotes2(char * string, char * output)
148 bool quoted = false, escaped = false;
150 for(c = 0; ch = string[c]; c++)
154 if(escaped || ch != '\"')
157 escaped = !escaped && ch == '\\';
171 // String Escape Copy
172 static void strescpy(char * d, char * s)
180 case '\n': d[k] = '\\'; d[++k] = 'n'; break;
181 case '\t': d[k] = '\\'; d[++k] = 't'; break;
182 case '\a': d[k] = '\\'; d[++k] = 'a'; break;
183 case '\b': d[k] = '\\'; d[++k] = 'b'; break;
184 case '\f': d[k] = '\\'; d[++k] = 'f'; break;
185 case '\r': d[k] = '\\'; d[++k] = 'r'; break;
186 case '\v': d[k] = '\\'; d[++k] = 'v'; break;
187 case '\\': d[k] = '\\'; d[++k] = '\\'; break;
188 case '\"': d[k] = '\\'; d[++k] = '\"'; break;
189 default: d[k] = s[j];
196 static char * CopyUnescapedSystemPath(char * p)
198 char * d = new char[strlen(p) + 1];
200 #if defined(__WIN32__)
201 ChangeCh(d, '/', '\\');
206 static char * CopyUnescapedUnixPath(char * p)
208 char * d = new char[strlen(p) + 1];
210 #if defined(__WIN32__)
211 ChangeCh(d, '\\', '/');
216 static char * CopyUnescapedString(char * s)
218 char * d = new char[strlen(s) + 1];
223 // String Unescape Copy
225 // TOFIX: THIS DOESN'T HANDLE NUMERIC ESCAPE CODES (OCTAL/HEXADECIMAL...)?
226 // Seems very similar to ReadString in pass15.ec (which also misses numeric escape codes :) )
228 static void struscpy(char * d, char * s)
239 case 'n': d[k] = '\n'; break;
240 case 't': d[k] = '\t'; break;
241 case 'a': d[k] = '\a'; break;
242 case 'b': d[k] = '\b'; break;
243 case 'f': d[k] = '\f'; break;
244 case 'r': d[k] = '\r'; break;
245 case 'v': d[k] = '\v'; break;
246 case '\\': d[k] = '\\'; break;
247 case '\"': d[k] = '\"'; break;
248 default: d[k] = '\\'; d[++k] = s[j];
259 static char * StripBrackets(char * string)
261 int length = strlen(string);
262 if(length > 1 && *string == '[' && string[length - 1] == ']')
265 string[length - 1] = '\0';
272 static char * StripCurlies(char * string)
274 int length = strlen(string);
275 if(length > 1 && *string == '{' && string[length - 1] == '}')
278 string[length - 1] = '\0';
285 static int StringGetInt(char * string, int start)
288 int i, len = strlen(string);
290 for(i = start; i < len && i < start + 8; i++)
292 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')
293 strncat(number, &string[i], 1);
300 static int TokenizeList(char * string, const char seperator, Array<char *> tokens)
304 bool quoted = false, escaped = false;
305 char * start = string, ch;
307 for(; (ch = *string); string++)
314 if(escaped || ch != '\"')
315 escaped = !escaped && ch == '\\';
321 else if(ch == '{' || ch == '[' || ch == '(' || ch == '<')
323 else if(ch == '}' || ch == ']' || ch == ')' || ch == '>')
325 else if(ch == seperator && !level)
334 //tokens[count] = start;
335 //tokens[count++] = start;
342 static bool TokenizeListItem(char * string, DebugListItem item)
344 char * equal = strstr(string, "=");
356 static bool CheckCommandAvailable(const char * command)
358 bool available = false;
360 char * name = new char[MAX_FILENAME];
361 char * pathVar = new char[maxPathLen];
363 GetEnvironment("PATH", pathVar, maxPathLen);
364 count = TokenizeWith(pathVar, sizeof(paths) / sizeof(char *), paths, pathListSep, false);
365 strcpy(name, command);
369 const char * extensions[] = { "exe", "com", "bat", null };
370 for(e=0; extensions[e]; e++)
372 ChangeExtension(name, extensions[e], name);
374 for(c=0; c<count; c++)
376 FileListing fl { paths[c] };
379 if(fl.stats.attribs.isFile && !fstrcmp(fl.name, name))
398 // define GdbGetLineSize = 1638400;
399 define GdbGetLineSize = 5638400;
400 #if defined(__unix__)
401 char progFifoPath[MAX_LOCATION];
402 char progFifoDir[MAX_LOCATION];
405 enum DebuggerState { none, prompt, loaded, running, stopped, terminated, error };
408 none, hit, breakEvent, signal, stepEnd, functionEnd, exit, valgrindStartPause, locationReached;
410 property bool canBeMonitored { get { return (this == hit || this == breakEvent || this == signal || this == stepEnd || this == functionEnd || this == locationReached); } };
412 enum DebuggerAction { none, internal, restart, stop, selectFrame, advance }; //, bpValidation
415 unknown, endSteppingRange, functionFinished, signalReceived, breakpointHit, locationReached
416 //watchpointTrigger, readWatchpointTrigger, accessWatchpointTrigger, watchpointScope,
417 //exited, exitedNormally, exitedSignalled;
421 none, internalMain, internalWinMain, internalModulesLoaded, user, runToCursor, internalModuleLoad, internalEntry;
423 property bool isInternal { get { return (this == internalMain || this == internalWinMain || this == internalModulesLoaded || this == internalModuleLoad || this == internalEntry); } };
424 property bool isUser { get { return (this == user || this == runToCursor); } };
426 enum DebuggerEvaluationError { none, symbolNotFound, memoryCantBeRead, unknown };
427 enum DebuggerUserAction
429 none, start, resume, _break, stop, restart, selectThread, selectFrame, stepInto, stepOver, stepUntil, stepOut, runToCursor;
430 property bool breaksOnInternalBreakpoint { get { return (this == stepInto || this == stepOver || this == stepUntil); } };
434 none, run, _continue, next, until, advance, step, finish;
435 property bool suspendInternalBreakpoints { get { return (this == until || this == advance || this == step || this == finish); } };
438 FileDialog debuggerFileDialog { type = selectDir };
440 static DualPipe vgTargetHandle;
441 static File vgLogFile;
442 static char vgLogPath[MAX_LOCATION];
443 static DualPipe gdbHandle;
444 static DebugEvaluationData eval { };
446 static int targetProcessId;
448 static bool gdbReady;
449 static bool breakpointError;
453 Semaphore serialSemaphore { };
459 bool sentBreakInsert;
460 bool ignoreBreakpoints;
468 int activeFrameLevel;
477 GdbExecution gdbExecution;
478 DebuggerUserAction userAction;
481 DebuggerAction breakType;
483 //DebuggerCommand lastCommand; // THE COMPILER COMPILES STUFF THAT DOES NOT EXIST???
485 GdbDataStop stopItem;
486 GdbDataBreakpoint bpItem;
489 List<Breakpoint> sysBPs { };
490 Breakpoint bpRunToCursor;
491 Breakpoint intBpEntry;
492 Breakpoint intBpMain;
493 Breakpoint intBpWinMain;
497 CompilerConfig currentCompiler;
498 ProjectConfig prjConfig;
501 CodeEditor codeEditor;
503 ValgrindLogThread vgLogThread { debugger = this };
504 ValgrindTargetThread vgTargetThread { debugger = this };
505 GdbThread gdbThread { debugger = this };
508 Map<String, bool> projectsLibraryLoaded { };
512 delay = 0.0, userData = this;
516 bool monitor = false;
517 DebuggerEvent curEvent = event;
518 GdbDataStop stopItem = this.stopItem;
519 Breakpoint bpUser = null;
520 Breakpoint bpInternal = null;
528 this.stopItem = null;
532 DynamicString bpReport { };
534 for(bp : sysBPs; bp.inserted)
536 bpReport.concatx(",", bp.type, "(", s=bp.CopyLocationString(false), ")");
539 if(bpRunToCursor && bpRunToCursor.inserted)
541 Breakpoint bp = bpRunToCursor;
542 bpReport.concatx(",", bp.type, "(", s=bp.CopyLocationString(false), ")");
545 for(bp : ide.workspace.breakpoints; bp.inserted)
547 bpReport.concatx(",", bp.type, "(", s=bp.CopyLocationString(false), ")");
551 _dpl2(_dpct, dplchan::debuggerBreakpoints, 0, "gdbTimer::DelayExpired: ", s+1);
556 Breakpoint bp = GetBreakpointById(stopItem.bkptno, &isInternal);
559 _dpl2(_dpct, dplchan::debuggerBreakpoints, 0, "gdb stopped by a breakpoint: ", bp.type, "(", s=bp.CopyLocationString(false), ")");
570 if(curEvent && curEvent != exit)
572 _dpl(0, "No stop item");
580 Restart(currentCompiler, prjConfig, bitDepth, usingValgrind);
589 GdbCommand(0, false, "-stack-select-frame %d", activeFrameLevel);
590 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
591 if(activeFrame.level == activeFrameLevel)
597 // GdbCommand(0, false, "-break-info %s", bpItem.number);
609 Breakpoint bp = stopItem ? GetBreakpointById(stopItem.bkptno, &isInternal) : null;
610 if(bp && bp.inserted && bp.bp.addr)
612 if(bp.type.isInternal)
616 if(stopItem && stopItem.frame)
618 if(bpInternal && bpRunToCursor && bpRunToCursor.inserted && !strcmp(bpRunToCursor.bp.addr, bp.bp.addr))
619 bpUser = bpRunToCursor;
622 for(item : (bpInternal ? ide.workspace.breakpoints : sysBPs); item.inserted)
624 if(item.bp && item.bp.addr && !strcmp(item.bp.addr, bp.bp.addr))
636 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Invalid stopItem!");
637 if(bpUser && stopItem.frame.addr && strcmp(stopItem.frame.addr, bpUser.bp.addr))
638 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Breakpoint bkptno(", stopItem.bkptno, ") address missmatch!");
641 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Breakpoint bkptno(", stopItem.bkptno, ") invalid or not found!");
642 if((bpUser && !ignoreBreakpoints) || (bpInternal && userAction.breaksOnInternalBreakpoint))
644 hitThread = stopItem.threadid;
648 signalThread = stopItem.threadid;
652 case locationReached:
654 ignoreBreakpoints = false;
656 case valgrindStartPause:
657 GdbExecContinue(true);
665 if(curEvent == signal)
669 ide.outputView.debugBox.Logf($"Signal received: %s - %s\n", stopItem.name, stopItem.meaning);
670 ide.outputView.debugBox.Logf(" %s:%d\n", (s = CopySystemPath(stopItem.frame.file)), stopItem.frame.line);
671 ide.outputView.Show();
672 ide.callStackView.Show();
675 else if(curEvent == breakEvent)
677 ide.threadsView.Show();
678 ide.callStackView.Show();
679 ide.callStackView.Activate();
681 else if(curEvent == hit)
683 if(BreakpointHit(stopItem, bpInternal, bpUser))
685 ide.AdjustDebugMenus();
686 if(bpUser && bpUser.type == runToCursor)
688 ignoreBreakpoints = false;
689 UnsetBreakpoint(bpUser);
690 delete bpRunToCursor;
695 if(breakType == advance && bpInternal && (bpInternal.type == internalMain || bpInternal.type == internalEntry))
698 GdbExecAdvance(breakString, 0);
703 GdbExecContinue(false);
709 if(monitor && curEvent.canBeMonitored)
712 activeThread = stopItem.threadid;
713 GdbCommand(0, false, "-thread-list-ids");
714 InternalSelectFrame(activeFrameLevel);
715 GoToStackFrameLine(activeFrameLevel, true, false);
717 ide.ShowCodeEditor();
718 ide.AdjustDebugMenus();
719 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
732 #ifdef GDB_DEBUG_CONSOLE
733 char lastGdbOutput[GdbGetLineSize];
735 #if defined(__unix__)
736 ProgramThread progThread { };
740 #define _ChangeUserAction(value) ChangeUserAction(__FILE__, __LINE__, value)
741 void ChangeUserAction(char * file, int line, DebuggerUserAction value)
743 bool same = value == userAction;
745 __dpl2(file, line, _dpct, dplchan::debuggerUserAction, 0, userAction, /*same ? " *** == *** " : */" -> ", value);
750 #define _ChangeUserAction(value) userAction = value
754 #define _ChangeState(value) ChangeState(__FILE__, __LINE__, value)
755 void ChangeState(char * file, int line, DebuggerState value)
757 #define _ChangeState(value) ChangeState(value)
758 void ChangeState(DebuggerState value)
761 bool same = value == state;
762 #if 0 //def _DEBUG_INST
763 __dpl2(file, line, _dpct, dplchan::debuggerState, 0, state, same ? " *** == *** " : " -> ", value);
766 if(!same) ide.AdjustDebugMenus();
771 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::CleanUp");
773 stackFrames.Free(Frame::Free);
783 waitingForPID = false;
788 sentBreakInsert = false;
789 ignoreBreakpoints = false;
792 activeFrameLevel = 0;
809 bpRunToCursor = null;
811 delete currentCompiler;
814 WatchesReleaseCodeEditor();
817 projectsLibraryLoaded.Free();
819 /*GdbThread gdbThread
825 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::constructor");
826 ideProcessId = Process_GetCurrentProcessId();
828 sysBPs.Add((intBpEntry = Breakpoint { type = internalEntry, enabled = false, level = -1 }));
829 sysBPs.Add((intBpMain = Breakpoint { type = internalMain, function = "main", enabled = true, level = -1 }));
830 #if defined(__WIN32__)
831 sysBPs.Add((intBpWinMain = Breakpoint { type = internalWinMain, function = "WinMain", enabled = true, level = -1 }));
833 sysBPs.Add(Breakpoint { type = internalModulesLoaded, enabled = true, level = -1 });
834 sysBPs.Add(Breakpoint { type = internalModuleLoad, function = "InternalModuleLoadBreakpoint", enabled = true, level = -1 });
839 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::destructor");
847 property bool isActive { get { return state == running || state == stopped; } }
848 property bool isPrepared { get { return state == loaded || state == running || state == stopped; } }
852 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Resume");
853 _ChangeUserAction(resume);
854 GdbExecContinue(true);
859 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Break");
860 _ChangeUserAction(_break);
864 GdbDebugBreak(false);
870 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Stop");
871 _ChangeUserAction(stop);
878 GdbDebugBreak(false);
892 void Restart(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
894 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Restart");
895 _ChangeUserAction(restart);
896 if(StartSession(compiler, config, bitDepth, useValgrind, true, false) == loaded)
900 bool GoToCodeLine(char * location)
903 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GoToCodeLine(", location, ")");
904 codloc = CodeLocation::ParseCodeLocation(location);
907 CodeEditor editor = (CodeEditor)ide.OpenFile(codloc.absoluteFile, false, true, null, no, normal, false);
910 EditBox editBox = editor.editBox;
911 if(editBox.GoToLineNum(codloc.line - 1))
912 editBox.GoToPosition(editBox.line, codloc.line - 1, 0);
919 bool GoToStackFrameLine(int stackLevel, bool askForLocation, bool fromCallStack)
921 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GoToStackFrameLine(", stackLevel, ", ", askForLocation, ")");
924 char filePath[MAX_LOCATION];
925 char sourceDir[MAX_LOCATION];
927 CodeEditor editor = null;
928 if(stackLevel == -1) // this (the two lines) is part of that fix that I would not put in for some time
930 for(frame = stackFrames.first; frame; frame = frame.next)
931 if(frame.level == stackLevel)
936 ide.callStackView.Show();
938 if(frame.absoluteFile)
939 editor = (CodeEditor)ide.OpenFile(frame.absoluteFile, false, true, null, no, normal, false);
940 if(!editor && frame.file)
941 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
942 if(!frame.absoluteFile && askForLocation && frame.file)
945 char title[MAX_LOCATION];
946 snprintf(title, sizeof(title), $"Provide source file location for %s", (s = CopySystemPath(frame.file)));
947 title[sizeof(title)-1] = 0;
949 if(SourceDirDialog(title, ide.workspace.projectDir, frame.file, sourceDir))
951 AddSourceDir(sourceDir);
952 frame.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(frame.file);
955 if(!editor && frame.absoluteFile)
956 editor = (CodeEditor)ide.OpenFile(frame.absoluteFile, false, true, null, no, normal, false);
958 ide.RepositionWindows(false);
960 if(editor && frame.line)
962 EditBox editBox = editor.editBox;
963 editBox.GoToLineNum(frame.line - 1);
964 editBox.GoToPosition(editBox.line, frame.line - 1, 0);
972 void SelectThread(int thread)
974 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SelectThread(", thread, ")");
975 _ChangeUserAction(selectThread);
978 if(thread != activeThread)
980 activeFrameLevel = -1;
981 ide.callStackView.Clear();
982 GdbCommand(0, false, "-thread-select %d", thread);
984 InternalSelectFrame(activeFrameLevel);
985 GoToStackFrameLine(activeFrameLevel, true, false);
989 ide.callStackView.Show();
993 void SelectFrame(int frame)
995 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SelectFrame(", frame, ")");
996 _ChangeUserAction(selectFrame);
999 if(frame != activeFrameLevel)
1001 InternalSelectFrame(frame);
1008 void InternalSelectFrame(int frame)
1010 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::InternalSelectFrame(", frame, ")");
1011 activeFrameLevel = frame; // there is no active frame number in the gdb reply
1012 GdbCommand(0, false, "-stack-select-frame %d", activeFrameLevel);
1013 for(activeFrame = stackFrames.first; activeFrame; activeFrame = activeFrame.next)
1014 if(activeFrame.level == activeFrameLevel)
1018 void HandleExit(char * reason, char * code)
1020 bool returnedExitCode = false;
1021 char verboseExitCode[128];
1023 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::HandleExit(", reason, ", ", code, ")");
1024 _ChangeState(loaded); // this state change seems to be superfluous, might be in case of gdb crash
1025 targetProcessId = 0;
1029 snprintf(verboseExitCode, sizeof(verboseExitCode), $" with exit code %s", code);
1030 verboseExitCode[sizeof(verboseExitCode)-1] = 0;
1033 verboseExitCode[0] = '\0';
1037 // ClearBreakDisplay();
1041 for(wh : ide.workspace.watches)
1043 if(wh.type) FreeType(wh.type);
1046 ide.watchesView.UpdateWatch(wh);
1050 #if defined(__unix__)
1053 progThread.terminate = true;
1056 fifoFile.CloseInput();
1066 char program[MAX_LOCATION];
1067 GetSystemPathBuffer(program, targetFile);
1069 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
1070 else if(!strcmp(reason, "exited-normally"))
1071 ide.outputView.debugBox.Logf($"The program %s has exited normally%s.\n", program, verboseExitCode);
1072 else if(!strcmp(reason, "exited"))
1073 ide.outputView.debugBox.Logf($"The program %s has exited%s.\n", program, verboseExitCode);
1074 else if(!strcmp(reason, "exited-signalled"))
1075 ide.outputView.debugBox.Logf($"The program %s has exited with a signal%s.\n", program, verboseExitCode);
1077 ide.outputView.debugBox.Logf($"The program %s has exited (gdb provided an unknown reason)%s.\n", program, verboseExitCode);
1082 DebuggerState StartSession(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool restart, bool ignoreBreakpoints)
1084 DebuggerState result = none;
1085 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StartSession(restart(", restart, "), ignoreBreakpoints(", ignoreBreakpoints, ")");
1086 if(restart && state == running && targetProcessId)
1088 breakType = DebuggerAction::restart;
1089 GdbDebugBreak(false);
1093 if(restart && state == stopped)
1095 if(needReset && state == loaded)
1096 GdbExit(); // this reset is to get a clean state with all the breakpoints until a better state can be maintained on program exit
1098 if(result == none || result == terminated)
1100 ide.outputView.ShowClearSelectTab(debug);
1101 ide.outputView.debugBox.Logf($"Starting debug mode\n");
1108 for(bp : ide.workspace.breakpoints)
1114 if(GdbInit(compiler, config, bitDepth, useValgrind))
1119 this.ignoreBreakpoints = ignoreBreakpoints;
1124 void Start(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
1126 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::Start()");
1127 _ChangeUserAction(start);
1128 if(StartSession(compiler, config, bitDepth, useValgrind, true, false) == loaded)
1132 void StepInto(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
1134 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepInto()");
1135 _ChangeUserAction(stepInto);
1136 switch(StartSession(compiler, config, bitDepth, useValgrind, false, false))
1138 case loaded: GdbExecRun(); break;
1139 case stopped: GdbExecStep(); break;
1143 void StepOver(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool ignoreBreakpoints)
1145 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepOver()");
1146 _ChangeUserAction(stepOver);
1147 switch(StartSession(compiler, config, bitDepth, useValgrind, false, ignoreBreakpoints))
1149 case loaded: GdbExecRun(); break;
1150 case stopped: GdbExecNext(); break;
1154 void StepUntil(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, bool ignoreBreakpoints)
1156 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepUntil()");
1157 _ChangeUserAction(stepUntil);
1158 switch(StartSession(compiler, config, bitDepth, useValgrind, false, ignoreBreakpoints))
1160 case loaded: GdbExecRun(); break;
1161 case stopped: GdbExecUntil(null, 0); break;
1165 void StepOut(bool ignoreBreakpoints)
1167 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::StepOut()");
1168 _ChangeUserAction(stepOut);
1169 if(state == stopped)
1171 this.ignoreBreakpoints = ignoreBreakpoints;
1175 GdbExecContinue(true);
1179 void RunToCursor(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind, char * absoluteFilePath, int lineNumber, bool ignoreBreakpoints, bool atSameLevel, bool oldImplementation)
1181 char relativeFilePath[MAX_LOCATION];
1182 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::RunToCursor()");
1183 _ChangeUserAction(runToCursor);
1184 WorkspaceGetRelativePath(absoluteFilePath, relativeFilePath, null);
1186 if(bpRunToCursor && bpRunToCursor.inserted && symbols)
1188 UnsetBreakpoint(bpRunToCursor);
1189 delete bpRunToCursor;
1192 StartSession(compiler, config, bitDepth, useValgrind, false, ignoreBreakpoints);
1195 if(oldImplementation)
1197 bpRunToCursor = Breakpoint { };
1198 bpRunToCursor.absoluteFilePath = absoluteFilePath;
1199 bpRunToCursor.relativeFilePath = relativeFilePath;
1200 bpRunToCursor.line = lineNumber;
1201 bpRunToCursor.type = runToCursor;
1202 bpRunToCursor.enabled = true;
1203 bpRunToCursor.level = atSameLevel ? frameCount - activeFrameLevel -1 : -1;
1208 breakType = advance;
1209 breakString = PrintString(relativeFilePath, ":", lineNumber);
1212 else if(state == stopped)
1214 if(oldImplementation)
1215 GdbExecContinue(true);
1219 GdbExecUntil(absoluteFilePath, lineNumber);
1221 GdbExecAdvance(absoluteFilePath, lineNumber);
1226 void GetCallStackCursorLine(bool * error, int * lineCursor, int * lineTopFrame)
1228 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetCallStackCursorLine()");
1229 if(activeFrameLevel == -1)
1237 *error = signalOn && activeThread == signalThread;
1238 *lineCursor = activeFrameLevel - ((frameCount > 192 && activeFrameLevel > 191) ? frameCount - 192 - 1 : 0) + 1;
1239 *lineTopFrame = activeFrameLevel ? 1 : 0;
1243 int GetMarginIconsLineNumbers(char * fileName, int lines[], bool enabled[], int max, bool * error, int * lineCursor, int * lineTopFrame)
1245 char winFilePath[MAX_LOCATION];
1246 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1248 Iterator<Breakpoint> it { ide.workspace.breakpoints };
1249 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GetMarginIconsLineNumbers()");
1250 while(it.Next() && count < max)
1252 Breakpoint bp = it.data;
1255 if(bp.absoluteFilePath && bp.absoluteFilePath[0] && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1257 lines[count] = bp.line;
1258 enabled[count] = bp.enabled;
1263 if(activeFrameLevel == -1)
1271 *error = signalOn && activeThread == signalThread;
1272 if(activeFrame && activeFrame.absoluteFile && !fstrcmp(absoluteFilePath, activeFrame.absoluteFile))
1273 *lineCursor = activeFrame.line;
1276 if(activeFrame && stopItem && stopItem.frame && activeFrame.level == stopItem.frame.level)
1278 else if(stopItem && stopItem.frame && stopItem.frame.absoluteFile && !fstrcmp(absoluteFilePath, stopItem.frame.absoluteFile))
1279 *lineTopFrame = stopItem.frame.line;
1283 if(*lineTopFrame == *lineCursor && *lineTopFrame)
1289 void ChangeWatch(DataRow row, char * expression)
1291 Watch wh = (Watch)row.tag;
1292 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ChangeWatch(", expression, ")");
1295 delete wh.expression;
1297 wh.expression = CopyString(expression);
1300 Iterator<Watch> it { ide.workspace.watches };
1302 ide.workspace.watches.Delete(it.pointer);
1308 row.tag = (int64)wh;
1309 ide.workspace.watches.Add(wh);
1311 wh.expression = CopyString(expression);
1313 ide.workspace.Save();
1314 //if(expression && state == stopped)
1319 void MoveIcons(char * fileName, int lineNumber, int move, bool start)
1321 char winFilePath[MAX_LOCATION];
1322 char * absoluteFilePath = GetSlashPathBuffer(winFilePath, fileName);
1325 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::MoveIcons()");
1326 for(bpLink = ide.workspace.breakpoints.first; bpLink; bpLink = next)
1328 Breakpoint bp = (Breakpoint)bpLink.data;
1331 if(bp.type == user && bp.absoluteFilePath && !fstrcmp(bp.absoluteFilePath, absoluteFilePath))
1333 if(bp.line > lineNumber || (bp.line == lineNumber && start))
1335 if(move < 0 && (bp.line < lineNumber - move))
1336 ide.workspace.RemoveBreakpoint(bp);
1340 ide.breakpointsView.UpdateBreakpoint(bp.row);
1341 ide.workspace.Save();
1347 // moving code cursors is futile, on next step, stop, hit, cursors will be offset anyways
1350 bool SourceDirDialog(char * title, char * startDir, char * test, char * sourceDir)
1354 String srcDir = null;
1356 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SourceDirDialog()");
1357 debuggerFileDialog.text = title;
1358 debuggerFileDialog.currentDirectory = startDir;
1359 debuggerFileDialog.master = ide;
1361 while(debuggerFileDialog.Modal())
1363 strcpy(sourceDir, debuggerFileDialog.filePath);
1364 if(!fstrcmp(ide.workspace.projectDir, sourceDir) &&
1365 MessageBox { type = yesNo, master = ide,
1366 contents = $"This is the project directory.\nWould you like to try again?",
1367 text = $"Invalid Source Directory" }.Modal() == no)
1371 for(dir : ide.workspace.sourceDirs)
1373 if(!fstrcmp(dir, sourceDir))
1381 MessageBox { type = yesNo, master = ide,
1382 contents = $"This source directory is already specified.\nWould you like to try again?",
1383 text = $"Invalid Source Directory" }.Modal() == no)
1389 char file[MAX_LOCATION];
1390 strcpy(file, sourceDir);
1391 PathCat(file, test);
1392 result = FileExists(file);
1394 MessageBox { type = yesNo, master = ide,
1395 contents = $"Unable to locate source file.\nWould you like to try again?",
1396 text = $"Invalid Source Directory" }.Modal() == no)
1410 void AddSourceDir(char * sourceDir)
1412 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::AddSourceDir(", sourceDir, ")");
1413 ide.workspace.sourceDirs.Add(CopyString(sourceDir));
1414 ide.workspace.Save();
1418 DebuggerState oldState = state;
1423 GdbDebugBreak(true);
1426 GdbCommand(0, false, "-environment-directory \"%s\"", sourceDir);
1429 if(oldState == running)
1430 GdbExecContinue(false);
1434 void ToggleBreakpoint(char * fileName, int lineNumber)
1436 char absolutePath[MAX_LOCATION];
1437 Breakpoint bp = null;
1439 _dpl2(_dpct, dplchan::debuggerBreakpoints, 0, "Debugger::ToggleBreakpoint(", fileName, ":", lineNumber, ")");
1441 GetSlashPathBuffer(absolutePath, fileName);
1442 for(i : ide.workspace.breakpoints; i.type == user && i.absoluteFilePath && !fstrcmp(i.absoluteFilePath, absolutePath) && i.line == lineNumber)
1451 ide.workspace.RemoveBreakpoint(bp);
1460 char relativePath[MAX_LOCATION];
1462 WorkspaceGetRelativePath(absolutePath, relativePath, &owner);
1464 if(!owner && !FileExists(absolutePath))
1466 char title[MAX_LOCATION];
1467 char directory[MAX_LOCATION];
1468 char sourceDir[MAX_LOCATION];
1469 StripLastDirectory(absolutePath, directory);
1470 snprintf(title, sizeof(title), $"Provide source files location directory for %s", relativePath);
1471 title[sizeof(title)-1] = 0;
1474 String srcDir = null;
1475 for(dir : ide.workspace.sourceDirs)
1477 if(IsPathInsideOf(absolutePath, dir))
1479 MakePathRelative(absolutePath, dir, relativePath);
1487 if(SourceDirDialog(title, directory, null, sourceDir))
1489 if(IsPathInsideOf(absolutePath, sourceDir))
1491 AddSourceDir(sourceDir);
1492 MakePathRelative(absolutePath, sourceDir, relativePath);
1495 else if(MessageBox { type = yesNo, master = ide,
1496 contents = $"You must provide a valid source directory in order to place a breakpoint in this file.\nWould you like to try again?",
1497 text = $"Invalid Source Directory" }.Modal() == no)
1504 ide.workspace.bpCount++;
1505 bp = { line = lineNumber, type = user, enabled = true, level = -1, project = owner };
1506 ide.workspace.breakpoints.Add(bp);
1507 bp.absoluteFilePath = absolutePath;
1508 bp.relativeFilePath = relativePath;
1509 ide.breakpointsView.AddBreakpoint(bp);
1514 DebuggerState oldState = state;
1519 GdbDebugBreak(true);
1522 if(!SetBreakpoint(bp, false))
1523 SetBreakpoint(bp, true);
1526 if(oldState == running)
1527 GdbExecContinue(false);
1530 ide.workspace.Save();
1533 void UpdateRemovedBreakpoint(Breakpoint bp)
1535 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::UpdateRemovedBreakpoint()");
1536 if(targeted && bp.inserted)
1538 DebuggerState oldState = state;
1543 GdbDebugBreak(true);
1546 UnsetBreakpoint(bp);
1549 if(oldState == running)
1550 GdbExecContinue(false);
1556 void ParseFrame(Frame frame, char * string)
1559 Array<char *> frameTokens { minAllocSize = 50 };
1560 Array<char *> argsTokens { minAllocSize = 50 };
1561 Array<char *> argumentTokens { minAllocSize = 50 };
1562 DebugListItem item { };
1565 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ParseFrame()");
1566 TokenizeList(string, ',', frameTokens);
1567 for(i = 0; i < frameTokens.count; i++)
1569 if(TokenizeListItem(frameTokens[i], item))
1571 StripQuotes(item.value, item.value);
1572 if(!strcmp(item.name, "level"))
1573 frame.level = atoi(item.value);
1574 else if(!strcmp(item.name, "addr"))
1575 frame.addr = item.value;
1576 else if(!strcmp(item.name, "func"))
1577 frame.func = item.value;
1578 else if(!strcmp(item.name, "args"))
1580 if(!strcmp(item.value, "[]"))
1581 frame.argsCount = 0;
1584 item.value = StripBrackets(item.value);
1585 TokenizeList(item.value, ',', argsTokens);
1586 for(j = 0; j < argsTokens.count; j++)
1588 argsTokens[j] = StripCurlies(argsTokens[j]);
1589 TokenizeList(argsTokens[j], ',', argumentTokens);
1590 for(k = 0; k < argumentTokens.count; k++)
1593 frame.args.Add(arg);
1594 if(TokenizeListItem(argumentTokens[k], item))
1596 if(!strcmp(item.name, "name"))
1598 StripQuotes(item.value, item.value);
1599 arg.name = item.value;
1601 else if(!strcmp(item.name, "value"))
1603 StripQuotes(item.value, item.value);
1604 arg.val = item.value;
1607 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "frame args item (", item.name, "=", item.value, ") is unheard of");
1610 _dpl(0, "Bad frame args item");
1612 argumentTokens.RemoveAll();
1614 frame.argsCount = argsTokens.count;
1615 argsTokens.RemoveAll();
1618 else if(!strcmp(item.name, "from"))
1619 frame.from = item.value;
1620 else if(!strcmp(item.name, "file"))
1621 frame.file = item.value;
1622 else if(!strcmp(item.name, "line"))
1623 frame.line = atoi(item.value);
1624 else if(!strcmp(item.name, "fullname"))
1626 // GDB 6.3 on OS X is giving "fullname" and "dir", all in absolute, but file name only in 'file'
1627 Workspace ws = ide.workspace;
1630 String path = ide.workspace.GetPathWorkspaceRelativeOrAbsolute(item.value);
1631 if(strcmp(frame.file, path))
1635 frame.absoluteFile = item.value;
1638 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "frame member (", item.name, "=", item.value, ") is unheard of");
1641 _dpl(0, "Bad frame");
1646 delete argumentTokens;
1650 Breakpoint GetBreakpointById(int id, bool * isInternal)
1652 Breakpoint bp = null;
1653 //_dpl2(_dpct, dplchan::debuggerBreakpoints, 0, "Debugger::GetBreakpointById(", id, ")");
1655 *isInternal = false;
1658 for(i : sysBPs; i.bp && i.bp.id == id)
1665 if(!bp && bpRunToCursor && bpRunToCursor.bp && bpRunToCursor.bp.id == id)
1669 for(i : ide.workspace.breakpoints; i.bp && i.bp.id == id)
1679 GdbDataBreakpoint ParseBreakpoint(char * string, Array<char *> outTokens)
1682 GdbDataBreakpoint bp { };
1683 DebugListItem item { };
1684 Array<char *> bpTokens { minAllocSize = 16 };
1685 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ParseBreakpoint()");
1686 string = StripCurlies(string);
1687 TokenizeList(string, ',', bpTokens);
1688 for(i = 0; i < bpTokens.count; i++)
1690 if(TokenizeListItem(bpTokens[i], item))
1692 StripQuotes(item.value, item.value);
1693 if(!strcmp(item.name, "number"))
1695 if(!strchr(item.value, '.'))
1696 bp.id = atoi(item.value);
1697 bp.number = item.value;
1699 else if(!strcmp(item.name, "type"))
1700 bp.type = item.value;
1701 else if(!strcmp(item.name, "disp"))
1702 bp.disp = item.value;
1703 else if(!strcmp(item.name, "enabled"))
1704 bp.enabled = (!strcmpi(item.value, "y"));
1705 else if(!strcmp(item.name, "addr"))
1707 if(outTokens && !strcmp(item.value, "<MULTIPLE>"))
1710 Array<GdbDataBreakpoint> bpArray = bp.multipleBPs = { };
1711 while(outTokens.count > ++c)
1713 GdbDataBreakpoint multBp = ParseBreakpoint(outTokens[c], null);
1714 bpArray.Add(multBp);
1718 bp.addr = item.value;
1720 else if(!strcmp(item.name, "func"))
1721 bp.func = item.value;
1722 else if(!strcmp(item.name, "file"))
1723 bp.file = item.value;
1724 else if(!strcmp(item.name, "fullname"))
1725 bp.fullname = item.value;
1726 else if(!strcmp(item.name, "line"))
1727 bp.line = atoi(item.value);
1728 else if(!strcmp(item.name, "at"))
1730 else if(!strcmp(item.name, "times"))
1731 bp.times = atoi(item.value);
1732 else if(!strcmp(item.name, "original-location") || !strcmp(item.name, "thread-groups"))
1733 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "breakpoint member (", item.name, "=", item.value, ") is ignored");
1735 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "breakpoint member (", item.name, "=", item.value, ") is unheard of");
1743 void ShowDebuggerViews()
1745 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ShowDebuggerViews()");
1746 ide.outputView.Show();
1747 ide.outputView.SelectTab(debug);
1748 ide.threadsView.Show();
1749 ide.callStackView.Show();
1750 ide.watchesView.Show();
1754 void HideDebuggerViews()
1756 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::HideDebuggerViews()");
1757 ide.RepositionWindows(true);
1760 bool ::GdbCommand(Time timeOut, bool focus, char * format, ...)
1762 bool result = false;
1766 // TODO: Improve this limit
1767 static char string[MAX_F_STRING*4];
1769 va_start(args, format);
1770 vsnprintf(string, sizeof(string), format, args);
1771 string[sizeof(string)-1] = 0;
1775 ide.debugger.serialSemaphore.TryWait();
1777 #ifdef GDB_DEBUG_CONSOLE
1778 _dpl2(_dpct, dplchan::gdbCommand, 0, string);
1780 #ifdef GDB_DEBUG_OUTPUT
1781 ide.outputView.gdbBox.Logf("cmd: %s\n", string);
1783 #ifdef GDB_DEBUG_GUI
1785 ide.gdbDialog.AddCommand(string);
1788 strcat(string,"\n");
1789 gdbHandle.Puts(string);
1792 Process_ShowWindows(targetProcessId);
1798 startTime = GetTime();
1801 if(ide.debugger.serialSemaphore.TryWait())
1808 if(GetTime() - startTime > timeOut)
1816 ide.debugger.serialSemaphore.Wait();
1825 bool ValidateBreakpoint(Breakpoint bp)
1827 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ValidateBreakpoint()");
1828 if(modules && bp.line && bp.bp)
1830 if(bp.bp.line != bp.line)
1836 ide.outputView.debugBox.Logf("WOULD HAVE -- Invalid breakpoint disabled: %s:%d\n", bp.relativeFilePath, bp.line);
1838 //UnsetBreakpoint(bp);
1839 //bp.enabled = false;
1845 ide.outputView.debugBox.Logf("Debugger Error: ValidateBreakpoint error\n");
1846 bp.line = bp.bp.line;
1853 void BreakpointsMaintenance()
1855 //_dpl2(_dpct, dplchan::debuggerBreakpoints, 0, "Debugger::BreakpointsMaintenance()");
1858 if(gdbExecution.suspendInternalBreakpoints)
1860 for(bp : sysBPs; bp.inserted)
1861 UnsetBreakpoint(bp);
1865 DirExpression objDir = ide.project.GetObjDir(currentCompiler, prjConfig, bitDepth);
1866 for(bp : sysBPs; !bp.inserted)
1868 bool insert = false;
1869 if(bp.type == internalModulesLoaded)
1871 char path[MAX_LOCATION];
1872 char name[MAX_LOCATION];
1873 char fixedModuleName[MAX_FILENAME];
1876 bool moduleLoadBlock = false;
1878 ReplaceSpaces(fixedModuleName, ide.project.moduleName);
1879 snprintf(name, sizeof(name),"%s.main.ec", fixedModuleName);
1880 name[sizeof(name)-1] = 0;
1881 strcpy(path, ide.workspace.projectDir);
1882 PathCatSlash(path, objDir.dir);
1883 PathCatSlash(path, name);
1884 f = FileOpen(path, read);
1887 for(lineNumber = 1; !f.Eof(); lineNumber++)
1889 if(f.GetLine(line, sizeof(line) - 1))
1891 bool moduleLoadLine;
1892 TrimLSpaces(line, line);
1893 moduleLoadLine = !strncmp(line, "eModule_Load", strlen("eModule_Load"));
1894 if(!moduleLoadBlock && moduleLoadLine)
1895 moduleLoadBlock = true;
1896 else if(moduleLoadBlock && !moduleLoadLine && strlen(line) > 0)
1902 char relative[MAX_LOCATION];
1903 bp.absoluteFilePath = path;
1904 MakePathRelative(path, ide.workspace.projectDir, relative);
1905 bp.relativeFilePath = relative;
1906 bp.line = lineNumber;
1912 else if(bp.type == internalModuleLoad)
1916 for(prj : ide.workspace.projects)
1918 if(!strcmp(prj.moduleName, "ecere"))
1920 ProjectNode node = prj.topNode.Find("instance.c", false);
1923 char path[MAX_LOCATION];
1924 char relative[MAX_LOCATION];
1925 node.GetFullFilePath(path);
1926 bp.absoluteFilePath = path;
1927 MakePathRelative(path, prj.topNode.path, relative);
1928 bp.relativeFilePath = relative;
1940 if(!SetBreakpoint(bp, false))
1941 SetBreakpoint(bp, true);
1947 if(userAction != runToCursor && bpRunToCursor && bpRunToCursor.inserted)
1948 UnsetBreakpoint(bpRunToCursor);
1949 if(bpRunToCursor && !bpRunToCursor.inserted)
1951 if(!SetBreakpoint(bpRunToCursor, false))
1952 SetBreakpoint(bpRunToCursor, true);
1955 if(ignoreBreakpoints)
1957 for(bp : ide.workspace.breakpoints; bp.inserted)
1958 UnsetBreakpoint(bp);
1962 for(bp : ide.workspace.breakpoints; !bp.inserted && bp.type == user)
1966 if(!SetBreakpoint(bp, false))
1967 SetBreakpoint(bp, true);
1976 bp.bp = GdbDataBreakpoint { };
1983 void UnsetBreakpoint(Breakpoint bp)
1985 char * s = null; _dpl2(_dpct, dplchan::debuggerBreakpoints, 0, "Debugger::UnsetBreakpoint(", s=bp.CopyLocationString(false), ") -- ", bp.type); delete s;
1986 if(symbols && bp.inserted)
1988 GdbCommand(0, false, "-break-delete %s", bp.bp.number);
1989 bp.inserted = false;
1995 bool SetBreakpoint(Breakpoint bp, bool removePath)
1997 char * s = null; _dpl2(_dpct, dplchan::debuggerBreakpoints, 0, "Debugger::SetBreakpoint(", s=bp.CopyLocationString(false), ", ", removePath ? "**** removePath(true) ****" : "", ") -- ", bp.type); delete s;
1998 breakpointError = false;
1999 if(symbols && bp.enabled && (!bp.project || bp.project.GetTargetType(bp.project.config) == staticLibrary || bp.project == ide.project || projectsLibraryLoaded[bp.project.name]))
2001 sentBreakInsert = true;
2003 GdbCommand(0, false, "-break-insert *%s", bp.address);
2006 char * location = bp.CopyLocationString(removePath);
2007 GdbCommand(0, false, "-break-insert %s", location);
2010 if(!breakpointError)
2012 char * address = null;
2013 if(bpItem && bpItem.multipleBPs && bpItem.multipleBPs.count)
2016 GdbDataBreakpoint first = null;
2017 for(n : bpItem.multipleBPs)
2019 if(!fstrcmp(n.fullname, bp.absoluteFilePath) && !first)
2029 GdbCommand(0, false, "-break-disable %s", n.number);
2033 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error breakpoint already disabled.");
2038 address = CopyString(first.addr);
2039 bpItem.addr = first.addr;
2040 bpItem.func = first.func;
2041 bpItem.file = first.file;
2042 bpItem.fullname = first.fullname;
2043 bpItem.line = first.line;
2044 //bpItem.thread-groups = first.thread-groups;*/
2047 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error multiple breakpoints all disabled.");
2049 _dpl2(_dpct, dplchan::debuggerProblem, 0, "Debugger::SetBreakpoint -- error multiple breakpoints in exact same file not supported.");
2050 bpItem.multipleBPs.Free();
2051 delete bpItem.multipleBPs;
2056 bp.inserted = (bp.bp && bp.bp.number && strcmp(bp.bp.number, "0"));
2058 ValidateBreakpoint(bp);
2062 UnsetBreakpoint(bp);
2063 bp.address = address;
2065 SetBreakpoint(bp, removePath);
2068 return !breakpointError;
2075 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbGetStack()");
2077 stackFrames.Free(Frame::Free);
2078 GdbCommand(0, false, "-stack-info-depth");
2080 GdbCommand(0, false, "-stack-info-depth 192");
2081 if(frameCount && frameCount <= 192)
2082 GdbCommand(0, false, "-stack-list-frames 0 %d", Min(frameCount-1, 191));
2085 GdbCommand(0, false, "-stack-list-frames 0 %d", Min(frameCount-1, 95));
2086 GdbCommand(0, false, "-stack-list-frames %d %d", Max(frameCount - 96, 96), frameCount - 1);
2088 GdbCommand(0, false, "");
2093 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbTargetSet()");
2096 char escaped[MAX_LOCATION];
2097 strescpy(escaped, targetFile);
2098 GdbCommand(0, false, "file \"%s\"", escaped); //GDB/MI Missing Implementation in 5.1.1 but we now have -file-exec-and-symbols / -file-exec-file / -file-symbol-file
2105 const char *vgdbCommand = "/usr/bin/vgdb"; // TODO: vgdb command config option
2106 //GdbCommand(0, false, "-target-select remote | %s --pid=%d", "vgdb", targetProcessId);
2107 printf("target remote | %s --pid=%d\n", vgdbCommand, targetProcessId);
2108 GdbCommand(0, false, "target remote | %s --pid=%d", vgdbCommand, targetProcessId); // TODO: vgdb command config option
2111 GdbCommand(0, false, "info target"); //GDB/MI Missing Implementation -file-list-symbol-files and -file-list-exec-sections
2113 /*for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
2114 GdbCommand(0, false, "-environment-directory \"%s\"", prj.topNode.path);*/
2116 for(dir : ide.workspace.sourceDirs; dir && dir[0])
2118 bool interference = false;
2119 for(prj : ide.workspace.projects)
2121 if(!fstrcmp(prj.topNode.path, dir))
2123 interference = true;
2127 if(!interference && dir[0])
2128 GdbCommand(0, false, "-environment-directory \"%s\"", dir);
2136 /*void GdbTargetRelease()
2140 BreakpointsDeleteAll();
2141 GdbCommand(0, false, "file"); //GDB/MI Missing Implementation -target-detach
2147 void GdbDebugBreak(bool internal)
2149 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbDebugBreak()");
2153 breakType = DebuggerAction::internal;
2155 if(ide) ide.Update(null);
2157 if(Process_Break(targetProcessId)) //GdbCommand(0, false, "-exec-interrupt");
2158 serialSemaphore.Wait();
2161 _ChangeState(loaded);
2162 targetProcessId = 0;
2167 ide.outputView.debugBox.Logf("Debugger Error: GdbDebugBreak with not target id should never happen\n");
2172 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecRun()");
2177 ShowDebuggerViews();
2179 GdbExecContinue(true);
2180 else if(!GdbCommand(3, true, "-exec-run"))
2181 gdbExecution = none;
2184 void GdbExecContinue(bool focus)
2186 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecContinue()");
2189 GdbCommand(0, focus, "-exec-continue");
2194 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecNext()");
2195 gdbExecution = next;
2197 GdbCommand(0, true, "-exec-next");
2200 void ForceUpdateCurrentFrame()
2203 GdbCommand(0, false, "-thread-list-ids");
2204 InternalSelectFrame(activeFrameLevel);
2205 GoToStackFrameLine(activeFrameLevel, true, false);
2207 ide.ShowCodeEditor();
2208 ide.AdjustDebugMenus();
2209 ideMainFrame.Activate(); // TOFIX: ide.Activate() is not reliable (app inactive)
2213 void GdbExecUntil(char * absoluteFilePath, int lineNumber)
2215 bool forceUpdate = false;
2216 char relativeFilePath[MAX_LOCATION];
2217 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecUntil()");
2218 gdbExecution = until;
2220 if(absoluteFilePath)
2222 WorkspaceGetRelativePath(absoluteFilePath, relativeFilePath, null);
2223 if(!GdbCommand(0.1, true, "-exec-until %s:%d", relativeFilePath, lineNumber))
2225 GetLastDirectory(relativeFilePath, relativeFilePath);
2226 if(GdbCommand(1, true, "-exec-until %s:%d", relativeFilePath, lineNumber))
2231 GdbCommand(0, true, "-exec-until");
2233 // This is to handle GDB 6.3 on OS X not giving us *running then *stopped:
2234 // (It may not be ideal, we may need to wait?)
2236 ForceUpdateCurrentFrame();
2239 void GdbExecAdvance(char * absoluteFilePathOrLocation, int lineNumber)
2241 bool forceUpdate = false;
2242 char relativeFilePath[MAX_LOCATION];
2243 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecAdvance()");
2244 gdbExecution = advance;
2248 WorkspaceGetRelativePath(absoluteFilePathOrLocation, relativeFilePath, null);
2249 if(!GdbCommand(0.1, true, "advance %s:%d", relativeFilePath, lineNumber)) // should use -exec-advance -- GDB/MI implementation missing
2251 GetLastDirectory(relativeFilePath, relativeFilePath);
2252 if(GdbCommand(1, true, "advance %s:%d", relativeFilePath, lineNumber))
2258 if(!GdbCommand(0.1, true, "advance %s", absoluteFilePathOrLocation)) // should use -exec-advance -- GDB/MI implementation missing
2260 GetLastDirectory(absoluteFilePathOrLocation, relativeFilePath);
2261 if(GdbCommand(1, true, "advance %s", relativeFilePath))
2266 // This is to handle GDB 6.3 on OS X not giving us *running then *stopped:
2267 // (It may not be ideal, we may need to wait?)
2269 ForceUpdateCurrentFrame();
2274 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecStep()");
2275 gdbExecution = step;
2277 GdbCommand(0, true, "-exec-step");
2280 void GdbExecFinish()
2282 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecFinish()");
2283 gdbExecution = finish;
2285 GdbCommand(0, true, "-exec-finish");
2288 void GdbExecCommon()
2290 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExecCommon()");
2291 BreakpointsMaintenance();
2294 #ifdef GDB_DEBUG_GUI
2295 void SendGDBCommand(char * command)
2297 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::SendGDBCommand()");
2298 DebuggerState oldState = state;
2303 GdbDebugBreak(true);
2306 GdbCommand(0, false, command);
2309 if(oldState == running)
2310 GdbExecContinue(false);
2314 void ClearBreakDisplay()
2316 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ClearBreakDisplay()");
2318 activeFrameLevel = -1;
2328 stackFrames.Free(Frame::Free);
2329 ide.callStackView.Clear();
2330 ide.threadsView.Clear();
2336 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbAbortExec()");
2338 GdbCommand(0, false, "-interpreter-exec console \"kill\""); // should use -exec-abort -- GDB/MI implementation incomplete
2342 bool GdbInit(CompilerConfig compiler, ProjectConfig config, int bitDepth, bool useValgrind)
2345 char oldDirectory[MAX_LOCATION];
2346 char tempPath[MAX_LOCATION];
2347 char command[MAX_F_STRING*4];
2348 Project project = ide.project;
2349 DirExpression targetDirExp = project.GetTargetDir(compiler, config, bitDepth);
2350 PathBackup pathBackup { };
2351 Map<String, String> envBackup { };
2353 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbInit()");
2354 if(currentCompiler != compiler)
2356 delete currentCompiler;
2357 currentCompiler = compiler;
2358 incref currentCompiler;
2361 this.bitDepth = bitDepth;
2362 usingValgrind = useValgrind;
2364 _ChangeState(loaded);
2366 sentBreakInsert = false;
2367 breakpointError = false;
2368 ignoreBreakpoints = false;
2374 projectsLibraryLoaded.Free();
2376 ide.outputView.ShowClearSelectTab(debug);
2377 ide.outputView.debugBox.Logf($"Starting debug mode\n");
2379 #ifdef GDB_DEBUG_OUTPUT
2380 ide.outputView.gdbBox.Logf("run: Starting GDB\n");
2383 strcpy(tempPath, ide.workspace.projectDir);
2384 PathCatSlash(tempPath, targetDirExp.dir);
2386 targetDir = CopyString(tempPath);
2387 project.CatTargetFileName(tempPath, compiler, config);
2389 targetFile = CopyString(tempPath);
2391 GetWorkingDir(oldDirectory, MAX_LOCATION);
2392 if(ide.workspace.debugDir && ide.workspace.debugDir[0])
2394 char temp[MAX_LOCATION];
2395 strcpy(temp, ide.workspace.projectDir);
2396 PathCatSlash(temp, ide.workspace.debugDir);
2397 ChangeWorkingDir(temp);
2400 ChangeWorkingDir(ide.workspace.projectDir);
2402 ide.SetPath(true, compiler, config, bitDepth);
2404 // TODO: This pollutes the environment, but at least it works
2405 // It shouldn't really affect the IDE as the PATH gets restored and other variables set for testing will unlikely cause problems
2406 // What is the proper solution for this? DualPipeOpenEnv?
2407 // gdb set environment commands don't seem to take effect
2408 for(e : ide.workspace.environmentVars)
2410 // Backing up the environment variables until we use DualPipeOpenEnv()
2411 envBackup[e.name] = CopyString(getenv(e.name));
2412 SetEnvironment(e.name, e.string);
2417 char * clArgs = ide.workspace.commandLineArgs;
2418 const char *valgrindCommand = "valgrind"; // TODO: valgrind command config option //TODO: valgrind options
2419 ValgrindLeakCheck vgLeakCheck = ide.workspace.vgLeakCheck;
2420 int vgRedzoneSize = ide.workspace.vgRedzoneSize;
2421 bool vgTrackOrigins = ide.workspace.vgTrackOrigins;
2422 vgLogFile = CreateTemporaryFile(vgLogPath, "ecereidevglog");
2426 vgLogThread.Create();
2430 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't open temporary log file for Valgrind output\n");
2433 if(result && !CheckCommandAvailable(valgrindCommand))
2435 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for Valgrind is not available.\n", valgrindCommand);
2440 char * vgRedzoneSizeFlag = vgRedzoneSize == -1 ? "" : PrintString(" --redzone-size=", vgRedzoneSize);
2441 sprintf(command, "%s --vgdb=yes --vgdb-error=0 --log-file=%s --leak-check=%s%s --track-origins=%s %s%s%s",
2442 valgrindCommand, vgLogPath, (char*)vgLeakCheck, vgRedzoneSizeFlag, vgTrackOrigins ? "yes" : "no", targetFile, clArgs ? " " : "", clArgs ? clArgs : "");
2443 if(vgRedzoneSize != -1)
2444 delete vgRedzoneSizeFlag;
2445 vgTargetHandle = DualPipeOpen(PipeOpenMode { output = true, /*error = true, */input = true }, command);
2448 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start Valgrind\n");
2454 incref vgTargetHandle;
2455 vgTargetThread.Create();
2457 targetProcessId = vgTargetHandle.GetProcessID();
2458 waitingForPID = false;
2459 if(!targetProcessId)
2461 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get Valgrind process ID\n");
2468 serialSemaphore.Wait();
2475 if(compiler.targetPlatform == win32)
2478 #if !((defined(__WORDSIZE) && __WORDSIZE == 8) || defined(__x86_64__))
2481 bitDepth == 32 ? "i686-w64-mingw32-gdb" : "gdb"); // x86_64-w64-mingw32-gdb
2484 // We really should have a box to select GDB in the compiler/toolchain options
2485 strcpy(command, "gdb");
2486 if(!CheckCommandAvailable(command))
2488 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Command %s for GDB is not available.\n", command);
2493 strcat(command, " -n -silent --interpreter=mi2"); //-async //\"%s\"
2495 gdbHandle = DualPipeOpen(PipeOpenMode { output = true, /*error = true, */input = true }, command);
2498 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't start GDB\n");
2508 gdbProcessId = gdbHandle.GetProcessID();
2511 ide.outputView.debugBox.Logf($"Debugger Fatal Error: Couldn't get GDB process ID\n");
2518 serialSemaphore.Wait();
2521 GdbCommand(0, false, "-gdb-set verbose off");
2522 //GdbCommand(0, false, "-gdb-set exec-done-display on");
2523 GdbCommand(0, false, "-gdb-set step-mode off");
2524 GdbCommand(0, false, "-gdb-set unwindonsignal on");
2525 //GdbCommand(0, false, "-gdb-set shell on");
2526 GdbCommand(0, false, "set print elements 992");
2527 GdbCommand(0, false, "-gdb-set backtrace limit 100000");
2531 //_ChangeState(terminated);
2537 #if defined(__unix__)
2539 CreateTemporaryDir(progFifoDir, "ecereide");
2540 strcpy(progFifoPath, progFifoDir);
2541 PathCat(progFifoPath, "ideprogfifo");
2542 if(!mkfifo(progFifoPath, 0600))
2544 //fileCreated = true;
2549 ide.outputView.debugBox.Logf(createFIFOMsg, progFifoPath);
2556 progThread.terminate = false;
2557 progThread.Create();
2561 #if defined(__WIN32__)
2562 GdbCommand(0, false, "-gdb-set new-console on");
2565 #if defined(__unix__)
2567 GdbCommand(0, false, "-inferior-tty-set %s", progFifoPath);
2571 GdbCommand(0, false, "-gdb-set args %s", ide.workspace.commandLineArgs ? ide.workspace.commandLineArgs : "");
2573 for(e : ide.workspace.environmentVars)
2575 GdbCommand(0, false, "set environment %s=%s", e.name, e.string);
2580 ChangeWorkingDir(oldDirectory);
2584 SetEnvironment(&e, e);
2593 delete targetDirExp;
2599 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbExit()");
2600 if(gdbHandle && gdbProcessId)
2603 GdbCommand(0, false, "-gdb-exit");
2612 vgLogFile.CloseInput();
2613 if(vgLogThread.created)
2623 vgTargetThread.Wait();
2633 _ChangeState(terminated); // this state change seems to be superfluous, is it safety for something?
2638 for(bp : ide.workspace.breakpoints)
2644 bpRunToCursor.Reset();
2646 ide.outputView.debugBox.Logf($"Debugging stopped\n");
2648 #if defined(__unix__)
2649 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
2651 progThread.terminate = true;
2654 fifoFile.CloseInput();
2660 DeleteFile(progFifoPath);
2661 progFifoPath[0] = '\0';
2670 bool WatchesLinkCodeEditor()
2672 bool goodFrame = activeFrame && activeFrame.absoluteFile;
2673 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesLinkCodeEditor()");
2674 if(codeEditor && (!goodFrame || fstrcmp(codeEditor.fileName, activeFrame.absoluteFile)))
2675 WatchesReleaseCodeEditor();
2677 if(!codeEditor && goodFrame)
2679 codeEditor = (CodeEditor)ide.OpenFile(activeFrame.absoluteFile, false, false, null, no, normal, false);
2682 codeEditor.inUseDebug = true;
2686 return codeEditor != null;
2689 void WatchesReleaseCodeEditor()
2691 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::WatchesReleaseCodeEditor()");
2694 codeEditor.inUseDebug = false;
2695 if(!codeEditor.visible)
2696 codeEditor.Destroy(0);
2701 bool ResolveWatch(Watch wh)
2703 bool result = false;
2705 _dpl2(_dpct, dplchan::debuggerWatches, 0, "Debugger::ResolveWatch()");
2717 char watchmsg[MAX_F_STRING];
2718 if(state == stopped && !codeEditor)
2719 wh.value = CopyString($"No source file found for selected frame");
2720 //if(codeEditor && state == stopped || state != stopped)
2723 Module backupPrivateModule;
2724 Context backupContext;
2725 Class backupThisClass;
2728 backupPrivateModule = GetPrivateModule();
2729 backupContext = GetCurrentContext();
2730 backupThisClass = GetThisClass();
2733 SetPrivateModule(codeEditor.privateModule);
2734 SetCurrentContext(codeEditor.globalContext);
2735 SetTopContext(codeEditor.globalContext);
2736 SetGlobalContext(codeEditor.globalContext);
2737 SetGlobalData(&codeEditor.globalData);
2740 exp = ParseExpressionString(wh.expression);
2742 if(exp && !GetParseError())
2744 char expString[4096];
2746 PrintExpression(exp, expString);
2749 if(codeEditor && activeFrame)
2750 DebugFindCtxTree(codeEditor.ast, activeFrame.line, 0);
2751 ProcessExpressionType(exp);
2753 wh.type = exp.expType;
2756 DebugComputeExpression(exp);
2757 if(ExpressionIsError(exp))
2759 GDBFallBack(exp, expString);
2762 /*if(exp.hasAddress)
2764 char temp[MAX_F_STRING];
2765 sprintf(temp, "0x%x", exp.address);
2766 wh.address = CopyString(temp);
2767 // wh.address = CopyStringf("0x%x", exp.address);
2772 Type dataType = exp.expType;
2775 char temp[MAX_F_STRING];
2776 switch(dataType.kind)
2779 sprintf(temp, "%i", exp.val.c);
2782 sprintf(temp, "%i", exp.val.s);
2787 sprintf(temp, "%i", exp.val.i);
2790 sprintf(temp, "%i", exp.val.i64);
2793 sprintf(temp, "%i", exp.val.p);
2798 long v = (long)exp.val.f;
2799 sprintf(temp, "%i", v);
2804 long v = (long)exp.val.d;
2805 sprintf(temp, "%i", v);
2810 wh.intVal = CopyString(temp);
2811 switch(dataType.kind)
2814 sprintf(temp, "0x%x", exp.val.c);
2817 sprintf(temp, "0x%x", exp.val.s);
2821 sprintf(temp, "0x%x", exp.val.i);
2824 sprintf(temp, "0x%x", exp.val.i64);
2827 sprintf(temp, "0x%x", exp.val.i64);
2830 sprintf(temp, "0x%x", exp.val.p);
2835 long v = (long)exp.val.f;
2836 sprintf(temp, "0x%x", v);
2841 long v = (long)exp.val.d;
2842 sprintf(temp, "0x%x", v);
2847 wh.hexVal = CopyString(temp);
2848 switch(dataType.kind)
2851 sprintf(temp, "0o%o", exp.val.c);
2854 sprintf(temp, "0o%o", exp.val.s);
2858 sprintf(temp, "0o%o", exp.val.i);
2861 sprintf(temp, "0o%o", exp.val.i64);
2864 sprintf(temp, "0o%o", exp.val.i64);
2867 sprintf(temp, "0o%o", exp.val.p);
2872 long v = (long)exp.val.f;
2873 sprintf(temp, "0o%o", v);
2878 long v = (long)exp.val.d;
2879 sprintf(temp, "0o%o", v);
2884 wh.octVal = CopyString(temp);
2887 // WHATS THIS HERE ?
2888 if(exp.type == constantExp && exp.constant)
2889 wh.constant = CopyString(exp.constant);
2895 case symbolErrorExp:
2896 snprintf(watchmsg, sizeof(watchmsg), $"Symbol \"%s\" not found", exp.identifier.string);
2898 case structMemberSymbolErrorExp:
2899 // todo get info as in next case (ExpClassMemberSymbolError)
2900 snprintf(watchmsg, sizeof(watchmsg), $"Error: Struct member not found for \"%s\"", wh.expression);
2902 case classMemberSymbolErrorExp:
2905 Expression memberExp = exp.member.exp;
2906 Identifier memberID = exp.member.member;
2907 Type type = memberExp.expType;
2910 _class = (memberID && memberID.classSym) ? memberID.classSym.registered : ((type.kind == classType && type._class) ? type._class.registered : null);
2913 char string[256] = "";
2915 PrintTypeNoConst(type, string, false, true);
2916 classSym = FindClass(string);
2917 _class = classSym ? classSym.registered : null;
2920 snprintf(watchmsg, sizeof(watchmsg), $"Member \"%s\" not found in class \"%s\"", memberID ? memberID.string : "", _class.name);
2922 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in unregistered class? (Should never get this message)", memberID ? memberID.string : "");
2925 snprintf(watchmsg, sizeof(watchmsg), "Member \"%s\" not found in no type? (Should never get this message)", memberID ? memberID.string : "");
2928 case memoryErrorExp:
2929 // Need to ensure when set to memoryErrorExp, constant is set
2930 snprintf(watchmsg, sizeof(watchmsg), $"Memory can't be read at %s", /*(exp.type == constantExp) ? */exp.constant /*: null*/);
2932 case dereferenceErrorExp:
2933 snprintf(watchmsg, sizeof(watchmsg), $"Dereference failure for \"%s\"", wh.expression);
2935 case unknownErrorExp:
2936 snprintf(watchmsg, sizeof(watchmsg), $"Unknown error for \"%s\"", wh.expression);
2938 case noDebuggerErrorExp:
2939 snprintf(watchmsg, sizeof(watchmsg), $"Debugger required for symbol evaluation in \"%s\"", wh.expression);
2941 case debugStateErrorExp:
2942 snprintf(watchmsg, sizeof(watchmsg), $"Incorrect debugger state for symbol evaluation in \"%s\"", wh.expression);
2945 snprintf(watchmsg, sizeof(watchmsg), $"Null type for \"%s\"", wh.expression);
2948 wh.value = CopyString(exp.string);
2951 // Temporary Code for displaying Strings
2952 if((exp.expType && ((exp.expType.kind == pointerType ||
2953 exp.expType.kind == arrayType) && exp.expType.type.kind == charType)) ||
2954 (wh.type && wh.type.kind == classType && wh.type._class &&
2955 wh.type._class.registered && wh.type._class.registered.type == normalClass &&
2956 !strcmp(wh.type._class.registered.name, "String")))
2959 if(exp.expType.kind != arrayType || exp.hasAddress)
2963 //char temp[MAX_F_STRING * 32];
2965 ExpressionType evalError = dummyExp;
2966 /*if(exp.expType.kind == arrayType)
2967 sprintf(temp, "(char*)0x%x", exp.address);
2969 sprintf(temp, "(char*)%s", exp.constant);*/
2971 //evaluation = Debugger::EvaluateExpression(temp, &evalError);
2972 // address = strtoul(exp.constant, null, 0);
2973 address = _strtoui64(exp.constant, null, 0);
2974 //_dpl(0, "0x", address);
2975 // snprintf(value, sizeof(value), "0x%08x ", address);
2977 if(address > 0xFFFFFFFFLL)
2978 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%016I64x " : "0x%016llx ", address);
2980 snprintf(value, sizeof(value), (GetRuntimePlatform() == win32) ? "0x%08I64x " : "0x%08llx ", address);
2981 value[sizeof(value)-1] = 0;
2984 strcat(value, $"Null string");
2987 String string = new char[4097];
2989 bool success = false;
2993 for(start = 0; !done && start + size <= 4096; start += size)
2998 // Try to read 256 bytes at a time, then half if that fails
2999 s = GdbReadMemory(address + start, size);
3003 memcpy(string + start, s, size);
3004 string[start + size] = 0;
3005 if(size == 1 || memchr(s, 0, size))
3019 int len = strlen(value);
3021 if(UTF8Validate(string))
3026 for(c = 0; (ch = string[c]); c++)
3029 value[len++] = '\0';
3034 ISO8859_1toUTF8(string, value + len, strlen(value) - len - 30);
3035 strcat(value, ") (ISO8859-1)");
3039 strcat(value, $"Empty string");
3042 strcat(value, $"Couldn't read memory");
3045 wh.value = CopyString(value);
3048 else if(wh.type && wh.type.kind == classType && wh.type._class &&
3049 wh.type._class.registered && wh.type._class.registered.type == enumClass)
3051 uint64 value = strtoul(exp.constant, null, 0);
3052 Class enumClass = eSystem_FindClass(GetPrivateModule(), wh.type._class.registered.name);
3053 EnumClassData enumeration = (EnumClassData)enumClass.data;
3055 for(item = enumeration.values.first; item; item = item.next)
3056 if((int)item.data == value)
3059 wh.value = CopyString(item.name);
3061 wh.value = CopyString($"Invalid Enum Value");
3064 else if(wh.type && (wh.type.kind == charType || (wh.type.kind == classType && wh.type._class &&
3065 wh.type._class.registered && !strcmp(wh.type._class.registered.fullName, "ecere::com::unichar"))) )
3072 if(exp.constant[0] == '\'')
3074 if((int)((byte *)exp.constant)[1] > 127)
3077 value = UTF8GetChar(exp.constant + 1, &nb);
3078 if(nb < 2) value = exp.constant[1];
3079 signedValue = value;
3083 signedValue = exp.constant[1];
3085 // Precomp Syntax error with boot strap here:
3086 byte b = (byte)(char)signedValue;
3087 value = (unichar) b;
3093 if(wh.type.kind == charType && wh.type.isSigned)
3095 signedValue = (int)(char)strtol(exp.constant, null, 0);
3097 // Precomp Syntax error with boot strap here:
3098 byte b = (byte)(char)signedValue;
3099 value = (unichar) b;
3104 value = (uint)strtoul(exp.constant, null, 0);
3105 signedValue = (int)value;
3109 UTF32toUTF8Len(&value, 1, charString, 5);
3111 snprintf(string, sizeof(string), "\'\\0' (0)");
3112 else if(value == '\t')
3113 snprintf(string, sizeof(string), "\'\\t' (%d)", value);
3114 else if(value == '\n')
3115 snprintf(string, sizeof(string), "\'\\n' (%d)", value);
3116 else if(value == '\r')
3117 snprintf(string, sizeof(string), "\'\\r' (%d)", value);
3118 else if(wh.type.kind == charType && wh.type.isSigned)
3119 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, signedValue);
3120 else if(value > 256 || wh.type.kind != charType)
3122 if(value > 0x10FFFF || !GetCharCategory(value))
3123 snprintf(string, sizeof(string), $"Invalid Unicode Keypoint (0x%08X)", value);
3125 snprintf(string, sizeof(string), "\'%s\' (U+%04X)", charString, value);
3128 snprintf(string, sizeof(string), "\'%s\' (%d)", charString, value);
3129 string[sizeof(string)-1] = 0;
3131 wh.value = CopyString(string);
3136 wh.value = CopyString(exp.constant);
3143 wh.value = PrintHexUInt64(exp.address);
3148 char tempString[256];
3149 if(exp.member.memberType == propertyMember)
3150 snprintf(watchmsg, sizeof(watchmsg), $"Missing property evaluation support for \"%s\"", wh.expression);
3152 snprintf(watchmsg, sizeof(watchmsg), $"Evaluation failed for \"%s\" of type \"%s\"", wh.expression,
3153 exp.type.OnGetString(tempString, null, null));
3159 snprintf(watchmsg, sizeof(watchmsg), $"Invalid expression: \"%s\"", wh.expression);
3160 if(exp) FreeExpression(exp);
3163 SetPrivateModule(backupPrivateModule);
3164 SetCurrentContext(backupContext);
3165 SetTopContext(backupContext);
3166 SetGlobalContext(backupContext);
3167 SetThisClass(backupThisClass);
3170 // wh.value = CopyString("No source file found for selected frame");
3172 watchmsg[sizeof(watchmsg)-1] = 0;
3174 wh.value = CopyString(watchmsg);
3176 ide.watchesView.UpdateWatch(wh);
3180 void EvaluateWatches()
3182 _dpl2(_dpct, dplchan::debuggerWatches, 0, "Debugger::EvaluateWatches()");
3183 WatchesLinkCodeEditor();
3184 if(state == stopped)
3186 for(wh : ide.workspace.watches)
3191 char * ::GdbEvaluateExpression(char * expression)
3193 _dpl2(_dpct, dplchan::debuggerWatches, 0, "Debugger::GdbEvaluateExpression(", expression, ")");
3196 GdbCommand(0, false, "-data-evaluate-expression \"%s\"", expression);
3198 ide.outputView.debugBox.Logf("Debugger Error: GdbEvaluateExpression\n");
3202 // to be removed... use GdbReadMemory that returns a byte array instead
3203 char * ::GdbReadMemoryString(uint64 address, int size, char format, int rows, int cols)
3205 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemoryString(", address, ")");
3210 _dpl(0, "GdbReadMemoryString called with size = 0!");
3212 // GdbCommand(0, false, "-data-read-memory 0x%08x %c, %d, %d, %d", address, format, size, rows, cols);
3213 if(GetRuntimePlatform() == win32)
3214 GdbCommand(0, false, "-data-read-memory 0x%016I64x %c, %d, %d, %d", address, format, size, rows, cols);
3216 GdbCommand(0, false, "-data-read-memory 0x%016llx %c, %d, %d, %d", address, format, size, rows, cols);
3218 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemoryString\n");
3222 byte * ::GdbReadMemory(uint64 address, int bytes)
3224 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbReadMemory(", address, ")");
3227 //GdbCommand(0, false, "-data-read-memory 0x%08x %c, 1, 1, %d", address, 'u', bytes);
3228 if(GetRuntimePlatform() == win32)
3229 GdbCommand(0, false, "-data-read-memory 0x%016I64x %c, 1, 1, %d", address, 'u', bytes);
3231 GdbCommand(0, false, "-data-read-memory 0x%016llx %c, 1, 1, %d", address, 'u', bytes);
3234 _dpl(0, "GdbReadMemory called with bytes = 0!");
3237 ide.outputView.debugBox.Logf("Debugger Error: GdbReadMemory\n");
3238 else if(eval.result && strcmp(eval.result, "N/A"))
3240 byte * result = new byte[bytes];
3241 byte * string = eval.result;
3245 result[c++] = (byte)strtol(string, &string, 10);
3261 bool BreakpointHit(GdbDataStop stopItem, Breakpoint bpInternal, Breakpoint bpUser)
3264 char * s1 = null; char * s2 = null;
3265 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::BreakpointHit(",
3266 "bpInternal(", bpInternal ? s1=bpInternal.CopyLocationString(false) : null, "), ",
3267 "bpUser(", bpUser ? s2=bpUser.CopyLocationString(false) : null, ")) -- ",
3268 "ignoreBreakpoints(", ignoreBreakpoints, "), ",
3269 "hitCursorBreakpoint(", bpUser && bpUser.type == runToCursor, ")");
3270 delete s1; delete s2;
3274 bool conditionMet = true;
3275 if(bpUser.condition)
3277 if(WatchesLinkCodeEditor())
3278 conditionMet = ResolveWatch(bpUser.condition);
3280 conditionMet = false;
3295 if(stopItem.frame.line && bpUser.line != stopItem.frame.line)
3297 // updating user breakpoint on hit location difference
3298 // todo, print something?
3299 bpUser.line = stopItem.frame.line;
3300 ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3301 ide.workspace.Save();
3304 ide.breakpointsView.UpdateBreakpoint(bpUser.row);
3309 if(bpInternal.type == internalModulesLoaded)
3311 if(userAction == stepOver)
3313 if((bpInternal.type == internalEntry && ((intBpMain && intBpMain.inserted) || (intBpWinMain && intBpWinMain.inserted))) ||
3314 (bpInternal.type == internalMain && intBpWinMain && intBpWinMain.inserted))
3317 if(!bpUser && !userAction.breaksOnInternalBreakpoint)
3319 if(userAction == stepOut)
3320 StepOut(ignoreBreakpoints);
3326 if(!bpUser && !bpInternal)
3332 void ValgrindTargetThreadExit()
3334 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ValgrindTargetThreadExit()");
3337 vgTargetHandle.Wait();
3338 delete vgTargetHandle;
3340 HandleExit(null, null);
3343 void GdbThreadExit()
3345 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::GdbThreadExit()");
3346 if(state != terminated)
3348 _ChangeState(terminated);
3349 targetProcessId = 0;
3350 ClearBreakDisplay();
3355 serialSemaphore.Release();
3360 ide.outputView.debugBox.Logf($"Debugger Fatal Error: GDB lost\n");
3361 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3363 HideDebuggerViews();
3365 //_ChangeState(terminated);
3369 void GdbThreadMain(char * output)
3373 Array<char *> outTokens { minAllocSize = 50 };
3374 Array<char *> subTokens { minAllocSize = 50 };
3375 DebugListItem item { };
3376 DebugListItem item2 { };
3377 bool setWaitingForPID = false;
3379 #if defined(GDB_DEBUG_CONSOLE) || defined(GDB_DEBUG_GUI)
3380 #ifdef GDB_DEBUG_CONSOLE
3381 // _dpl2(_dpct, dplchan::gdbOutput, 0, output);
3384 #ifdef GDB_DEBUG_OUTPUT
3386 int len = strlen(output);
3394 for(c = 0; c < len / 1024; c++)
3396 strncpy(tmp, start, 1024);
3397 ide.outputView.gdbBox.Logf("out: %s\n", tmp);
3400 ide.outputView.gdbBox.Logf("out: %s\n", start);
3404 ide.outputView.gdbBox.Logf("out: %s\n", output);
3408 #ifdef GDB_DEBUG_CONSOLE
3409 strcpy(lastGdbOutput, output);
3411 #ifdef GDB_DEBUG_GUI
3412 if(ide.gdbDialog) ide.gdbDialog.AddOutput(output);
3419 if(strstr(output, "No debugging symbols found") || strstr(output, "(no debugging symbols found)"))
3422 ide.outputView.debugBox.Logf($"Target doesn't contain debug information!\n");
3425 if(!entryPoint && (t = strstr(output, "Entry point:")))
3427 char * addr = t + strlen("Entry point:");
3429 if(*t++ == ' ' && *t++ == '0' && *t == 'x')
3432 while(isxdigit(*++t));
3434 for(bp : sysBPs; bp.type == internalEntry)
3437 bp.enabled = entryPoint = true;
3445 if(TokenizeList(output, ',', outTokens) && !strcmp(outTokens[0], "^done"))
3447 //if(outTokens.count == 1)
3452 _ChangeState(loaded);
3453 targetProcessId = 0;
3454 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3456 if(!strcmp(item.name, "reason"))
3458 char * reason = item.value;
3459 StripQuotes(reason, reason);
3460 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3463 if(outTokens.count > 2 && TokenizeListItem(outTokens[2], item2))
3465 StripQuotes(item2.value, item2.value);
3466 if(!strcmp(item2.name, "exit-code"))
3467 exitCode = item2.value;
3473 HandleExit(reason, exitCode);
3477 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "kill reply (", item.name, "=", item.value, ") is unheard of");
3480 HandleExit(null, null);
3483 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3485 if(!strcmp(item.name, "bkpt"))
3487 sentBreakInsert = false;
3493 bpItem = ParseBreakpoint(item.value, outTokens);
3494 //breakType = bpValidation;
3496 else if(!strcmp(item.name, "depth"))
3498 StripQuotes(item.value, item.value);
3499 frameCount = atoi(item.value);
3501 stackFrames.Free(Frame::Free);
3503 else if(!strcmp(item.name, "stack"))
3506 if(stackFrames.count)
3507 ide.callStackView.Logf("...\n");
3510 item.value = StripBrackets(item.value);
3511 TokenizeList(item.value, ',', subTokens);
3512 for(i = 0; i < subTokens.count; i++)
3514 if(TokenizeListItem(subTokens[i], item))
3516 if(!strcmp(item.name, "frame"))
3519 stackFrames.Add(frame);
3520 item.value = StripCurlies(item.value);
3521 ParseFrame(frame, item.value);
3522 if(frame.file && frame.from)
3523 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "unexpected frame file and from members present");
3527 if(activeFrameLevel == -1)
3529 if(ide.projectView.IsModuleInProject(frame.file));
3531 if(frame.level != 0)
3533 //stopItem.frame = frame;
3534 breakType = selectFrame;
3537 activeFrame = frame;
3538 activeFrameLevel = frame.level;
3541 ide.callStackView.Logf("%3d ", frame.level);
3542 if(!strncmp(frame.func, "__ecereMethod_", strlen("__ecereMethod_")))
3543 ide.callStackView.Logf($"%s Method, %s:%d\n", &frame.func[strlen("__ecereMethod_")], (s = CopySystemPath(frame.file)), frame.line);
3544 else if(!strncmp(frame.func, "__ecereProp_", strlen("__ecereProp_")))
3545 ide.callStackView.Logf($"%s Property, %s:%d\n", &frame.func[strlen("__ecereProp_")], (s = CopySystemPath(frame.file)), frame.line);
3546 else if(!strncmp(frame.func, "__ecereConstructor_", strlen("__ecereConstructor_")))
3547 ide.callStackView.Logf($"%s Constructor, %s:%d\n", &frame.func[strlen("__ecereConstructor_")], (s = CopySystemPath(frame.file)), frame.line);
3548 else if(!strncmp(frame.func, "__ecereDestructor_", strlen("__ecereDestructor_")))
3549 ide.callStackView.Logf($"%s Destructor, %s:%d\n", &frame.func[strlen("__ecereDestructor_")], (s = CopySystemPath(frame.file)), frame.line);
3551 ide.callStackView.Logf($"%s Function, %s:%d\n", frame.func, (s = CopySystemPath(frame.file)), frame.line);
3556 ide.callStackView.Logf("%3d ", frame.level);
3561 ide.callStackView.Logf($"inside %s, %s\n", frame.func, (s = CopySystemPath(frame.from)));
3565 ide.callStackView.Logf("%s\n", frame.func);
3567 ide.callStackView.Logf($"unknown source\n");
3571 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "stack content (", item.name, "=", item.value, ") is unheard of");
3574 if(activeFrameLevel == -1)
3576 activeFrameLevel = 0;
3577 activeFrame = stackFrames.first;
3579 ide.callStackView.Home();
3581 subTokens.RemoveAll();
3583 /*else if(!strcmp(item.name, "frame"))
3586 item.value = StripCurlies(item.value);
3587 ParseFrame(&frame, item.value);
3589 else if(!strcmp(item.name, "thread-ids"))
3591 ide.threadsView.Clear();
3592 item.value = StripCurlies(item.value);
3593 TokenizeList(item.value, ',', subTokens);
3594 for(i = subTokens.count - 1; ; i--)
3596 if(TokenizeListItem(subTokens[i], item))
3598 if(!strcmp(item.name, "thread-id"))
3601 StripQuotes(item.value, item.value);
3602 value = atoi(item.value);
3603 ide.threadsView.Logf("%3d \n", value);
3606 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "threads content (", item.name, "=", item.value, ") is unheard of");
3611 ide.threadsView.Home();
3613 subTokens.RemoveAll();
3614 //if(!strcmp(outTokens[2], "number-of-threads"))
3616 else if(!strcmp(item.name, "new-thread-id"))
3618 StripQuotes(item.value, item.value);
3619 activeThread = atoi(item.value);
3621 else if(!strcmp(item.name, "value"))
3623 StripQuotes(item.value, item.value);
3624 eval.result = CopyString(item.value);
3625 eval.active = false;
3627 else if(!strcmp(item.name, "addr"))
3629 for(i = 2; i < outTokens.count; i++)
3631 if(TokenizeListItem(outTokens[i], item))
3633 if(!strcmp(item.name, "total-bytes"))
3635 StripQuotes(item.value, item.value);
3636 eval.bytes = atoi(item.value);
3638 else if(!strcmp(item.name, "next-row"))
3640 StripQuotes(item.value, item.value);
3641 eval.nextBlockAddress = _strtoui64(item.value, null, 0);
3643 else if(!strcmp(item.name, "memory"))
3647 //StripQuotes(item.value, item.value);
3648 item.value = StripBrackets(item.value);
3649 // this should be treated as a list...
3650 item.value = StripCurlies(item.value);
3651 TokenizeList(item.value, ',', subTokens);
3652 for(j = 0; j < subTokens.count; j++)
3654 if(TokenizeListItem(subTokens[j], item))
3656 if(!strcmp(item.name, "data"))
3658 item.value = StripBrackets(item.value);
3659 StripQuotes2(item.value, item.value);
3660 eval.result = CopyString(item.value);
3661 eval.active = false;
3665 subTokens.RemoveAll();
3670 else if(!strcmp(item.name, "source-path") || !strcmp(item.name, "BreakpointTable"))
3671 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "command reply (", item.name, "=", item.value, ") is ignored");
3673 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "command reply (", item.name, "=", item.value, ") is unheard of");
3676 else if(!strcmp(outTokens[0], "^running"))
3678 waitingForPID = true;
3679 setWaitingForPID = true;
3680 ClearBreakDisplay();
3682 else if(!strcmp(outTokens[0], "^exit"))
3684 _ChangeState(terminated);
3685 // ide.outputView.debugBox.Logf("Exit\n");
3686 // ide.Update(null);
3688 serialSemaphore.Release();
3690 else if(!strcmp(outTokens[0], "^error"))
3694 sentBreakInsert = false;
3695 breakpointError = true;
3698 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
3700 if(!strcmp(item.name, "msg"))
3702 StripQuotes(item.value, item.value);
3705 eval.active = false;
3707 if(strstr(item.value, "No symbol") && strstr(item.value, "in current context"))
3708 eval.error = symbolNotFound;
3709 else if(strstr(item.value, "Cannot access memory at address"))
3710 eval.error = memoryCantBeRead;
3712 eval.error = unknown;
3714 else if(!strcmp(item.value, "Previous frame inner to this frame (corrupt stack?)"))
3717 else if(!strncmp(item.value, "Cannot access memory at address", 31))
3720 else if(!strcmp(item.value, "Cannot find bounds of current function"))
3722 _ChangeState(stopped);
3723 gdbHandle.Printf("-exec-continue\n");
3725 else if(!strcmp(item.value, "ptrace: No such process."))
3727 _ChangeState(loaded);
3728 targetProcessId = 0;
3730 else if(!strcmp(item.value, "Function \\\"WinMain\\\" not defined."))
3733 else if(!strcmp(item.value, "You can't do that without a process to debug."))
3735 _ChangeState(loaded);
3736 targetProcessId = 0;
3738 else if(strstr(item.value, "No such file or directory."))
3740 _ChangeState(loaded);
3741 targetProcessId = 0;
3743 else if(strstr(item.value, "During startup program exited with code "))
3745 _ChangeState(loaded);
3746 targetProcessId = 0;
3751 if(strlen(item.value) < MAX_F_STRING)
3754 ide.outputView.debugBox.Logf("GDB: %s\n", (s = CopyUnescapedString(item.value)));
3758 ide.outputView.debugBox.Logf("GDB: %s\n", item.value);
3764 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "error content (", item.name, "=", item.value, ") is unheard of");
3767 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "result-record: ", outTokens[0]);
3768 outTokens.RemoveAll();
3771 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "status-async-output: ", outTokens[0]);
3774 if(TokenizeList(output, ',', outTokens))
3776 if(!strcmp(outTokens[0], "=library-loaded"))
3777 FGODetectLoadedLibraryForAddedProjectIssues(outTokens, false);
3778 else if(!strcmp(outTokens[0], "=shlibs-added"))
3779 FGODetectLoadedLibraryForAddedProjectIssues(outTokens, true);
3780 else if(!strcmp(outTokens[0], "=thread-group-created") || !strcmp(outTokens[0], "=thread-group-added") ||
3781 !strcmp(outTokens[0], "=thread-group-started") || !strcmp(outTokens[0], "=thread-group-exited") ||
3782 !strcmp(outTokens[0], "=thread-created") || !strcmp(outTokens[0], "=thread-exited") ||
3783 !strcmp(outTokens[0], "=cmd-param-changed") || !strcmp(outTokens[0], "=library-unloaded") ||
3784 !strcmp(outTokens[0], "=breakpoint-modified"))
3785 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, outTokens[0], outTokens.count>1 ? outTokens[1] : "",
3786 outTokens.count>2 ? outTokens[2] : "", outTokens.count>3 ? outTokens[3] : "",
3787 outTokens.count>4 ? outTokens[4] : "", outTokens.count>5 ? outTokens[5] : "",
3788 outTokens.count>6 ? outTokens[6] : "", outTokens.count>7 ? outTokens[7] : "",
3789 outTokens.count>8 ? outTokens[8] : "", outTokens.count>9 ? outTokens[9] : "");
3791 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "notify-async-output: ", outTokens[0]);
3793 outTokens.RemoveAll();
3797 if(TokenizeList(output, ',', outTokens))
3799 if(!strcmp(outTokens[0],"*running"))
3801 waitingForPID = true;
3802 setWaitingForPID = true;
3804 else if(!strcmp(outTokens[0], "*stopped"))
3807 _ChangeState(stopped);
3809 for(tk = 1; tk < outTokens.count; tk++)
3811 if(TokenizeListItem(outTokens[tk], item))
3813 if(!strcmp(item.name, "reason"))
3815 char * reason = item.value;
3816 StripQuotes(reason, reason);
3817 if(!strcmp(reason, "exited-normally") || !strcmp(reason, "exited") || !strcmp(reason, "exited-signalled"))
3820 if(outTokens.count > tk+1 && TokenizeListItem(outTokens[tk+1], item2))
3823 StripQuotes(item2.value, item2.value);
3824 if(!strcmp(item2.name, "exit-code"))
3825 exitCode = item2.value;
3831 HandleExit(reason, exitCode);
3834 else if(!strcmp(reason, "breakpoint-hit") ||
3835 !strcmp(reason, "function-finished") ||
3836 !strcmp(reason, "end-stepping-range") ||
3837 !strcmp(reason, "location-reached") ||
3838 !strcmp(reason, "signal-received"))
3842 if(stopItem) _dpl(0, "problem");
3844 stopItem = GdbDataStop { };
3845 stopItem.reason = r == 'b' ? breakpointHit : r == 'f' ? functionFinished : r == 'e' ? endSteppingRange : r == 'l' ? locationReached : signalReceived;
3847 for(i = tk+1; i < outTokens.count; i++)
3849 TokenizeListItem(outTokens[i], item);
3850 StripQuotes(item.value, item.value);
3851 if(!strcmp(item.name, "thread-id"))
3852 stopItem.threadid = atoi(item.value);
3853 else if(!strcmp(item.name, "frame"))
3855 item.value = StripCurlies(item.value);
3856 ParseFrame(stopItem.frame, item.value);
3858 else if(stopItem.reason == breakpointHit && !strcmp(item.name, "bkptno"))
3859 stopItem.bkptno = atoi(item.value);
3860 else if(stopItem.reason == functionFinished && !strcmp(item.name, "gdb-result-var"))
3861 stopItem.gdbResultVar = CopyString(item.value);
3862 else if(stopItem.reason == functionFinished && !strcmp(item.name, "return-value"))
3863 stopItem.returnValue = CopyString(item.value);
3864 else if(stopItem.reason == signalReceived && !strcmp(item.name, "signal-name"))
3865 stopItem.name = CopyString(item.value);
3866 else if(stopItem.reason == signalReceived && !strcmp(item.name, "signal-meaning"))
3867 stopItem.meaning = CopyString(item.value);
3868 else if(!strcmp(item.name, "stopped-threads"))
3869 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, reason, ": Advanced thread debugging not handled");
3870 else if(!strcmp(item.name, "core"))
3871 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, reason, ": Information (core) not used");
3872 else if(!strcmp(item.name, "disp"))
3873 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, reason, ": (", item.name, "=", item.value, ")");
3875 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown ", reason, " item name (", item.name, "=", item.value, ")");
3878 if(stopItem.reason == signalReceived && !strcmp(stopItem.name, "SIGTRAP"))
3894 event = r == 'b' ? hit : r == 'f' ? functionEnd : r == 'e' ? stepEnd : r == 'l' ? locationReached : signal;
3898 else if(!strcmp(reason, "watchpoint-trigger"))
3899 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint trigger not handled");
3900 else if(!strcmp(reason, "read-watchpoint-trigger"))
3901 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason read watchpoint trigger not handled");
3902 else if(!strcmp(reason, "access-watchpoint-trigger"))
3903 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason access watchpoint trigger not handled");
3904 else if(!strcmp(reason, "watchpoint-scope"))
3905 _dpl2(_dpct, dplchan::gdbProtoIgnored, 0, "Reason watchpoint scope not handled");
3907 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown reason: ", reason);
3915 if(usingValgrind && event == none && !stopItem)
3916 event = valgrindStartPause;
3921 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, "Unknown exec-async-output: ", outTokens[0]);
3922 outTokens.RemoveAll();
3925 if(!strcmpi(output, "(gdb) "))
3929 Time startTime = GetTime();
3930 char exeFile[MAX_LOCATION];
3931 int oldProcessID = targetProcessId;
3932 GetLastDirectory(targetFile, exeFile);
3934 while(!targetProcessId/*true*/)
3936 targetProcessId = Process_GetChildExeProcessId(gdbProcessId, exeFile);
3937 if(targetProcessId) break;
3938 // Can't break on Peek(), GDB is giving us =library and other info before the process is listed in /proc
3939 // if(gdbHandle.Peek()) break;
3941 if(gdbHandle.Peek() && GetTime() - startTime > 2.5) // Give the process 2.5 seconds to show up in /proc
3946 _ChangeState(running);
3947 else if(!oldProcessID)
3949 ide.outputView.debugBox.Logf($"Debugger Error: No target process ID\n");
3950 // TO VERIFY: The rest of this block has not been thoroughly tested in this particular location
3951 gdbHandle.Printf("-gdb-exit\n");
3953 _ChangeState(terminated); //loaded;
3958 for(bp : ide.workspace.breakpoints)
3959 bp.inserted = false;
3962 bp.inserted = false;
3964 bpRunToCursor.inserted = false;
3966 ide.outputView.debugBox.Logf($"Debugging stopped\n");
3967 ClearBreakDisplay();
3969 #if defined(__unix__)
3970 if(!usingValgrind && FileExists(progFifoPath)) //fileCreated)
3972 progThread.terminate = true;
3975 fifoFile.CloseInput();
3982 DeleteFile(progFifoPath);
3983 progFifoPath[0] = '\0';
3990 serialSemaphore.Release();
3993 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown prompt", output);
3997 if(!strncmp(output, "&\"warning:", 10))
4000 content = strstr(output, "\"");
4001 StripQuotes(content, content);
4002 content = strstr(content, ":");
4008 ide.outputView.debugBox.LogRaw((s = CopyUnescapedString(content)));
4015 _dpl2(_dpct, dplchan::gdbProtoUnknown, 0, $"Unknown output: ", output);
4017 if(!setWaitingForPID)
4018 waitingForPID = false;
4019 setWaitingForPID = false;
4027 // From GDB Output functions
4028 void FGODetectLoadedLibraryForAddedProjectIssues(Array<char *> outTokens, bool shlibs)
4030 char path[MAX_LOCATION] = "";
4031 char file[MAX_FILENAME] = "";
4032 bool symbolsLoaded = false;
4033 DebugListItem item { };
4034 //_dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGODetectLoadedLibraryForAddedProjectIssues()");
4035 for(token : outTokens)
4037 if(TokenizeListItem(token, item))
4039 if(!strcmp(item.name, "target-name"))
4041 StripQuotes(item.value, path);
4042 MakeSystemPath(path);
4043 GetLastDirectory(path, file);
4045 else if(!strcmp(item.name, "symbols-loaded"))
4047 symbolsLoaded = (atoi(item.value) == 1);
4049 else if(!strcmp(item.name, "shlib-info"))
4051 DebugListItem subItem { };
4052 Array<char *> tokens { minAllocSize = 50 };
4053 item.value = StripBrackets(item.value);
4054 TokenizeList(item.value, ',', tokens);
4057 if(TokenizeListItem(t, subItem))
4059 if(!strcmp(subItem.name, "path"))
4061 StripQuotes(subItem.value, path);
4062 MakeSystemPath(path);
4063 GetLastDirectory(path, file);
4064 symbolsLoaded = true;
4075 if(path[0] && file[0])
4077 for(prj : ide.workspace.projects; prj != ide.workspace.projects.firstIterator.data)
4081 char prjTargetPath[MAX_LOCATION];
4082 char prjTargetFile[MAX_FILENAME];
4083 DirExpression targetDirExp = prj.GetTargetDir(currentCompiler, prj.config, bitDepth);
4084 strcpy(prjTargetPath, prj.topNode.path);
4085 PathCat(prjTargetPath, targetDirExp.dir);
4086 delete targetDirExp;
4087 prjTargetFile[0] = '\0';
4088 prj.CatTargetFileName(prjTargetFile, currentCompiler, prj.config);
4089 PathCat(prjTargetPath, prjTargetFile);
4090 MakeSystemPath(prjTargetPath);
4092 match = !fstrcmp(prjTargetFile, file);
4093 if(!match && (dot = strstr(prjTargetFile, ".so.")))
4095 char * dot3 = strstr(dot+4, ".");
4099 match = !fstrcmp(prjTargetFile, file);
4104 match = !fstrcmp(prjTargetFile, file);
4109 // TODO: nice visual feedback to better warn user. use some ide notification system or other means.
4110 /* -- 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)
4112 ide.outputView.debugBox.Logf($"Attention! No symbols for loaded library %s matched to the %s added project.\n", path, prj.topNode.name);
4114 match = !fstrcmp(prjTargetPath, path);
4115 if(!match && (dot = strstr(prjTargetPath, ".so.")))
4117 char * dot3 = strstr(dot+4, ".");
4121 match = !fstrcmp(prjTargetPath, path);
4126 match = !fstrcmp(prjTargetPath, path);
4130 projectsLibraryLoaded[prj.name] = true;
4132 ide.outputView.debugBox.Logf($"Loaded library %s doesn't match the %s target of the %s added project.\n", path, prjTargetPath, prj.topNode.name);
4139 void FGOBreakpointModified(Array<char *> outTokens)
4141 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::FGOBreakpointModified() -- TODO only if needed: support breakpoint modified");
4143 DebugListItem item { };
4144 if(outTokens.count > 1 && TokenizeListItem(outTokens[1], item))
4146 if(!strcmp(item.name, "bkpt"))
4148 GdbDataBreakpoint modBp = ParseBreakpoint(item.value, outTokens);
4156 ExpressionType ::DebugEvalExpTypeError(char * result)
4158 _dpl2(_dpct, dplchan::debuggerWatches, 0, "Debugger::DebugEvalExpTypeError()");
4163 case symbolNotFound:
4164 return symbolErrorExp;
4165 case memoryCantBeRead:
4166 return memoryErrorExp;
4168 return unknownErrorExp;
4171 char * ::EvaluateExpression(char * expression, ExpressionType * error)
4174 _dpl2(_dpct, dplchan::debuggerWatches, 0, "Debugger::EvaluateExpression(", expression, ")");
4175 if(ide.projectView && ide.debugger.state == stopped)
4177 result = GdbEvaluateExpression(expression);
4178 *error = DebugEvalExpTypeError(result);
4183 *error = noDebuggerErrorExp;
4188 char * ::ReadMemory(uint64 address, int size, char format, ExpressionType * error)
4191 char * result = GdbReadMemoryString(address, size, format, 1, 1);
4192 _dpl2(_dpct, dplchan::debuggerCall, 0, "Debugger::ReadMemory(", address, ")");
4193 if(!result || !strcmp(result, "N/A"))
4194 *error = memoryErrorExp;
4196 *error = DebugEvalExpTypeError(result);
4201 class ValgrindLogThread : Thread
4207 static char output[4096];
4208 bool lastLineEmpty = true;
4209 Array<char> dynamicBuffer { minAllocSize = 4096 };
4210 File oldValgrindHandle = vgLogFile;
4211 incref oldValgrindHandle;
4214 while(debugger.state != terminated && vgLogFile && vgLogFile.input)
4219 result = vgLogFile.Read(output, 1, sizeof(output));
4221 if(debugger.state == terminated || !vgLogFile) // || vgLogFile.Eof()
4228 for(c = 0; c<result; c++)
4230 if(output[c] == '\n')
4232 int pos = dynamicBuffer.size;
4233 dynamicBuffer.size += c - start;
4234 memcpy(&dynamicBuffer[pos], output + start, c - start);
4235 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4236 dynamicBuffer.size++;
4237 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4239 // printf("%s\n", dynamicBuffer.array);
4241 if(strstr(&dynamicBuffer[0], "vgdb me ..."))
4242 debugger.serialSemaphore.Release();
4244 char * s = strstr(&dynamicBuffer[0], "==");
4246 s = strstr(s+2, "== ");
4250 if(s[0] == '\0' && !lastLineEmpty)
4253 lastLineEmpty = true;
4254 dynamicBuffer[0] = '\0';
4263 if(strstr(s, "vgdb me ..."))
4265 if(strstr(s, "(action on error) vgdb me ..."))
4266 ide.outputView.debugBox.Logf($"...breaked on Valgrind error (F5 to resume)\n");
4273 if(strstr(s, "TO DEBUG THIS PROCESS USING GDB: start GDB like this"))
4279 if(strstr(s, "and then give GDB the following command"))
4285 if(strstr(s, "/path/to/gdb") || strstr(s, "target remote | /usr/lib/valgrind/../../bin/vgdb --pid="))
4291 if(strstr(s, "--pid is optional if only one valgrind process is running"))
4297 if((s = strstr(s, "; rerun with -h for copyright info")))
4309 if(lastLineEmpty && t[0] != '\0')
4310 lastLineEmpty = false;
4313 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4315 dynamicBuffer.size = 0;
4321 int pos = dynamicBuffer.size;
4322 dynamicBuffer.size += c - start;
4323 memcpy(&dynamicBuffer[pos], output + start, c - start);
4326 else if(debugger.state == stopped)
4329 printf("Got end of file from GDB!\n");
4336 delete dynamicBuffer;
4337 _dpl2(_dpct, dplchan::debuggerCall, 0, "ValgrindLogThreadExit");
4338 //if(oldValgrindHandle == vgLogFile)
4339 debugger.GdbThreadExit/*ValgrindLogThreadExit*/();
4340 delete oldValgrindHandle;
4346 class ValgrindTargetThread : Thread
4352 static char output[4096];
4353 Array<char> dynamicBuffer { minAllocSize = 4096 };
4354 DualPipe oldValgrindHandle = vgTargetHandle;
4355 incref oldValgrindHandle;
4358 while(debugger.state != terminated && vgTargetHandle && !vgTargetHandle.Eof())
4362 result = vgTargetHandle.Read(output, 1, sizeof(output));
4364 if(debugger.state == terminated || !vgTargetHandle || vgTargetHandle.Eof())
4371 for(c = 0; c<result; c++)
4373 if(output[c] == '\n')
4375 int pos = dynamicBuffer.size;
4376 dynamicBuffer.size += c - start;
4377 memcpy(&dynamicBuffer[pos], output + start, c - start);
4378 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4379 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4380 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4381 dynamicBuffer.size++;
4382 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4384 // printf("%s\n", dynamicBuffer.array);
4386 ide.outputView.debugBox.Logf("%s\n", &dynamicBuffer[0]);
4388 dynamicBuffer.size = 0;
4394 int pos = dynamicBuffer.size;
4395 dynamicBuffer.size += c - start;
4396 memcpy(&dynamicBuffer[pos], output + start, c - start);
4402 printf("Got end of file from GDB!\n");
4406 delete dynamicBuffer;
4407 //if(oldValgrindHandle == vgTargetHandle)
4408 debugger.ValgrindTargetThreadExit();
4409 delete oldValgrindHandle;
4415 class GdbThread : Thread
4421 static char output[4096];
4422 Array<char> dynamicBuffer { minAllocSize = 4096 };
4423 DualPipe oldGdbHandle = gdbHandle;
4424 incref oldGdbHandle;
4427 while(debugger.state != terminated && gdbHandle && !gdbHandle.Eof())
4431 result = gdbHandle.Read(output, 1, sizeof(output));
4433 if(debugger.state == terminated || !gdbHandle || gdbHandle.Eof())
4440 for(c = 0; c<result; c++)
4442 if(output[c] == '\n')
4444 int pos = dynamicBuffer.size;
4445 dynamicBuffer.size += c - start;
4446 memcpy(&dynamicBuffer[pos], output + start, c - start);
4447 if(dynamicBuffer.count && dynamicBuffer[dynamicBuffer.count - 1] != '\r')
4448 // COMMENTED OUT DUE TO ISSUE #135, FIXED
4449 //if(dynamicBuffer.array[dynamicBuffer.count - 1] != '\r')
4450 dynamicBuffer.size++;
4451 dynamicBuffer[dynamicBuffer.count - 1] = '\0';
4453 // _dpl(0, dynamicBuffer.array);
4455 debugger.GdbThreadMain(&dynamicBuffer[0]);
4456 dynamicBuffer.size = 0;
4462 int pos = dynamicBuffer.size;
4463 dynamicBuffer.size += c - start;
4464 memcpy(&dynamicBuffer[pos], output + start, c - start);
4470 _dpl(0, "Got end of file from GDB!");
4474 delete dynamicBuffer;
4475 //if(oldGdbHandle == gdbHandle)
4476 debugger.GdbThreadExit();
4477 delete oldGdbHandle;
4483 static define createFIFOMsg = $"err: Unable to create FIFO %s\n";
4484 static define openFIFOMsg = $"err: Unable to open FIFO %s for read\n";
4486 #if defined(__unix__)
4491 #include <sys/types.h>
4496 class ProgramThread : Thread
4502 bool fileCreated = false;
4504 static char output[1000];
4507 /*if(!mkfifo(progFifoPath, mask))
4514 ide.outputView.debugBox.Logf($"err: Unable to create FIFO %s\n", progFifoPath);
4518 if(FileExists(progFifoPath)) //fileCreated)
4520 fifoFile = FileOpen(progFifoPath, read);
4524 ide.outputView.debugBox.Logf(openFIFOMsg, progFifoPath);
4529 fd = fileno((FILE *)fifoFile.input);
4530 //fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
4534 while(!terminate && fifoFile && !fifoFile.Eof())
4537 struct timeval time;
4545 selectResult = select(fd + 1, &rs, null, null, &time);
4546 if(FD_ISSET(fd, &rs))
4548 int result = (int)read(fd, output, sizeof(output)-1);
4549 if(!result || (result < 0 && errno != EAGAIN))
4553 output[result] = '\0';
4554 if(strcmp(output,"&\"warning: GDB: Failed to set controlling terminal: Invalid argument\\n\"\n"))
4557 ide.outputView.debugBox.Log(output);
4566 //fifoFile.CloseInput();
4569 ide.outputView.debugBox.Log("\n");
4573 if(FileExists(progFifoPath)) //fileCreated)
4575 DeleteFile(progFifoPath);
4576 progFifoPath[0] = '\0';
4584 class Argument : struct
4586 Argument prev, next;
4588 property char * name { set { delete name; if(value) name = CopyString(value); } }
4590 property char * val { set { delete val; if(value) val = CopyString(value); } }
4604 class Frame : struct
4609 property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4611 property char * func { set { delete func; if(value) func = CopyString(value); } }
4615 property char * from { set { delete from; if(value) from = CopyUnescapedUnixPath(value); } }
4617 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4618 char * absoluteFile;
4619 property char * absoluteFile { set { delete absoluteFile; if(value) absoluteFile = CopyUnescapedUnixPath(value); } }
4628 delete absoluteFile;
4629 args.Free(Argument::Free);
4638 class GdbDataStop : struct
4640 DebuggerReason reason;
4655 char * gdbResultVar;
4665 if(reason == signalReceived)
4670 else if(reason == functionFinished)
4672 delete gdbResultVar;
4676 if(frame) frame.Free();
4685 class GdbDataBreakpoint : struct
4689 property char * number { set { delete number; if(value) number = CopyString(value); } }
4691 property char * type { set { delete type; if(value) type = CopyString(value); } }
4693 property char * disp { set { delete disp; if(value) disp = CopyString(value); } }
4696 property char * addr { set { delete addr; if(value) addr = CopyString(value); } }
4698 property char * func { set { delete func; if(value) func = CopyString(value); } }
4700 property char * file { set { delete file; if(value) file = CopyUnescapedUnixPath(value); } }
4702 property char * fullname { set { delete fullname; if(value) fullname = CopyUnescapedUnixPath(value); } }
4705 property char * at { set { delete at; if(value) at = CopyString(value); } }
4708 Array<GdbDataBreakpoint> multipleBPs;
4713 PrintLn("{", "#", number, " T", type, " D", disp, " E", enabled, " H", times, " (", func, ") (", file, ":", line, ") (", fullname, ") (", addr, ") (", at, ")", "}");
4724 if(multipleBPs) multipleBPs.Free();
4730 ~GdbDataBreakpoint()
4736 class Breakpoint : struct
4741 property char * function { set { delete function; if(value) function = CopyString(value); } }
4742 char * relativeFilePath;
4743 property char * relativeFilePath { set { delete relativeFilePath; if(value) relativeFilePath = CopyString(value); } }
4744 char * absoluteFilePath;
4745 property char * absoluteFilePath { set { delete absoluteFilePath; if(value) absoluteFilePath = CopyString(value); } }
4747 property char * location { set { delete location; if(value) location = CopyString(value); } }
4756 BreakpointType type;
4758 GdbDataBreakpoint bp;
4761 property char * address { set { delete address; if(value) address = CopyString(value); } }
4763 void ParseLocation()
4765 char * prjName = null;
4766 char * filePath = null;
4769 char fullPath[MAX_LOCATION];
4770 if(location[0] == '(' && location[1] && (file = strchr(location+2, ')')) && file[1])
4772 prjName = new char[file-location];
4773 strncpy(prjName, location+1, file-location-1);
4774 prjName[file-location-1] = '\0';
4779 if((line = strchr(file+1, ':')))
4781 filePath = new char[strlen(file)+1];
4782 strncpy(filePath, file, line-file);
4783 filePath[line-file] = '\0';
4787 filePath = CopyString(file);
4788 property::relativeFilePath = filePath;
4791 for(prj : ide.workspace.projects)
4793 if(!strcmp(prjName, prj.name))
4795 if(ProjectGetAbsoluteFromRelativePath(prj, filePath, fullPath))
4797 property::absoluteFilePath = fullPath;
4804 this.line = atoi(line);
4808 Project prj = ide.project;
4809 if(ProjectGetAbsoluteFromRelativePath(prj, filePath, fullPath))
4811 property::absoluteFilePath = fullPath;
4815 if(!absoluteFilePath)
4816 property::absoluteFilePath = "";
4821 char * CopyLocationString(bool removePath)
4824 char * file = relativeFilePath ? relativeFilePath : absoluteFilePath;
4825 bool removingPath = removePath && file;
4828 char * fileName = new char[MAX_FILENAME];
4829 GetLastDirectory(file, fileName);
4835 location = PrintString(file, ":", function);
4837 location = CopyString(function);
4840 location = PrintString(file, ":", line);
4846 char * CopyUserLocationString()
4849 char * loc = CopyLocationString(false);
4851 if(absoluteFilePath)
4853 for(p : ide.workspace.projects; p != ide.workspace.projects.firstIterator.data)
4855 if(p.topNode.FindByFullPath(absoluteFilePath, false))
4864 location = PrintString("(", prj.name, ")", loc);
4874 if(relativeFilePath && relativeFilePath[0])
4876 char * location = CopyUserLocationString();
4877 f.Printf(" * %d,%d,%d,%d,%s\n", enabled ? 1 : 0, ignore, level, line, location);
4880 f.Printf(" ~ %s\n", condition.expression);
4888 delete relativeFilePath;
4889 delete absoluteFilePath;
4909 class Watch : struct
4920 f.Printf(" ~ %s\n", expression);
4944 class DebugListItem : struct
4950 struct DebugEvaluationData
4955 uint64 nextBlockAddress;
4957 DebuggerEvaluationError error;
4960 class CodeLocation : struct
4963 char * absoluteFile;
4966 CodeLocation ::ParseCodeLocation(char * location)
4970 char * colon = null;
4972 char loc[MAX_LOCATION];
4973 strcpy(loc, location);
4974 for(temp = loc; temp = strstr(temp, ":"); temp++)
4982 int line = atoi(colon);
4985 CodeLocation codloc { line = line };
4986 codloc.file = CopyString(loc);
4987 codloc.absoluteFile = ide.workspace.GetAbsolutePathFromRelative(loc);
4999 delete absoluteFile;
5008 void GDBFallBack(Expression exp, String expString)
5011 ExpressionType evalError = dummyExp;
5012 result = Debugger::EvaluateExpression(expString, &evalError);
5015 exp.constant = result;
5016 exp.type = constantExp;
5020 static Project WorkspaceGetFileOwner(char * absolutePath)
5022 Project owner = null;
5023 for(prj : ide.workspace.projects)
5025 if(prj.topNode.FindByFullPath(absolutePath, false))
5032 WorkspaceGetObjectFileNode(absolutePath, &owner);
5036 static ProjectNode WorkspaceGetObjectFileNode(char * filePath, Project * project)
5038 ProjectNode node = null;
5039 char ext[MAX_EXTENSION];
5040 GetExtension(filePath, ext);
5043 IntermediateFileType type = IntermediateFileType::FromExtension(ext);
5046 char fileName[MAX_FILENAME];
5047 GetLastDirectory(filePath, fileName);
5050 DotMain dotMain = DotMain::FromFileName(fileName);
5051 for(prj : ide.workspace.projects)
5053 if((node = prj.FindNodeByObjectFileName(fileName, type, dotMain, null)))
5066 static ProjectNode ProjectGetObjectFileNode(Project project, char * filePath)
5068 ProjectNode node = null;
5069 char ext[MAX_EXTENSION];
5070 GetExtension(filePath, ext);
5073 IntermediateFileType type = IntermediateFileType::FromExtension(ext);
5076 char fileName[MAX_FILENAME];
5077 GetLastDirectory(filePath, fileName);
5080 DotMain dotMain = DotMain::FromFileName(fileName);
5081 node = project.FindNodeByObjectFileName(fileName, type, dotMain, null);
5088 static void WorkspaceGetRelativePath(char * absolutePath, char * relativePath, Project * owner)
5090 Project prj = WorkspaceGetFileOwner(absolutePath);
5094 prj = ide.workspace.projects.firstIterator.data;
5097 MakePathRelative(absolutePath, prj.topNode.path, relativePath);
5098 MakeSlashPath(relativePath);
5101 relativePath[0] = '\0';
5104 static bool ProjectGetAbsoluteFromRelativePath(Project project, char * relativePath, char * absolutePath)
5106 ProjectNode node = project.topNode.FindWithPath(relativePath, false);
5108 node = ProjectGetObjectFileNode(project, relativePath);
5111 strcpy(absolutePath, node.project.topNode.path);
5112 PathCat(absolutePath, relativePath);
5113 MakeSlashPath(absolutePath);
5115 return node != null;