; --------------------------------------------------------------------------------------------; Copyright (c) Fantaisie Software. All rights reserved.; Dual licensed under the GPL and Fantaisie Software licenses.; See LICENSE and LICENSE-FANTAISIE in the project root for license information.; --------------------------------------------------------------------------------------------CompilerIf #PRINT_DEBUGGER_COMMANDSOpenConsole()CompilerEndIfDeclare Debugger_ForceDestroy(*Debugger.DebuggerData)Procedure Debugger_Quit()While FirstElement(RunningDebuggers())Debugger_ForceDestroy(@RunningDebuggers())WendEndProcedureProcedure IsDebuggerValid(*Debugger)Found = 0Index = ListIndex(RunningDebuggers())ForEach RunningDebuggers()If @RunningDebuggers() = *DebuggerFound = 1BreakEndIfNext RunningDebuggers()If Index = -1ResetList(RunningDebuggers())ElseSelectElement(RunningDebuggers(), Index)EndIfProcedureReturn FoundEndProcedure; The debugger structure is destroyed when the exe is unloaded, AND all the; debugger windows have been closed by the user; this procedure checks these conditions and destroys the debugger structure; if needed;Procedure Debugger_CheckDestroy(*Debugger.DebuggerData); is the debugger structure even valid any more?;If IsDebuggerValid(*Debugger) = 0ProcedureReturnEndIf; check if exe is running;If *Debugger\CanDestroy = 0ProcedureReturnEndIf; check for open windows;For i = 0 To #DEBUGGER_WINDOW_LAST-1If i = #DEBUGGER_WINDOW_Debug ; this is a special case (can be open, but hidden)If *Debugger\Windows[i] And *Debugger\IsDebugOutputVisibleProcedureReturnEndIfElseIf i = #DEBUGGER_WINDOW_WatchList ; this too is a special case (is always open)If *Debugger\IsWatchListVisibleProcedureReturnEndIfElseIf i = #DEBUGGER_WINDOW_DataBreakpointsIf *Debugger\DataBreakpointsVisibleProcedureReturnEndIfElseIf *Debugger\Windows[i]ProcedureReturnEndIfNext i; this is a special case!;If IsWindow(*Debugger\Windows[#DEBUGGER_WINDOW_Debug])CloseWindow(*Debugger\Windows[#DEBUGGER_WINDOW_Debug])EndIfIf IsWindow(*Debugger\Windows[#DEBUGGER_WINDOW_WatchList])VariableGadget_Free(*Debugger\Gadgets[#DEBUGGER_GADGET_WatchList_List])CloseWindow(*Debugger\Windows[#DEBUGGER_WINDOW_WatchList])EndIfIf IsWindow(*Debugger\Windows[#DEBUGGER_WINDOW_DataBreakpoints])CloseWindow(*Debugger\Windows[#DEBUGGER_WINDOW_DataBreakpoints])EndIf; free any memory blocks referenced from this structure;If *Debugger\IncludedFilesFreeMemory(*Debugger\IncludedFiles)EndIfIf *Debugger\ProceduresFreeMemory(*Debugger\Procedures)EndIfIf *Debugger\MemoryDumpFreeMemory(*Debugger\MemoryDump)EndIf; the data array is freed automatically when the window closes;If *Debugger\ProfilerFiles*Files.Debugger_ProfilerList = *Debugger\ProfilerFiles; release the color images;For i = 0 To *Debugger\NbIncludedFilesIf *Files\file[i]\ColorImageFreeImage(*Files\file[i]\ColorImage)EndIfNext iFreeMemory(*Debugger\ProfilerFiles)EndIfIf *Debugger\History*history.Debugger_History = *Debugger\HistoryFor i = 0 To *Debugger\HistorySize - 1VariableGadget_Free(*history\item[i]\Variables)Next iFreeMemory(*Debugger\History)EndIfIf *Debugger\LibraryList*libraries.Debugger_LibraryList = *Debugger\LibraryListFor i = 0 To *Debugger\NbLibraries - 1FreePBString(@*libraries\library[i]\LibraryID$)FreePBString(@*libraries\library[i]\Name$)FreePBString(@*libraries\library[i]\TitleString$)Next iFreeMemory(*Debugger\LibraryList)EndIfIf *Debugger\ObjectListFreeMemory(*Debugger\ObjectList)EndIf; Terminate the communication, if not already done;If *Debugger\Communication*Debugger\Communication\Disconnect()EndIf; Close the Process ObjectIf *Debugger\ProcessObject; There can be zombies process in Linux when the debugger exits: https://www.purebasic.fr/english/viewtopic.php?t=84602;CompilerIf #CompileLinuxIf WaitProgram(*Debugger\ProcessObject, 0) = #False ; still runningKillProgram(*Debugger\ProcessObject)EndIfCompilerEndIfCloseProgram(*Debugger\ProcessObject)*Debugger\ProcessObject = 0EndIf; Now cleanup the communication, if not done yet;If *Debugger\Communication*Debugger\Communication\Close()*Debugger\Communication = 0EndIfCompilerIf #CompileWindowsIf *Debugger\TerminationMutexCloseHandle_(*Debugger\TerminationMutex)*Debugger\TerminationMutex = 0EndIfCompilerEndIf; remove the structure;ChangeCurrentElement(RunningDebuggers(), *Debugger)DeleteElement(RunningDebuggers())EndProcedureProcedure Debugger_ForceDestroy(*Debugger.DebuggerData); is the debugger structure even valid any more?;If IsDebuggerValid(*Debugger) = 0ProcedureReturnEndIf; flag structure for destruction;*Debugger\CanDestroy = 1; Terminate communication;If *Debugger\CommunicationIf *Debugger\IsNetwork; Send the KILL command so the target exe can quit itself (if the connection still works though);Command.CommandInfo\Command = #COMMAND_KillSendDebuggerCommand(*Debugger, @Command)EndIf*Debugger\Communication\Disconnect()EndIf; Kill exe, if it still runs;If *Debugger\ProcessObjectIf WaitProgram(*Debugger\ProcessObject, 0) = #False ; still running; Try to quit the Exe without a forced kill;CompilerIf #CompileWindowsIf *Debugger\TerminationMutex; releasing this mutex should cause the process to quit itself (cleaner then TerminatProcess)ReleaseMutex_(*Debugger\TerminationMutex)EndIfCompilerElse; "ask" the program to quit (with calling the EndFunctions)kill_(ProgramID(*Debugger\ProcessObject), #SIGTERM) ; ProgramID() returns the pid on unixCompilerEndIf; Wait a bit and kill if it did not work;If WaitProgram(*Debugger\ProcessObject, 1500) = #FalseKillProgram(*Debugger\ProcessObject)EndIfEndIfCloseProgram(*Debugger\ProcessObject)*Debugger\ProcessObject = 0EndIf; Now cleanup the communication, if not done yet;If *Debugger\Communication*Debugger\Communication\Close()*Debugger\Communication = 0EndIfCompilerIf #CompileWindowsIf *Debugger\TerminationMutexCloseHandle_(*Debugger\TerminationMutex)*Debugger\TerminationMutex = 0EndIfCompilerEndIf; close all associated windows; will also call the Debugger_CheckDestroy() function after the windows are removed;For i = 0 To #DEBUGGER_WINDOW_LAST-1If *Debugger\Windows[i]; send it a window close event:;Debugger_ProcessEvents(*Debugger\Windows[i], #PB_Event_CloseWindow); each closing of a debugger window can destroy the structure already,; as they all call Debugger_CheckDestroy(), so we must check for that!;If IsDebuggerValid(*Debugger) = 0ProcedureReturnEndIf; if it is still there then forcibly remove it;If IsWindow(*Debugger\Windows[i])CloseWindow(*Debugger\Windows[i])*Debugger\Windows[i] = 0EndIfEndIfNext i; make sure the thing is really removed from the Debuggers list!; even if some other procedure didn't clean up right!;If IsDebuggerValid(*Debugger); Call the CheckDestroy() once again to give it a last chance to correctly free the dataDebugger_CheckDestroy(*Debugger)If IsDebuggerValid(*Debugger) ; if it is still there, it is really messed upChangeCurrentElement(RunningDebuggers(), *Debugger)DeleteElement(RunningDebuggers())EndIfEndIfEndProcedure;; Debugging only. To log all communication to a file;CompilerIf #LOG_DEBUGGER_COMMANDSProcedure LogDebuggerCommand(Direction, *Command.CommandInfo, *CommandData)If DirectionIf *CommandRestore DebuggerCommands_OutgoingFor i = 0 To *Command\CommandRead.s ConstantName$Next iEndIfDirection$ = "Debugger -> Executable"ElseIf *CommandIf *Command\Command = #COMMAND_FatalErrorConstantName$ = "#COMMAND_FatalError"ElseRestore DebuggerCommands_IncomingFor i = 0 To *Command\CommandRead.s ConstantName$Next iEndIfEndIfDirection$ = "Executable -> Debugger"EndIfLog = OpenFile(#PB_Any, #LOG_DEBUGGER_FILE)If LogFileSeek(Log, Lof(Log))WriteStringN(Log, "")WriteStringN(Log, "--------------------------------------------------------------------------------")If *CommandWriteStringN(Log, FormatDate("[%hh:%ii:%ss] ", *Command\Timestamp) + Direction$)WriteStringN(Log, " Command : " + Str(*Command\Command) + " ("+ConstantName$+")")WriteStringN(Log, " Value1 : " + Str(*Command\Value1) + " ($" + Hex(*Command\Value1, #PB_Long)+")")WriteStringN(Log, " Value2 : " + Str(*Command\Value2) + " ($" + Hex(*Command\Value2, #PB_Long)+")")WriteStringN(Log, " DataSize: " + Str(*Command\DataSize) + " Bytes")ElseWriteStringN(Log, FormatDate("[%hh:%ii:%ss] ", Date()) + Direction$)WriteStringN(Log, " --- ERROR: Invalid command (*Command = 0) ---")EndIfWriteStringN(Log, "--------------------------------------------------------------------------------")If *Command And *Command\DataSize > 0If *CommandData = 0WriteStringN(Log, " --- ERROR: Expected CommandData not available! ---")Else*Base = *CommandDataWhile *Base < *CommandData + *Command\DataSizeOffset$ = " $" + RSet(Hex(*Base - *CommandData), 8, "0")Hex$ = " "String$ = " " ; one space is added by the last hex columnFor i = 0 To 15If *Base + i < *CommandData + *Command\DataSizeValue = PeekB(*Base + i) & $FFHex$ + RSet(Hex(Value), 2, "0") + " "If Value < 32String$ + "."ElseString$ + Chr(Value)EndIfElseHex$ + " "EndIfNext i*Base + 16WriteStringN(Log, Offset$+Hex$+String$)WendEndIfWriteStringN(Log, "--------------------------------------------------------------------------------")EndIfWriteStringN(Log, "")CloseFile(Log)EndIfEndProcedureProcedure LogProgramCreation(Executable,ドル Parameters,ドル Directory$)Info$ = "PureBasic " + StrD(#PB_Compiler_Version/100, 2)CompilerIf #CompileWindowsInfo$ + " - Windows"CompilerElseCompilerIf #CompileLinuxInfo$ + "- Linux"CompilerElseInfo$ + "- Mac OSX"CompilerEndIfCompilerEndIfCompilerIf #CompileX86Info$ + "- x86"CompilerElseCompilerIf #CompileX64Info$ + "- x64"CompilerElseInfo$ + "- ppc"CompilerEndIfCompilerEndIfCompilerIf #PB_Compiler_UnicodeInfo$ + " (Unicode mode)"CompilerElseInfo$ + " (Ascii mode)"CompilerEndIfLog = CreateFile(#PB_Any, #LOG_DEBUGGER_fILE)If LogWriteStringN(Log, "--------------------------------------------------------------------------------")WriteStringN(Log, " Debugger communication log")WriteStringN(Log, "--------------------------------------------------------------------------------")WriteStringN(Log, "")WriteStringN(Log, " Debugger exe : " + ProgramFilename())WriteStringN(Log, " Build date : "+FormatDate("%mm/%dd/%yyyy - %hh:%ii", #PB_Compiler_Date))WriteStringN(Log, " Build with : "+Info$)WriteStringN(Log, "")WriteStringN(Log, " Executable : "+Executable$)WriteStringN(Log, " Parameters : "+Parameters$)WriteStringN(Log, " Directory : "+Directory$)WriteStringN(Log, " Session start: "+FormatDate("%hh:%ii:%ss", Date()))WriteStringN(Log, "")WriteStringN(Log, "--------------------------------------------------------------------------------")WriteStringN(Log, "")CloseFile(Log)EndIfEndProcedureCompilerEndIf; sends a command to the debugger;Procedure SendDebuggerCommandWithData(*Debugger.DebuggerData, *Command.CommandInfo, *CommandData)CompilerIf #PRINT_DEBUGGER_COMMANDSRestore DebuggerCommands_OutgoingFor i = 0 To *Command\CommandRead.s ConstantName$Next iPrintN("IDE::SEND->"+ConstantName$+" ("+Str(*Command\Command)+")")CompilerEndIf*Command\TimeStamp = Date()CompilerIf #LOG_DEBUGGER_COMMANDSLogDebuggerCommand(1, *Command, *CommandData)CompilerEndIfIf *Debugger\Communication And *Debugger\ProgramState <> -1 ; can only send to loaded executables!*Debugger\Communication\Send(*Command, *CommandData)CompilerIf #CompileLinuxGtk2 ; TODO-GTK3 TODO-QT; Send an X event to the child (actually to all x windows), which should make its WaitWindowEvent() return.Event.GdkEventClientEvent\type = #GDK_CLIENT_EVENTEvent\send_event = 1Event\message_type = gdk_atom_intern_("PureBasic_Break", 0)Event\data_format = 32gdk_event_send_clientmessage_toall_(@Event)CompilerEndIfEndIfEndProcedureProcedure SendDebuggerCommand(*Debugger.DebuggerData, *Command.CommandInfo)*Command\DataSize = 0SendDebuggerCommandWithData(*Debugger, *Command, 0)EndProcedure; Function shared with the standalone debugger loop;Procedure Debugger_End(*Debugger.DebuggerData)UpdateDebugOutputWindow(*Debugger)*Debugger\CanDestroy = 1Debugger_CheckDestroy(*Debugger) ; make an attempt to remove the structure (if all windows are closed)EndProcedure; this procedure checks the command stack for; all debuggers and processes any waiting ones.; it returns nonzero when at least one command was processed; NOTE: this must be called on a constant basis from the main; loop!;Procedure Debugger_ProcessIncomingCommands()result = 0; CompilerIf #CompileWindows; ProcessDebugEvents() ; Win32 debug API events; CompilerEndIfForEach RunningDebuggers()*Debugger.DebuggerData = @RunningDebuggers()RepeatIf *Debugger\Communication = 0BreakElseIf *Debugger\Communication\Receive(@*Debugger\Command, @*Debugger\CommandData) = #FalseIf *Debugger\Communication\CheckErrors(@*Debugger\Command, *Debugger\ProcessObject) = #FalseBreakEndIf*Debugger\CommandData = 0EndIfresult + 1 ; a command is being processed; debugging...CompilerIf #PRINT_DEBUGGER_COMMANDSIf *Debugger\Command\Command = #COMMAND_FatalErrorPrintN("IDE::PROCESSING->FatalError (Value1 = "+Str(*Debugger\Command\Value1)+")")ElseRestore DebuggerCommands_IncomingFor i = 0 To *Debugger\Command\CommandRead.s ConstantName$Next iPrintN("IDE::PROCESSING->"+ConstantName$+" ("+Str(*Debugger\Command\Command)+")")EndIfCompilerEndIfCompilerIf #LOG_DEBUGGER_COMMANDSLogDebuggerCommand(0, *Debugger\Command, *Debugger\CommandData)CompilerEndIf; If *Debugger\Windows[#DEBUGGER_WINDOW_Debug] = 0; OpenDebugWindow(*Debugger, #False); EndIf; Gadget = *Debugger\Gadgets[#DEBUGGER_GADGET_Debug_List]; AddGadgetItem(Gadget, -1, "IDE::RECEIVE->"+Str(*Debugger\Command\Command)); SetGadgetState(Gadget, CountGadgetItems(Gadget)-1); we are outside the locked area and outside the thread,; so we can do pretty much what we want here (except accessing the command stack);Select *Debugger\Command\Command ; handle stuff that is the same for ide and debuggerCase #COMMAND_FatalError ; indicates fatal communication error of debuggerSelect *Debugger\Command\Value1Case #ERROR_MemoryMessage$ = Language("Debugger", "MemoryError")Case #ERROR_PipeMessage$ = Language("Debugger", "PipeError")Case #ERROR_ExeQuitMessage$ = Language("Debugger", "ExeQuitError")Case #ERROR_TimeoutIf DebuggerTimeout % 1000 = 0Timeout$ = Str(DebuggerTimeout/1000)ElseTimeout$ = StrF(DebuggerTimeout/1000.0, 1)EndIfMessage$ = ReplaceString(Language("Debugger", "TimeoutError"), "%timeout%", Timeout$)Case #ERROR_NetworkFailMessage$ = Language("Debugger", "NetworkError")EndSelectCompilerIf #CompileWindows; Very weird Vista related problem:;; If a program is missing a dll, and you get the "missing dll" message at; startup, Vista will put up another "the program has a problem" dialog; once you click ok. Somehow this window messes with the z-order or something.; After you click "close" on this dialog, the following requester will; open behind (!) the IDE, and be unaccessible (=lockup).;; The only way to solve this is to force our window to the foreground before.; (Leave this in for all Windows versions, because in case of a Memory/Pipe error,; we never know who had the focus so maybe this could happened there too);CompilerIf Defined(PUREBASIC_IDE, #PB_Constant)SetWindowForeground_Real(#WINDOW_Main)CompilerElseSetWindowForeground_Real(#WINDOW_Main)CompilerEndIfCompilerEndIfMessageRequester("PureBasic Debugger", Message,ドル #FLAG_Error); pass this on to the calllbackRealProgramState = *Debugger\ProgramState ; save for later restoring (so ForceDestroy() correctly ends the program)*Debugger\ProgramState = -1 ; tell that the exe is not loaded not loaded*Debugger\LastProgramState = -1 ; last state no longer mattersDebugger_UpdateWindowStates(*Debugger)DebuggerCallback(*Debugger)*Debugger\ProgramState = RealProgramStateDebugger_ForceDestroy(*Debugger) ; force the removal of this debugger!Break 2 ; important, because the *Debugger is now invalid!Case #COMMAND_InitIf *Debugger\Command\Value2 = #DEBUGGER_Version*Debugger\NbIncludedFiles = *Debugger\Command\Value1 ; save the included files*Debugger\IncludedFiles = *Debugger\CommandData*Debugger\CommandData = 0 ; do not free this buffer*Debugger\LastProgramState = *Debugger\ProgramState*Debugger\ProgramState = 0 ; runningDebugger_UpdateWindowStates(*Debugger)If IsWindow(*Debugger\Windows[#DEBUGGER_WINDOW_Variable]) ; if the variable viewer is open before the program loadedCommand.CommandInfo\Command = #COMMAND_GetGlobalNamesSendDebuggerCommand(*Debugger, @Command)EndIf; request module and procedure names from the executable;Command.CommandInfo\Command = #COMMAND_GetModulesSendDebuggerCommand(*Debugger, @Command)Command.CommandInfo\Command = #COMMAND_GetProceduresSendDebuggerCommand(*Debugger, @Command); AutoRun Profiler if requested;If ProfilerRunAtStartCommand.CommandInfo\Command = #COMMAND_StartProfilerSendDebuggerCommand(*Debugger, @Command); request the profiler offsets right away, so they are available; even if the user only opens the profiler after the program quit.Command.CommandInfo\Command = #COMMAND_GetProfilerOffsetsSendDebuggerCommand(*Debugger, @Command)*Debugger\ProfilerRunning = 1EndIf; open the windows which are set to auto-open;If AutoOpenDebugOutputOpenDebugWindow(*Debugger, #True)EndIfIf AutoOpenAsmWindowOpenAsmWindow(*Debugger)EndIfIf AutoOpenMemoryViewerOpenMemoryViewerWindow(*Debugger)EndIfIf AutoOpenVariableViewerOpenVariableWindow(*Debugger)EndIfIf AutoOpenHistoryOpenHistoryWindow(*Debugger)EndIfIf AutoOpenWatchlistOpenWatchListWindow(*Debugger)EndIfIf AutoOpenLibraryViewerOpenLibraryViewerWindow(*Debugger)EndIfIf AutoOpenProfilerOpenProfilerWindow(*Debugger)EndIfIf AutoOpenDataBreakpointsOpenDataBreakpointWindow(*Debugger)EndIfIf AutoOpenPurifierOpenPurifierWindow(*Debugger)EndIfElse; version conflict!!MessageRequester("PureBasic Debugger", Language("Debugger","VersionError"), #FLAG_Error); pass this on to the calllback*Debugger\Command\Command = #COMMAND_FatalError*Debugger\Command\Value1 = #ERROR_VersionRealProgramState = *Debugger\ProgramState ; save for later restoring (so ForceDestroy() correctly ends the program)*Debugger\ProgramState = -1 ; tell that the exe is not loaded not loaded*Debugger\LastProgramState = -1 ; last state no longer mattersDebugger_UpdateWindowStates(*Debugger)DebuggerCallback(*Debugger)*Debugger\ProgramState = RealProgramStateDebugger_ForceDestroy(*Debugger) ; force the removal of this debugger!Break 2 ; important, because the *Debugger is now invalid!EndIfCase #COMMAND_ExeModeIf *Debugger\Command\Value1 & (1 << 0)*Debugger\IsUnicode = 1EndIfIf *Debugger\Command\Value1 & (1 << 1)*Debugger\IsThread = 1EndIfIf *Debugger\Command\Value1 & (1 << 2)*Debugger\Is64Bit = 1EndIfIf *Debugger\Command\Value1 & (1 << 3)*Debugger\IsPurifier = 1EndIf; initialize the directories for these outputs to the source directory; do this here and not in #COMMAND_Init so the standalone debugger had a chance to init the file nameDebuggerOutputFile$ = GetPathPart(*Debugger\FileName$)MemoryViewerFile$ = GetPathPart(*Debugger\FileName$)Case #COMMAND_End*Debugger\ProgramState = -1 ; not loaded*Debugger\LastProgramState = -1 ; last state no longer matters*Debugger\ProgramEnded = 1 ; for ignoring any following pipe errors (happens on linux in console mode)Debugger_UpdateWindowStates(*Debugger)Case #COMMAND_Error*Debugger\LastProgramState = *Debugger\ProgramState*Debugger\ProgramState = *Debugger\Command\Value2Debugger_UpdateWindowStates(*Debugger); No generic action to take here, as the program state is not modified; (only display needs to be done by the callback); Case #COMMAND_WarningCase #COMMAND_Stopped*Debugger\LastProgramState = *Debugger\ProgramState*Debugger\ProgramState = *Debugger\Command\Value2Debugger_UpdateWindowStates(*Debugger)Case #COMMAND_Continued*Debugger\LastProgramState = *Debugger\ProgramState*Debugger\ProgramState = 0 ; runningDebugger_UpdateWindowStates(*Debugger)Case #COMMAND_Debug, #COMMAND_DebugDouble, #COMMAND_DebugQuad, #COMMAND_Expression, #COMMAND_SetVariableResult, #COMMAND_ControlDebugOutputDebugOutput_DebuggerEvent(*Debugger)Case #COMMAND_RegisterLayout, #COMMAND_Register, #COMMAND_Stack, #COMMAND_ControlAssemblyViewerAsmDebug_DebuggerEvent(*Debugger)Case #COMMAND_Memory, #COMMAND_ControlMemoryViewerMemoryViewer_DebuggerEvent(*Debugger)Case #COMMAND_GlobalNames, #COMMAND_Globals, #COMMAND_Locals, #COMMAND_ArrayInfo, #COMMAND_ArrayData, #COMMAND_ListData, #COMMAND_ListInfo, #COMMAND_MapData, #COMMAND_MapInfo, #COMMAND_ControlVariableViewerVariableDebug_DebuggerEvent(*Debugger)Case #COMMAND_History, #COMMAND_HistoryLocals, #COMMAND_ControlCallstackHistory_DebuggerEvent(*Debugger)Case #COMMAND_Procedures*Debugger\NbProcedures = *Debugger\Command\Value1*Debugger\Procedures = *Debugger\CommandData*Debugger\CommandData = 0; in case the history window was open before exe started, add the list nowHistory_DebuggerEvent(*Debugger); the watchlist requires this event too! (to fill the procedure list)WatchList_DebuggerEvent(*Debugger)DataBreakpoint_DebuggerEvent(*Debugger)Case #COMMAND_ProcedureStatsHistory_DebuggerEvent(*Debugger)Case #COMMAND_Watchlist, #COMMAND_WatchlistEvent, #COMMAND_WatchlistError, #COMMAND_ControlWatchlistWatchList_DebuggerEvent(*Debugger)Case #COMMAND_DataBreakPointDataBreakpoint_DebuggerEvent(*Debugger)Case #COMMAND_Libraries, #COMMAND_LibraryInfo, #COMMAND_ObjectID, #COMMAND_ObjectText, #COMMAND_ObjectData, #COMMAND_ControlLibraryViewerLibraryViewer_DebuggerEvent(*Debugger)Case #COMMAND_ProfilerOffsets, #COMMAND_ProfilerData, #COMMAND_ControlProfilerProfiler_DebuggerEvent(*Debugger)Case #COMMAND_ControlPurifierPurifier_DebuggerEvent(*Debugger)Case #COMMAND_Modules*Debugger\NbModules = *Debugger\Command\Value1If *Debugger\CommandData And *Debugger\NbModules > 0ReDim *Debugger\ModuleNames(*Debugger\NbModules-1)*Pointer = *Debugger\CommandDataFor i = 0 To *Debugger\NbModules - 1*Debugger\ModuleNames(i) = PeekS(*Pointer, -1, #PB_Ascii)*Pointer + MemoryStringLength(*Pointer, #PB_Ascii) + 1Next iEndIfEndSelectDebuggerCallback(*Debugger) ; call the declared callbackIf IsDebuggerValid(*Debugger)= 0 ; make sure the debugger is still valid (might have been closed by the callbackBreak 2EndIf; exe is unloaded, take necessary steps;If *Debugger\Command\Command = #COMMAND_EndDebugger_End(*Debugger) ; Function shared with the Standalone debugger callbackBreak 2 ; important, because the *Debugger is now invalid!EndIf; free any associated command data;If *Debugger\CommandDataFreeMemory(*Debugger\CommandData)*Debugger\CommandData = 0EndIfUntil result > 100 ; only process 100 commands at a time to allow GUI updates to take place!If IsDebuggerValid(*Debugger)UpdateDebugOutputWindow(*Debugger) ; Will check if the debugger output needs to be updated (some debug message in queue)EndIfNext RunningDebuggers()ProcedureReturn resultEndProcedure; executes the given program and returns a debugger structure for it;Procedure Debugger_ExecuteProgram(FileName,ドル CommandLine,ドル Directory$)Debug "Debugger_ExecuteProgram():"Debug FileName$Debug CommandLine$Debug Directory$Debug DebuggerIseFIFO; add a new debugger structure;LastElement(RunningDebuggers())If AddElement(RunningDebuggers()) = 0Debug " -- Debugger_ExecuteProgram() failed: AddElement()"ProcedureReturn 0EndIf; generate a unique ID to represent this structure;RunningDebuggers()\ID = GetUniqueID(); this is to fix an issue in the CPU monitor of the ide; RunningDebuggers()\CPUTime = -1; create the communication object and; setup the environment variables or file to pass on to the new process;; CompilerIf #CompileMac ; OSX-debug; DebuggerUseFIFO = 1 ; always used; CompilerEndIfCompilerIf #CompileWindows = 0If DebuggerUseFIFORunningDebuggers()\Communication = CreateFifoCommunication()If RunningDebuggers()\Communication = 0DeleteElement(RunningDebuggers())Debug " -- Debugger_ExecuteProgram() failed: CreateFifoCommunication()"ProcedureReturn 0EndIfFile = CreateFile(#PB_Any, "/tmp/.pbdebugger.out")If FileWriteStringN(File, "PB_DEBUGGER_Communication") ; identifyer stringWriteStringN(File, Str(Date())) ; use a timestamp to ignore an old .purebasic.out from a crash (else the console debugger never starts!)WriteStringN(File, RunningDebuggers()\Communication\GetInfo())WriteStringN(File, Str(#PB_Compiler_Unicode)+";"+Str(CallDebuggerOnStart)+";"+Str(CallDebuggerOnEnd)+";"+Str(#DEBUGGER_BigEndian)) ; options (UnicodeMode;CallOnStart;CallOnEnd;IsBigEndian)CloseFile(File)ElseRunningDebuggers()\Communication\Close()DeleteElement(RunningDebuggers())Debug " -- Debugger_ExecuteProgram() failed: CreateFile(/tmp/.pbdebugger.out)"ProcedureReturn 0EndIfElseCompilerEndIfRunningDebuggers()\Communication = CreatePipeCommunication()If RunningDebuggers()\Communication = 0DeleteElement(RunningDebuggers())Debug " -- Debugger_ExecuteProgram() failed: CreatePipeCommunication()"ProcedureReturn 0EndIfSetEnvironmentVariable("PB_DEBUGGER_Communication", RunningDebuggers()\Communication\GetInfo())SetEnvironmentVariable("PB_DEBUGGER_Options", Str(#PB_Compiler_Unicode)+";"+Str(CallDebuggerOnStart)+";"+Str(CallDebuggerOnEnd)+";"+Str(#DEBUGGER_BigEndian))CompilerIf #CompileWindows = 0EndIfCompilerEndIfRunningDebuggers()\IsNetwork = #False; for windows we need an alternative solution for TerminateProcess_(); we do it through a mutex.; It is initially locked, and if unlocked, the debugged program will terminate itself.;CompilerIf #CompileWindows; Note: We now use RunProgram() on Windows, so no handle inheriting.; Use a named mutex then.MutexName$ = "PureBasic_DebuggerMutex_" + Hex(Random(7ドルFFFFFFF))RunningDebuggers()\TerminationMutex = CreateMutex_(#Null, 1, MutexName$)If RunningDebuggers()\TerminationMutex = 0 Or GetLastError_() = #ERROR_ALREADY_EXISTSRunningDebuggers()\Communication\Close()DeleteElement(RunningDebuggers())Debug " -- Debugger_ExecuteProgram() failed: CreateMutex_() (Windows termination mutex)"ProcedureReturn 0EndIfSetEnvironmentVariable("PB_DEBUGGER_KillMutex", MutexName$)CompilerEndIf; try to create the new process;RunningDebuggers()\ProcessObject = RunProgram(FileName,ドル CommandLine,ドル Directory,ドル #PB_Program_Open)If RunningDebuggers()\ProcessObject = 0RunningDebuggers()\Communication\Close()CompilerIf #CompileWindowsCloseHandle_(RunningDebuggers()\TerminationMutex)CompilerEndIfDeleteElement(RunningDebuggers())Debug " -- Debugger_ExecuteProgram() failed: RunProgram()"ProcedureReturn 0EndIf; remove the environment variables again;CompilerIf #CompileWindows = 0If DebuggerUseFIFO = 0CompilerEndIfRemoveEnvironmentVariable("PB_DEBUGGER_Communication")RemoveEnvironmentVariable("PB_DEBUGGER_Options")CompilerIf #CompileWindows = 0EndIfCompilerEndIfCompilerIf #CompileWindowsRemoveEnvironmentVariable("PB_DEBUGGER_KillMutex")CompilerEndIf; Try to connect the communication;If RunningDebuggers()\Communication\Connect() = 0RunningDebuggers()\Communication\Close() ; cleanup; Try to quit the Exe without a forced killCompilerIf #CompileWindowsReleaseMutex_(RunningDebuggers()\TerminationMutex)CompilerElsekill_(ProgramID(RunningDebuggers()\ProcessObject), #SIGTERM) ; ProgramID() returns the pid on unixCompilerEndIf; Wait a bit and kill if it did not workIf WaitProgram(RunningDebuggers()\ProcessObject, 1500) = #FalseKillProgram(RunningDebuggers()\ProcessObject)EndIfCompilerIf #CompileWindowsCloseHandle_(RunningDebuggers()\TerminationMutex)CompilerEndIfDeleteElement(RunningDebuggers())Debug " -- Debugger_ExecuteProgram() failed: Communication\Connect()"ProcedureReturn 0EndIfRunningDebuggers()\ProgramState = -1 ; indicate that exe is not loaded yet. (or has not called PB_DEBUGGER_Start())RunningDebuggers()\LastProgramState = -1; the watchlist window is always open, but invisible, same for the debug one;CreateWatchlistWindow(@RunningDebuggers())CreateDebugWindow(@RunningDebuggers())CreateDataBreakpointWindow(@RunningDebuggers()); Default purifier options if not set otherwise laterRunningDebuggers()\PurifierGlobal = 1RunningDebuggers()\PurifierLocal = 1RunningDebuggers()\PurifierString = 64RunningDebuggers()\PurifierDynamic = 1;; Debugging;CompilerIf #LOG_DEBUGGER_COMMANDSLogProgramCreation(Executable,ドル CommandLine,ドル Directory$)CompilerEndIf; all done, process & debugger successfully created; return the debugger structureProcedureReturn @RunningDebuggers()EndProcedure
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。