def fBugReportCallback(oBugId, oBugReport): global guDetectedBugsCount, \ guMaximumNumberOfBugs, \ gduNumberOfRepros_by_sBugIdAndLocation; guDetectedBugsCount += 1; oConsole.fLock(); try: oConsole.fPrint(u"\u250C\u2500 ", HILITE, "A bug was detect in the application ", NORMAL, sPadding = u"\u2500"); if oBugReport.sBugLocation: oConsole.fPrint(u"\u2502 Id @ Location: ", INFO, oBugReport.sId, NORMAL, " @ ", INFO, oBugReport.sBugLocation); sBugIdAndLocation = "%s @ %s" % (oBugReport.sId, oBugReport.sBugLocation); else: oConsole.fPrint(u"\u2502 Id: ", INFO, oBugReport.sId); sBugIdAndLocation = oBugReport.sId; gduNumberOfRepros_by_sBugIdAndLocation.setdefault(sBugIdAndLocation, 0); gduNumberOfRepros_by_sBugIdAndLocation[sBugIdAndLocation] += 1; if oBugReport.sBugSourceLocation: oConsole.fPrint(u"\u2502 Source: ", INFO, oBugReport.sBugSourceLocation); oConsole.fPrint(u"\u2502 Description: ", INFO, oBugReport.sBugDescription); oConsole.fPrint(u"\u2502 Security impact: ", INFO, oBugReport.sSecurityImpact); oConsole.fPrint(u"\u2502 Version: ", NORMAL, oBugReport.asVersionInformation[0]); # The process' binary. for sVersionInformation in oBugReport.asVersionInformation[1:]: # There may be two if the crash was in a oConsole.fPrint(u"\u2502 ", NORMAL, sVersionInformation); # different binary (e.g. a .dll) if dxConfig["bGenerateReportHTML"]: # Use a report file name base on the BugId. sDesiredReportFileName = "%s.html" % sBugIdAndLocation; # In collateral mode, we will number the reports so you know in which order bugs were reported. if guMaximumNumberOfBugs > 1: sDesiredReportFileName = "#%d %s" % (guDetectedBugsCount, sDesiredReportFileName); # Translate characters that are not valid in file names. sValidReportFileName = mFileSystem.fsValidName(sDesiredReportFileName, bUnicode = \ dxConfig["bUseUnicodeReportFileNames"]); if dxConfig["sReportFolderPath"] is not None: sReportFilePath = mFileSystem.fsPath(dxConfig["sReportFolderPath"], sValidReportFileName); else: sReportFilePath = mFileSystem.fsPath(sValidReportFileName); eWriteDataToFileResult = mFileSystem.feWriteDataToFile( oBugReport.sReportHTML, sReportFilePath, fbRetryOnFailure = lambda: False, ); if eWriteDataToFileResult: oConsole.fPrint(u"\u2502 Bug report: ", ERROR, "Cannot be saved (", \ ERROR_INFO, repr(eWriteDataToFileResult), ERROR, ")"); else: oConsole.fPrint(u"\u2502 Bug report: ", NORMAL, sValidReportFileName, \ " (%d bytes)" % len(oBugReport.sReportHTML)); oConsole.fPrint(u"\u2514", sPadding = u"\u2500"); finally: oConsole.fUnlock();
r"\\?\x:\y\z": r"\\?\x:\y\z", r"\\x\y\z": r"\\?\UNC\x\y\z", r"\\?\UNC\x\y\z": r"\\?\UNC\x\y\z", }; for (sInput, sExpectedOutput) in dsPathTests.items(): sOutput = mFileSystem.fsPath(sInput); assert sOutput == sExpectedOutput, \ "fsPath error for %s: got %s, expected %s" % (sInput, sOutput, sExpectedOutput); sInput += "\\"; sOutput = mFileSystem.fsPath(sInput); assert sOutput == sExpectedOutput, \ "fsPath error for %s: got %s, expected %s" % (sInput, sOutput, sExpectedOutput); for bUnicode in [True, False]: print "* Testing file operations with special characters in %s mode..." % (bUnicode and "Unicode" or "ASCII"); sTranslatedSpecialChars = mFileSystem.fsValidName(sSpecialChars, bUnicode = bUnicode); print u" File/folder name: %s" % sTranslatedSpecialChars; sData = "x"; # Clean old files/folders print " * Cleaning up old files/folders..."; print " * Checking if old test file exists..."; ebIsFileResult = mFileSystem.febIsFile(sTempFolderPath, sTranslatedSpecialChars); assert isinstance(ebIsFileResult, bool), "Expected febIsFile result to be a boolean, got %s" % repr(ebIsFileResult); if ebIsFileResult: print " * Deleting old test file..."; ebDeleteFileResult = mFileSystem.febDeleteFile(sTempFolderPath, sTranslatedSpecialChars); assert ebDeleteFileResult is True, "Expected febDeleteFile result to be True, got %s" % repr(ebDeleteFileResult); print " * Checking if old test folder exists..."; ebIsFolderResult = mFileSystem.febIsFolder(sTempFolderPath, sTranslatedSpecialChars); assert isinstance(ebIsFolderResult, bool), "Expected febIsFolder result to be a boolean, got %s" % repr(ebIsFolderResult);
def fTest( sISA, axCommandLineArguments, asExpectedBugIdAndLocations, sExpectedFailedToDebugApplicationErrorMessage=None, bRunInShell=False, sApplicationBinaryPath=None, uMaximumNumberOfBugs=2, bExcessiveCPUUsageChecks=False, ): global gbTestFailed if gbTestFailed: return asApplicationArguments = axCommandLineArguments and [ isinstance(x, str) and x or x < 10 and ("%d" % x) or ("0x%X" % x) for x in axCommandLineArguments ] or [] if sApplicationBinaryPath is None: sApplicationBinaryPath = dsBinaries_by_sISA[sISA] asCommandLine = [sApplicationBinaryPath] + asApplicationArguments sFailedToDebugApplicationErrorMessage = None if sExpectedFailedToDebugApplicationErrorMessage: sTestDescription = "%s => %s" % ( "Running %s" % sApplicationBinaryPath, repr(sExpectedFailedToDebugApplicationErrorMessage)) else: sTestDescription = "%s %s%s => %s" % ( sISA, " ".join(asApplicationArguments), \ bRunInShell and " (in child process)" or "", asExpectedBugIdAndLocations and " => ".join(asExpectedBugIdAndLocations) or "no bugs") sTestBinaryName = os.path.basename(sApplicationBinaryPath).lower() if bRunInShell: asApplicationArguments = ["/C", sApplicationBinaryPath ] + asApplicationArguments sApplicationBinaryPath = dsComSpec_by_sISA[sISA] fSetTitle(sTestDescription) if gbDebugStartFinish: fOutput("* Started %s" % sTestDescription) else: fOutput("* %s" % sTestDescription, bCRLF=False) asLog = [] def fCdbStdInInputCallback(oBugId, sInput): if gbShowCdbIO: fOutput("stdin<%s" % sInput) asLog.append("stdin<%s" % sInput) def fCdbStdOutOutputCallback(oBugId, sOutput): if gbShowCdbIO: fOutput("stdout>%s" % sOutput) asLog.append("stdout>%s" % sOutput) def fCdbStdErrOutputCallback(oBugId, sOutput): if gbShowCdbIO: fOutput("stderr>%s" % sOutput) asLog.append("stderr>%s" % sOutput) # asLog.append("log>%s%s" % (sMessage, sData and " (%s)" % sData or "")); def fApplicationDebugOutputCallback(oBugId, oProcess, bIsMainProcess, asOutput): bFirstLine = True for sOutput in asOutput: sLogLine = "%s process %d/0x%X (%s): %s>%s" % (bIsMainProcess and "Main" or "Sub", oProcess.uId, \ oProcess.uId, oProcess.sBinaryName, bFirstLine and "debug" or " ", sOutput) if gbShowApplicationIO: fOutput(sLogLine) asLog.append(sLogLine) bFirstLine = False def fApplicationStdErrOutputCallback(oBugId, oConsoleProcess, bIsMainProcess, sOutput): # This is always a main process sLogLine = "%s process %d/0x%X (%s): stderr> %s" % (bIsMainProcess and "Main" or "Sub", oConsoleProcess.uId, \ oConsoleProcess.uId, oConsoleProcess.sBinaryName, sOutput) if gbShowApplicationIO: fOutput(sLogLine) asLog.append(sLogLine) def fApplicationStdOutOutputCallback(oBugId, oConsoleProcess, bIsMainProcess, sOutput): # This is always a main process sLogLine = "%s process %d/0x%X (%s): stdout> %s" % (bIsMainProcess and "Main" or "Sub", oConsoleProcess.uId, \ oConsoleProcess.uId, oConsoleProcess.sBinaryName, sOutput) if gbShowApplicationIO: fOutput(sLogLine) asLog.append(sLogLine) def fApplicationSuspendedCallback(oBugId, sReason): asLog.append("Application suspended (%s)" % sReason) def fApplicationResumedCallback(oBugId): asLog.append("Application resumed") def fApplicationRunningCallback(oBugId): asLog.append("Application running") def fFailedToDebugApplicationCallback(oBugId, sErrorMessage): global gbTestFailed if sExpectedFailedToDebugApplicationErrorMessage == sErrorMessage: return gbTestFailed = True if not gbShowCdbIO: for sLine in asLog: fOutput(sLine) fOutput("- Failed test: %s" % sTestDescription) if sExpectedFailedToDebugApplicationErrorMessage: fOutput(" Expected: %s" % repr(sExpectedFailedToDebugApplicationErrorMessage)) else: fOutput(" BugId unexpectedly failed to debug the application") fOutput(" Error: %s" % repr(sErrorMessage)) oBugId.fStop() def fFailedToApplyMemoryLimitsCallback(oBugId, oProcess): global gbTestFailed gbTestFailed = True if not gbShowCdbIO: for sLine in asLog: fOutput(sLine) fOutput("- Failed to apply memory limits to process %d/0x%X (%s: %s) for test: %s" % (oProcess.uId, \ oProcess.uId, oProcess.sBinaryName, oProcess.sCommandLine, sTestDescription)) oBugId.fStop() def fFinishedCallback(oBugId): if gbShowCdbIO: fOutput("Finished") asLog.append("Finished") def fLicenseWarningsCallback(oBugId, asLicenseWarnings): global gbLicenseWarningsShown if not gbLicenseWarningsShown: fOutput("@" * 80) for sLicenseWarning in asLicenseWarnings: fOutput("@ %s" % sLicenseWarning) fOutput("@" * 80) gbLicenseWarningsShown = True def fLicenseErrorsCallback(oBugId, asLicenseErrors): fOutput("@" * 80) for sLicenseError in asLicenseErrors: fOutput("@ %s" % sLicenseError) fOutput("@" * 80) os._exit(1) def fInternalExceptionCallback(oBugId, oException, oTraceBack): global gbTestFailed gbTestFailed = True if not gbShowCdbIO: for sLine in asLog: fOutput(sLine) fOutput("@" * 80) fOutput("- An internal exception has occured in test: %s" % sTestDescription) fOutput(" %s" % repr(oException)) fOutput(" Stack:") txStack = traceback.extract_tb(oTraceBack) uFrameIndex = len(txStack) - 1 for (sFileName, uLineNumber, sFunctionName, sCode) in reversed(txStack): sSource = "%s/%d" % (sFileName, uLineNumber) if sFunctionName != "<module>": sSource = "%s (%s)" % (sFunctionName, sSource) fOutput(" %3d %s" % (uFrameIndex, sSource)) if sCode: fOutput(" > %s" % sCode.strip()) uFrameIndex -= 1 fOutput("@" * 80) oBugId.fStop() def fPageHeapNotEnabledCallback(oBugId, oProcess, bIsMainProcess, bPreventable): assert oProcess.sBinaryName == "cmd.exe", \ "It appears you have not enabled page heap for %s, which is required to run tests." % sBinaryName def fProcessAttachedCallback(oBugId, oProcess, bIsMainProcess): asLog.append("%s process %d/0x%X (%s): attached." % (bIsMainProcess and "Main" or "Sub", \ oProcess.uId, oProcess.uId, oProcess.sBinaryName)) def fProcessStartedCallback(oBugId, oConsoleProcess, bIsMainProcess): # This is always a main process asLog.append("%s process %d/0x%X (%s): started." % \ (bIsMainProcess and "Main" or "Sub", oConsoleProcess.uId, oConsoleProcess.uId, oConsoleProcess.sBinaryName)) def fProcessTerminatedCallback(oBugId, oProcess, bIsMainProcess): asLog.append("%s process %d/0x%X (%s): terminated." % (bIsMainProcess and "Main" or "Sub", \ oProcess.uId, oProcess.uId, oProcess.sBinaryName)) def fLogMessageCallback(oBugId, sMessage, dsData=None): sData = dsData and ", ".join( ["%s: %s" % (sName, sValue) for (sName, sValue) in dsData.items()]) sLogLine = "log>%s%s" % (sMessage, sData and " (%s)" % sData or "") if gbShowCdbIO: fOutput(sLogLine) asLog.append(sLogLine) aoBugReports = [] def fBugReportCallback(oBugId, oBugReport): aoBugReports.append(oBugReport) if gbShowCdbIO: fOutput() fOutput("=" * 80) fOutput("%s %s" % (sApplicationBinaryPath, " ".join(asApplicationArguments))) if asExpectedBugIdAndLocations: for sExpectedBugIdAndLocation in asExpectedBugIdAndLocations: fOutput(" => %s" % sExpectedBugIdAndLocation) fOutput("-" * 80) bBugIdStarted = False bBugIdStopped = False try: oBugId = cBugId( sCdbISA= sISA, # Debug with a cdb.exe for an ISA that matches the target process. sApplicationBinaryPath=sApplicationBinaryPath, asApplicationArguments=asApplicationArguments, asSymbolServerURLs=["http://msdl.microsoft.com/download/symbols" ], # Will be ignore if symbols are disabled. bGenerateReportHTML=gbGenerateReportHTML, uTotalMaxMemoryUse=guTotalMaxMemoryUse, uMaximumNumberOfBugs=uMaximumNumberOfBugs, ) oBugId.fAddEventCallback("Application resumed", fApplicationResumedCallback) oBugId.fAddEventCallback("Application running", fApplicationRunningCallback) oBugId.fAddEventCallback("Application debug output", fApplicationDebugOutputCallback) oBugId.fAddEventCallback("Application stderr output", fApplicationStdErrOutputCallback) oBugId.fAddEventCallback("Application stdout output", fApplicationStdOutOutputCallback) oBugId.fAddEventCallback("Application suspended", fApplicationSuspendedCallback) oBugId.fAddEventCallback("Bug report", fBugReportCallback) oBugId.fAddEventCallback("Cdb stderr output", fCdbStdErrOutputCallback) oBugId.fAddEventCallback("Cdb stdin input", fCdbStdInInputCallback) oBugId.fAddEventCallback("Cdb stdout output", fCdbStdOutOutputCallback) oBugId.fAddEventCallback("Failed to apply application memory limits", fFailedToApplyMemoryLimitsCallback) oBugId.fAddEventCallback("Failed to apply process memory limits", fFailedToApplyMemoryLimitsCallback) oBugId.fAddEventCallback("Failed to debug application", fFailedToDebugApplicationCallback) oBugId.fAddEventCallback("Finished", fFinishedCallback) oBugId.fAddEventCallback("Internal exception", fInternalExceptionCallback) oBugId.fAddEventCallback("License warnings", fLicenseWarningsCallback) oBugId.fAddEventCallback("License errors", fLicenseErrorsCallback) oBugId.fAddEventCallback("Page heap not enabled", fPageHeapNotEnabledCallback) oBugId.fAddEventCallback("Process attached", fProcessAttachedCallback) oBugId.fAddEventCallback("Process terminated", fProcessTerminatedCallback) oBugId.fAddEventCallback("Process started", fProcessStartedCallback) oBugId.fAddEventCallback("Log message", fLogMessageCallback) if bExcessiveCPUUsageChecks: def fExcessiveCPUUsageDetectedCallback(oBugId, bExcessiveCPUUsageDetected): if not bExcessiveCPUUsageDetected: oBugId.fCheckForExcessiveCPUUsage( fExcessiveCPUUsageDetectedCallback) oBugId.foSetTimeout( sDescription="Start check for excessive CPU usage", nTimeout=cBugId. dxConfig["nExcessiveCPUUsageCheckInitialTimeout"], fCallback=lambda oBugId: fExcessiveCPUUsageDetectedCallback( oBugId, False), ) oBugId.fStart() bBugIdStarted = True oBugId.fWait() bBugIdStopped = True if gbShowCdbIO: fOutput("= Finished ".ljust(80, "=")) if gbTestFailed: return def fDumpExpectedAndReported(): uCounter = 0 while 1: sExpectedBugIdAndLocation = uCounter < len( asExpectedBugIdAndLocations ) and asExpectedBugIdAndLocations[uCounter] oBugReport = uCounter < len( aoBugReports) and aoBugReports[uCounter] if not sExpectedBugIdAndLocation and not oBugReport: break uCounter += 1 fOutput(" Expected #%d: %s" % (uCounter, sExpectedBugIdAndLocation)) fOutput(" Reported : %s" % (oBugReport and "%s @ %s" % (oBugReport.sId, oBugReport.sBugLocation))) if oBugReport: fOutput(" %s" % oBugReport.sBugDescription) if sExpectedFailedToDebugApplicationErrorMessage: pass elif asExpectedBugIdAndLocations is None: uCounter = 0 for oBugReport in aoBugReports: uCounter += 1 sBugIdAndLocation = "%s @ %s" % (oBugReport.sId, oBugReport.sBugLocation) fOutput("* Test result for: %s" % sTestDescription) fOutput(" Test bug #%d: %s." % (uCounter, sBugIdAndLocation)) elif asExpectedBugIdAndLocations: if len(aoBugReports) != len(asExpectedBugIdAndLocations): gbTestFailed = True if not gbShowCdbIO: for sLine in asLog: fOutput(sLine) fOutput("- Failed test: %s" % sTestDescription) fOutput( " Test reported %d instead of %d bugs in the application." % (len(aoBugReports), len(asExpectedBugIdAndLocations))) fDumpExpectedAndReported() else: uCounter = 0 for oBugReport in aoBugReports: sExpectedBugIdAndLocation = asExpectedBugIdAndLocations[ uCounter] uCounter += 1 sBugIdAndLocation = "%s @ %s" % (oBugReport.sId, oBugReport.sBugLocation) if sExpectedBugIdAndLocation[ 0] == "*": # string contains a regular expression # Remove "*" and insert (escaped) test binary name in location sExpectedBugIdAndLocation = "^(%s)$" % sExpectedBugIdAndLocation[ 1:].replace("<test-binary>", re.escape(sTestBinaryName)) bSuccess = re.match(sExpectedBugIdAndLocation, sBugIdAndLocation) else: sExpectedBugIdAndLocation = sExpectedBugIdAndLocation.replace( "<test-binary>", sTestBinaryName) bSuccess = sBugIdAndLocation == sExpectedBugIdAndLocation if not bSuccess: gbTestFailed = True if not gbShowCdbIO: for sLine in asLog: fOutput(sLine) fOutput("- Failed test: %s" % sTestDescription) fOutput(" Test bug #%d does not match %s." % (uCounter, sExpectedBugIdAndLocation)) fDumpExpectedAndReported() break if gbSaveReportHTML: for oBugReport in aoBugReports: # We'd like a report file name base on the BugId, but the later may contain characters that are not valid in a file name sDesiredReportFileName = "%s %s @ %s.html" % ( sPythonISA, oBugReport.sId, oBugReport.sBugLocation) # Thus, we need to translate these characters to create a valid filename that looks very similar to the BugId sValidReportFileName = mFileSystem.fsValidName( sDesiredReportFileName, bUnicode=False) sReportsFilePath = mFileSystem.fsPath(sReportsFolderName, sValidReportFileName) mFileSystem.fWriteDataToFile( oBugReport.sReportHTML, sReportsFilePath, fbRetryOnFailure=lambda: False, ) fOutput(" Wrote report: %s" % sReportsFilePath) except Exception, oException: if bBugIdStarted and not bBugIdStopped: oBugId.fTerminate() fOutput("- Failed test: %s" % sTestDescription) fOutput(" Exception: %s" % repr(oException)) raise
def fMain(asArguments): global \ gasAttachToProcessesForExecutableNames, \ gasBinaryNamesThatAreAllowedToRunWithoutPageHeap, \ gbQuiet, \ gbVerbose, \ guDetectedBugsCount, \ guMaximumNumberOfBugs; if len(asArguments) == 0: fPrintLogo(); fPrintUsage(ddxApplicationSettings_by_sKeyword.keys()); os._exit(0); # Parse all arguments until we encounter "--". sApplicationKeyword = None; sApplicationBinaryPath = None; auApplicationProcessIds = []; sUWPApplicationPackageName = None; sUWPApplicationId = None; asApplicationOptionalArguments = None; sApplicationISA = None; bRepeat = False; bCheckForUpdates = False; dxUserProvidedConfigSettings = {}; bFast = False; while asArguments: sArgument = asArguments.pop(0); if sArgument == "--": if len(auApplicationProcessIds) > 0: # The rest of the arguments are to be passed to the application oConsole.fPrint(ERROR, "- You cannot provide process ids and application arguments."); os._exit(2); asApplicationOptionalArguments = asArguments; break; elif sArgument in ["-q", "/q"]: gbQuiet = True; elif sArgument in ["-v", "/v"]: gbVerbose = True; elif sArgument in ["-f", "/f"]: bFast = True; elif sArgument in ["-r", "/r"]: bRepeat = True; elif sArgument in ["-c", "/c"]: guMaximumNumberOfBugs = guDefaultCollateralMaximumNumberOfBugs; elif sArgument in ["-?", "/?", "-h", "/h"]: fPrintLogo(); fPrintUsage(ddxApplicationSettings_by_sKeyword.keys()); os._exit(0); elif sArgument.startswith("--"): if "=" in sArgument: sSettingName, sValue = sArgument[2:].split("=", 1); else: # "--bFlag" is an alias for "--bFlag=true" sSettingName = sArgument[2:]; sValue = None; if sSettingName in ["pid", "pids"]: if not sValue: oConsole.fPrint(ERROR, "- You must provide at least one process id."); os._exit(2); if sApplicationBinaryPath is not None: oConsole.fPrint(ERROR, "- You cannot provide an application binary and process ids."); os._exit(2); if sUWPApplicationPackageName is not None: oConsole.fPrint(ERROR, "- You cannot provide an UWP application package name and process ids."); os._exit(2); auApplicationProcessIds += [long(x) for x in sValue.split(",")]; elif sSettingName in ["uwp", "uwp-app"]: if not sValue: oConsole.fPrint(ERROR, "- You must provide an UWP application package name."); os._exit(2); if sUWPApplicationPackageName is not None: oConsole.fPrint(ERROR, "- You cannot provide multiple UWP application package names."); os._exit(2); if sApplicationBinaryPath is not None: oConsole.fPrint(ERROR, "- You cannot provide an application binary and UWP package name."); os._exit(2); if len(auApplicationProcessIds) > 0: oConsole.fPrint(ERROR, "- You cannot provide process ids and an UWP application package name."); os._exit(2); if "!" not in sValue: oConsole.fPrint(ERROR, "- Please provide a string of the form ", ERROR_INFO, sSettingName, \ "=<package name>!<application id>."); os._exit(2); sUWPApplicationPackageName, sUWPApplicationId = sValue.split("!", 1); elif sSettingName in ["help"]: fPrintLogo(); fPrintUsage(ddxApplicationSettings_by_sKeyword.keys()); os._exit(0); elif sSettingName in ["version", "check-for-updates"]: fVersionCheck(); os._exit(0); elif sSettingName in ["isa", "cpu"]: if not sValue: oConsole.fPrint(ERROR, "- You must provide an Instruction Set Architecture."); os._exit(2); if sValue not in ["x86", "x64"]: oConsole.fPrint(ERROR, "- Unknown Instruction Set Architecture ", repr(sValue)); os._exit(2); sApplicationISA = sValue; elif sSettingName in ["quiet", "silent"]: if sValue is None or sValue.lower() == "true": gbQuiet = True; elif sValue.lower() == "false": gbQuiet = False; else: oConsole.fPrint(ERROR, "- The value for ", ERROR_INFO, "--", sSettingName, ERROR, \ " must be \"true\" or \"false\"."); elif sSettingName in ["verbose", "debug"]: if sValue is None or sValue.lower() == "true": gbVerbose = True; elif sValue.lower() == "false": gbVerbose = False; else: oConsole.fPrint(ERROR, "- The value for ", ERROR_INFO, "--", sSettingName, ERROR, \ " must be \"true\" or \"false\"."); elif sSettingName in ["fast", "quick"]: if sValue is None or sValue.lower() == "true": bFast = True; elif sValue.lower() == "false": bFast = False; else: oConsole.fPrint(ERROR, "- The value for ", ERROR_INFO, "--", sSettingName, ERROR, \ " must be \"true\" or \"false\"."); elif sSettingName in ["repeat", "forever"]: if sValue is None or sValue.lower() == "true": bRepeat = True; elif sValue.lower() == "false": bRepeat = False; else: oConsole.fPrint(ERROR, "- The value for ", ERROR_INFO, "--", sSettingName, ERROR, \ " must be \"true\" or \"false\"."); elif sSettingName in ["collateral"]: if sValue is None: guMaximumNumberOfBugs = guDefaultCollateralMaximumNumberOfBugs; else: guMaximumNumberOfBugs = long(sValue); elif sSettingName in ["test-internal-error", "internal-error-test"]: raise Exception("Testing internal error"); else: if not sValue: oConsole.fPrint(ERROR, "- You cannot provide an argument (", ERROR_INFO, "--", sSettingName, ERROR, \ ") without a value."); os._exit(2); try: xValue = json.loads(sValue); except ValueError as oError: oConsole.fPrint(ERROR, "- Cannot decode argument JSON value ", ERROR_INFO, "--", sSettingName, "=", sValue, \ ERROR, ": ", ERROR_INFO, " ".join(oError.args), ERROR, "."); os._exit(2); # User provided config settings must be applied after any keyword specific config settings: dxUserProvidedConfigSettings[sSettingName] = xValue; elif sArgument in ddxApplicationSettings_by_sKeyword: if sApplicationKeyword is not None: oConsole.fPrint(ERROR, "- You cannot provide multiple application keywords."); os._exit(2); sApplicationKeyword = sArgument; elif sArgument[-1] == "?": sApplicationKeyword = sArgument[:-1]; dxApplicationSettings = ddxApplicationSettings_by_sKeyword.get(sApplicationKeyword); if not dxApplicationSettings: oConsole.fPrint(ERROR, "- Unknown application keyword ", ERROR_INFO, sApplicationKeyword, ERROR, "."); os._exit(2); fPrintApplicationKeyWordHelp(sApplicationKeyword, dxApplicationSettings); os._exit(0); else: if sApplicationBinaryPath is not None: oConsole.fLock(); try: oConsole.fPrint(ERROR, "- You cannot provide multiple application binaries."); oConsole.fPrint(ERROR, " (Did you perhaps forget to put ", ERROR_INFO, "--", ERROR, \ " before the start of the application arguments?)"); finally: oConsole.fUnlock(); os._exit(2); if len(auApplicationProcessIds) > 0: oConsole.fPrint(ERROR, "- You cannot provide process ids and an application binary."); os._exit(2); if sUWPApplicationPackageName is not None: oConsole.fPrint(ERROR, "- You cannot provide an application UWP package name and a binary."); os._exit(2); sApplicationBinaryPath = sArgument; if bFast: gbQuiet = True; dxUserProvidedConfigSettings["bGenerateReportHTML"] = False; dxUserProvidedConfigSettings["asSymbolServerURLs"] = []; dxUserProvidedConfigSettings["cBugId.bUse_NT_SYMBOL_PATH"] = False; dsApplicationURLTemplate_by_srSourceFilePath = {}; fCleanup = None; if sApplicationKeyword: dxApplicationSettings = ddxApplicationSettings_by_sKeyword.get(sApplicationKeyword); if not dxApplicationSettings: oConsole.fPrint(ERROR, "- Unknown application keyword ", ERROR_INFO, sApplicationKeyword, ERROR, "."); os._exit(2); fCheckApplication = dxApplicationSettings.get("fCheckApplication"); if fCheckApplication: fCheckApplication(); # This should terminate BugId with the relevant exit code if needed. fCleanup = dxApplicationSettings.get("fCleanup"); # Get application binary/UWP package name/process ids as needed: if "sBinaryPath" in dxApplicationSettings: # This application is started from the command-line. if auApplicationProcessIds: oConsole.fPrint(ERROR, "- You cannot provide process ids for application keyword ", ERROR_INFO, \ sApplicationKeyword, ERROR, "."); os._exit(2); if sUWPApplicationPackageName: oConsole.fPrint(ERROR, "- You cannot provide an application UWP package name for application keyword ", \ ERROR_INFO, sApplicationKeyword, ERROR, "."); os._exit(2); if sApplicationBinaryPath is None: sApplicationBinaryPath = dxApplicationSettings["sBinaryPath"]; if sApplicationBinaryPath is None: oConsole.fPrint(ERROR, "- The main application binary for ", ERROR_INFO, sApplicationKeyword, \ ERROR, " could not be detected on your system."); oConsole.fPrint(ERROR, " Please provide the path to this binary in the arguments."); os._exit(4); elif "dxUWPApplication" in dxApplicationSettings: dxUWPApplication = dxApplicationSettings["dxUWPApplication"]; # This application is started as a Universal Windows Platform application. if sApplicationBinaryPath: oConsole.fPrint(ERROR, "- You cannot provide an application binary for application keyword ", \ ERROR_INFO, sApplicationKeyword, ERROR, "."); os._exit(2); if auApplicationProcessIds: oConsole.fPrint(ERROR, "- You cannot provide process ids for application keyword ", ERROR_INFO, \ sApplicationKeyword, ERROR, "."); os._exit(2); sUWPApplicationPackageName = dxUWPApplication["sPackageName"]; sUWPApplicationId = dxUWPApplication["sId"]; elif not auApplicationProcessIds: # This application is attached to. oConsole.fPrint(ERROR, "- You must provide process ids for application keyword ", \ ERROR_INFO, sApplicationKeyword, ERROR, "."); os._exit(2); elif asApplicationOptionalArguments: # Cannot provide arguments if we're attaching to processes oConsole.fPrint(ERROR, "- You cannot provide arguments for application keyword ", \ ERROR_INFO, sApplicationKeyword, ERROR, "."); os._exit(2); if "asApplicationAttachToProcessesForExecutableNames" in dxApplicationSettings: gasAttachToProcessesForExecutableNames = dxApplicationSettings["asApplicationAttachToProcessesForExecutableNames"]; # Get application arguments; if "fasGetStaticArguments" in dxApplicationSettings: fasGetApplicationStaticArguments = dxApplicationSettings["fasGetStaticArguments"]; asApplicationStaticArguments = fasGetApplicationStaticArguments(bForHelp = False); else: asApplicationStaticArguments = []; if asApplicationOptionalArguments is None and "fasGetOptionalArguments" in dxApplicationSettings: fasGetApplicationOptionalArguments = dxApplicationSettings["fasGetOptionalArguments"]; asApplicationOptionalArguments = fasGetApplicationOptionalArguments(bForHelp = False); asApplicationArguments = asApplicationStaticArguments + asApplicationOptionalArguments; # Apply application specific settings if dxApplicationSettings.get("dxConfigSettings"): dxApplicationConfigSettings = dxApplicationSettings["dxConfigSettings"]; if gbVerbose: oConsole.fPrint("* Applying application specific configuration for %s:" % sApplicationKeyword); for (sSettingName, xValue) in dxApplicationConfigSettings.items(): if sSettingName not in dxUserProvidedConfigSettings: fApplyConfigSetting(sSettingName, xValue, [None, " "][gbVerbose]); # Apply and show result indented. if gbVerbose: oConsole.fPrint(); # Apply application specific source settings if "dsURLTemplate_by_srSourceFilePath" in dxApplicationSettings: dsApplicationURLTemplate_by_srSourceFilePath = dxApplicationSettings["dsURLTemplate_by_srSourceFilePath"]; # If not ISA is specified, apply the application specific ISA (if any). if not sApplicationISA and "sISA" in dxApplicationSettings: sApplicationISA = dxApplicationSettings["sISA"]; if "asBinaryNamesThatAreAllowedToRunWithoutPageHeap" in dxApplicationSettings: gasBinaryNamesThatAreAllowedToRunWithoutPageHeap = [ sBinaryName.lower() for sBinaryName in dxApplicationSettings["asBinaryNamesThatAreAllowedToRunWithoutPageHeap"] ]; elif (auApplicationProcessIds or sUWPApplicationPackageName or sApplicationBinaryPath): # There are no static arguments if there is no application keyword, only the user-supplied optional arguments # are used if they are supplied: asApplicationArguments = asApplicationOptionalArguments or []; else: oConsole.fLock(); try: oConsole.fPrint(ERROR, "- You must provide something to debug. This can be either one or more process"); oConsole.fPrint(ERROR, " ids, an application command-line or an UWP application package name."); oConsole.fPrint("Run \"", INFO, "BugId -h", NORMAL, "\" for help on command-line arguments."); finally: oConsole.fUnlock(); os._exit(2); # Apply user provided settings: for (sSettingName, xValue) in dxUserProvidedConfigSettings.items(): fApplyConfigSetting(sSettingName, xValue, [None, ""][gbVerbose]); # Apply and show result if bRepeat: duNumberOfRepros_by_sBugIdAndLocation = {}; sValidStatisticsFileName = mFileSystem.fsValidName("Reproduction statistics.txt"); uRunCounter = 0; while 1: # Will only loop if bRepeat is True nStartTime = time.clock(); if fCleanup and dxConfig["bCleanup"]: oConsole.fStatus("* Cleaning up application state..."); fCleanup(); uRunCounter += 1; oConsole.fLock(); try: if sApplicationBinaryPath: if not gbQuiet: asCommandLine = [sApplicationBinaryPath] + asApplicationArguments; oConsole.fPrint("* Command line: ", INFO, " ".join(asCommandLine)); oConsole.fStatus("* The debugger is starting the application..."); else: if auApplicationProcessIds: asProcessIdsOutput = []; for uApplicationProcessId in auApplicationProcessIds: if asProcessIdsOutput: asProcessIdsOutput.append(", "); asProcessIdsOutput.extend([INFO, str(uApplicationProcessId), NORMAL]); oConsole.fPrint("* Running process ids: ", INFO, *asProcessIdsOutput); if sUWPApplicationPackageName: if not gbQuiet: if asApplicationArguments: oConsole.fPrint("* UWP application id: ", INFO, sUWPApplicationId, NORMAL, ", package name: ", INFO, \ sUWPApplicationPackageName, NORMAL, ", Arguments: ", INFO, " ".join(asApplicationArguments)); else: oConsole.fPrint("* UWP application id: ", INFO, sUWPApplicationId, NORMAL, ", package name: ", INFO, \ sUWPApplicationPackageName); if not sUWPApplicationPackageName: oConsole.fStatus("* The debugger is attaching to running processes of the application..."); elif auApplicationProcessIds: oConsole.fStatus("* The debugger is attaching to running processes and starting the application..."); else: oConsole.fStatus("* The debugger is starting the application..."); finally: oConsole.fUnlock(); oBugId = cBugId( sCdbISA = sApplicationISA or cBugId.sOSISA, sApplicationBinaryPath = sApplicationBinaryPath or None, auApplicationProcessIds = auApplicationProcessIds or None, sUWPApplicationPackageName = sUWPApplicationPackageName or None, sUWPApplicationId = sUWPApplicationId or None, asApplicationArguments = asApplicationArguments, asLocalSymbolPaths = dxConfig["asLocalSymbolPaths"], asSymbolCachePaths = dxConfig["asSymbolCachePaths"], asSymbolServerURLs = dxConfig["asSymbolServerURLs"], dsURLTemplate_by_srSourceFilePath = dsApplicationURLTemplate_by_srSourceFilePath, bGenerateReportHTML = dxConfig["bGenerateReportHTML"], uProcessMaxMemoryUse = dxConfig["uProcessMaxMemoryUse"], uTotalMaxMemoryUse = dxConfig["uTotalMaxMemoryUse"], uMaximumNumberOfBugs = guMaximumNumberOfBugs, ); oBugId.fAddEventCallback("Failed to debug application", fFailedToDebugApplicationCallback); oBugId.fAddEventCallback("Failed to apply application memory limits", fFailedToApplyApplicationMemoryLimitsCallback); oBugId.fAddEventCallback("Failed to apply process memory limits", fFailedToApplyProcessMemoryLimitsCallback); oBugId.fAddEventCallback("Application running", fApplicationRunningCallback); oBugId.fAddEventCallback("Application suspended", fApplicationSuspendedCallback); oBugId.fAddEventCallback("Application resumed", fApplicationResumedCallback); oBugId.fAddEventCallback("Process terminated", fProcessTerminatedCallback); oBugId.fAddEventCallback("Internal exception", fInternalExceptionCallback); oBugId.fAddEventCallback("Page heap not enabled", fPageHeapNotEnabledCallback); if gbVerbose: oBugId.fAddEventCallback("Cdb stdin input", fCdbStdInInputCallback); oBugId.fAddEventCallback("Cdb stdout output", fCdbStdOutOutputCallback); oBugId.fAddEventCallback("Cdb stderr output", fCdbStdErrOutputCallback); oBugId.fAddEventCallback("Started process", fStartedProcessCallback); oBugId.fAddEventCallback("Attached to process", fAttachedToProcessCallback); oBugId.fAddEventCallback("Application stdout output", fApplicationStdOutOutputCallback); oBugId.fAddEventCallback("Application stderr output", fApplicationStdOutOutputCallback); oBugId.fAddEventCallback("Bug report", fBugReportCallback); if dxConfig["nApplicationMaxRunTime"] is not None: oBugId.foSetTimeout("Maximum application runtime", dxConfig["nApplicationMaxRunTime"], \ fApplicationMaxRunTimeCallback); if dxConfig["bExcessiveCPUUsageCheckEnabled"] and dxConfig["nExcessiveCPUUsageCheckInitialTimeout"]: oBugId.fSetCheckForExcessiveCPUUsageTimeout(dxConfig["nExcessiveCPUUsageCheckInitialTimeout"]); guDetectedBugsCount = 0; oBugId.fStart(); oBugId.fWait(); if gbAnErrorOccured: os._exit(3); if guDetectedBugsCount == 0: oConsole.fPrint(u"\u2500\u2500 The application terminated without a bug being detected ", sPadding = u"\u2500"); sBugIdAndLocation = "No crash"; if gbVerbose: oConsole.fPrint(" Application time: %s seconds" % (long(oBugId.fnApplicationRunTime() * 1000) / 1000.0)); nOverheadTime = time.clock() - nStartTime - oBugId.fnApplicationRunTime(); oConsole.fPrint(" BugId overhead: %s seconds" % (long(nOverheadTime * 1000) / 1000.0)); if not bRepeat: os._exit(guDetectedBugsCount > 0 and 1 or 0); duNumberOfRepros_by_sBugIdAndLocation.setdefault(sBugIdAndLocation, 0) duNumberOfRepros_by_sBugIdAndLocation[sBugIdAndLocation] += 1; sStatistics = ""; auOrderedNumberOfRepros = sorted(list(set(duNumberOfRepros_by_sBugIdAndLocation.values()))); auOrderedNumberOfRepros.reverse(); for uNumberOfRepros in auOrderedNumberOfRepros: for sBugIdAndLocation in duNumberOfRepros_by_sBugIdAndLocation.keys(): if duNumberOfRepros_by_sBugIdAndLocation[sBugIdAndLocation] == uNumberOfRepros: sStatistics += "%d \xD7 %s (%d%%)\r\n" % (uNumberOfRepros, str(sBugIdAndLocation), \ round(100.0 * uNumberOfRepros / uRunCounter)); if dxConfig["sReportFolderPath"] is not None: sStatisticsFilePath = mFileSystem.fsPath(dxConfig["sReportFolderPath"], sValidStatisticsFileName); else: sStatisticsFilePath = mFileSystem.fsPath(sValidStatisticsFileName); eWriteDataToFileResult = mFileSystem.feWriteDataToFile( sStatistics, sStatisticsFilePath, fbRetryOnFailure = lambda: False, ); if eWriteDataToFileResult: oConsole.fPrint(" Statistics: ", ERROR, "Cannot be saved (", ERROR_INFO, repr(eWriteDataToFileResult), \ ERROR, ")"); else: oConsole.fPrint(" Statistics: ", INFO, sStatisticsFilePath, NORMAL, " (%d bytes)" % len(sStatistics)); oConsole.fPrint(); # and loop
r"\\x\y\z": r"\\?\UNC\x\y\z", r"\\?\UNC\x\y\z": r"\\?\UNC\x\y\z", } for (sInput, sExpectedOutput) in dsPathTests.items(): sOutput = mFileSystem.fsPath(sInput) assert sOutput == sExpectedOutput, \ "fsPath error for %s: got %s, expected %s" % (sInput, sOutput, sExpectedOutput) sInput += "\\" sOutput = mFileSystem.fsPath(sInput) assert sOutput == sExpectedOutput, \ "fsPath error for %s: got %s, expected %s" % (sInput, sOutput, sExpectedOutput) for bUnicode in [True, False]: print "* Testing file operations with special characters in %s mode..." % ( bUnicode and "Unicode" or "ASCII") sTranslatedSpecialChars = mFileSystem.fsValidName(sSpecialChars, bUnicode=bUnicode) print u" File/folder name: %s" % sTranslatedSpecialChars sData = "x" # Clean old files/folders print " * Cleaning up old files/folders..." print " * Checking if old test file exists..." ebIsFileResult = mFileSystem.febIsFile(sTempFolderPath, sTranslatedSpecialChars) assert isinstance( ebIsFileResult, bool), "Expected febIsFile result to be a boolean, got %s" % repr( ebIsFileResult) if ebIsFileResult: print " * Deleting old test file..." ebDeleteFileResult = mFileSystem.febDeleteFile(
def fuMain(asArguments): global gbVerbose, gbQuiet, gasAttachToProcessesForExecutableNames; if len(asArguments) == 0: fPrintLogo(); fPrintUsage(asApplicationKeywords); return 0; # Parse all arguments until we encounter "--". sApplicationKeyword = None; sApplicationBinaryPath = None; auApplicationProcessIds = []; sUWPApplicationPackageName = None; sUWPApplicationId = None; asApplicationOptionalArguments = None; sApplicationISA = None; bRepeat = False; bCheckForUpdates = False; dxUserProvidedConfigSettings = {}; bFast = False; while asArguments: sArgument = asArguments.pop(0); if sArgument == "--": if len(auApplicationProcessIds) > 0: # The rest of the arguments are to be passed to the application oConsole.fPrint(ERROR, "- You cannot provide process ids and application arguments."); return 2; asApplicationOptionalArguments = asArguments; break; elif sArgument in ["-q", "/q"]: gbQuiet = True; elif sArgument in ["-v", "/v"]: gbVerbose = True; elif sArgument in ["-f", "/f"]: bFast = True; elif sArgument in ["-r", "/r"]: bRepeat = True; elif sArgument in ["-?", "/?", "-h", "/h"]: fPrintLogo(); fPrintUsage(asApplicationKeywords); return 0; elif sArgument.startswith("--"): if "=" in sArgument: sSettingName, sValue = sArgument[2:].split("=", 1); else: # "--bFlag" is an alias for "--bFlag=true" sSettingName = sArgument[2:]; sValue = None; if sSettingName in ["pid", "pids"]: if not sValue: oConsole.fPrint(ERROR, "- You must provide at least one process id."); return 2; if sApplicationBinaryPath is not None: oConsole.fPrint(ERROR, "- You cannot provide an application binary and process ids."); return 2; if sUWPApplicationPackageName is not None: oConsole.fPrint(ERROR, "- You cannot provide an UWP application package name and process ids."); return 2; auApplicationProcessIds += [long(x) for x in sValue.split(",")]; elif sSettingName in ["uwp", "uwp-app"]: if not sValue: oConsole.fPrint(ERROR, "- You must provide an UWP application package name."); return 2; if sUWPApplicationPackageName is not None: oConsole.fPrint(ERROR, "- You cannot provide multiple UWP application package names."); return 2; if sApplicationBinaryPath is not None: oConsole.fPrint(ERROR, "- You cannot provide an application binary and UWP package name."); return 2; if len(auApplicationProcessIds) > 0: oConsole.fPrint(ERROR, "- You cannot provide process ids and an UWP application package name."); return 2; if "!" not in sValue: oConsole.fPrint(ERROR, "- Please provide a string of the form ", ERROR_INFO, sSettingName, "=<package name>!<application id>."); return 2; sUWPApplicationPackageName, sUWPApplicationId = sValue.split("!", 1); elif sSettingName in ["version", "check-for-updates"]: fVersionCheck(); return 0; elif sSettingName in ["isa", "cpu"]: if not sValue: oConsole.fPrint(ERROR, "- You must provide an Instruction Set Architecture."); return 2; if sValue not in ["x86", "x64"]: oConsole.fPrint(ERROR, "- Unknown Instruction Set Architecture ", repr(sValue)); return 2; sApplicationISA = sValue; elif sSettingName in ["quiet", "silent"]: if sValue is None or sValue.lower() == "true": gbQuiet = True; elif sValue.lower() == "false": gbQuiet = False; else: oConsole.fPrint(ERROR, "- The value for ", ERROR_INFO, "--", sSettingName, ERROR, " must be \"true\" or \"false\"."); elif sSettingName in ["verbose", "debug"]: if sValue is None or sValue.lower() == "true": gbVerbose = True; elif sValue.lower() == "false": gbVerbose = False; else: oConsole.fPrint(ERROR, "- The value for ", ERROR_INFO, "--", sSettingName, ERROR, " must be \"true\" or \"false\"."); elif sSettingName in ["fast", "quick"]: if sValue is None or sValue.lower() == "true": bFast = True; elif sValue.lower() == "false": bFast = False; else: oConsole.fPrint(ERROR, "- The value for ", ERROR_INFO, "--", sSettingName, ERROR, " must be \"true\" or \"false\"."); elif sSettingName in ["repeat", "forever"]: if sValue is None or sValue.lower() == "true": bRepeat = True; elif sValue.lower() == "false": bRepeat = False; else: oConsole.fPrint(ERROR, "- The value for ", ERROR_INFO, "--", sSettingName, ERROR, " must be \"true\" or \"false\"."); elif sSettingName in ["test-internal-error", "internal-error-test"]: raise Exception("Testing internal error"); else: try: xValue = json.loads(sValue); except ValueError as oError: oConsole.fPrint(ERROR, "- Cannot decode argument JSON value ", ERROR_INFO, sValue, ERROR, ": ", \ ERROR_INFO, " ".join(oError.args), ERROR, "."); return 2; # User provided config settings must be applied after any keyword specific config settings: dxUserProvidedConfigSettings[sSettingName] = xValue; elif sArgument in asApplicationKeywords: if sApplicationKeyword is not None: oConsole.fPrint(ERROR, "- You cannot provide multiple application keywords."); return 2; sApplicationKeyword = sArgument; elif sArgument[-1] == "?" and sArgument[:-1] in asApplicationKeywords: return fuShowApplicationKeyWordHelp(sArgument[:-1]); else: if sApplicationBinaryPath is not None: oConsole.fLock(); try: oConsole.fPrint(ERROR, "- You cannot provide multiple application binaries."); oConsole.fPrint(ERROR, " (Did you perhaps forget to put ", ERROR_INFO, "--", ERROR, " before the start of the application arguments?)"); finally: oConsole.fUnlock(); return 2; if len(auApplicationProcessIds) > 0: oConsole.fPrint(ERROR, "- You cannot provide process ids and an application binary."); return 2; if sUWPApplicationPackageName is not None: oConsole.fPrint(ERROR, "- You cannot provide an application UWP package name and a binary."); return 2; sApplicationBinaryPath = sArgument; if bFast: gbQuiet = True; dxUserProvidedConfigSettings["bGenerateReportHTML"] = False; dxUserProvidedConfigSettings["asSymbolServerURLs"] = []; dxUserProvidedConfigSettings["cBugId.bUse_NT_SYMBOL_PATH"] = False; dsURLTemplate_by_srSourceFilePath = {}; rImportantStdOutLines = None; rImportantStdErrLines = None; if sApplicationKeyword: fCleanup = dxConfig["bCleanup"] and gdfCleanup_by_sKeyword.get(sApplicationKeyword) or None; # Get application binary/UWP package name/process ids as needed: if sApplicationKeyword in gdApplication_sBinaryPath_by_sKeyword: # This application is started from the command-line. if auApplicationProcessIds: oConsole.fPrint(ERROR, "- You cannot provide process ids for application keyword ", ERROR_INFO, \ sApplicationKeyword, ERROR, "."); return 2; if sUWPApplicationPackageName: oConsole.fPrint(ERROR, "- You cannot provide an application UWP package name for application keyword ", \ ERROR_INFO, sApplicationKeyword, ERROR, "."); return 2; if sApplicationBinaryPath is None: sApplicationBinaryPath = gdApplication_sBinaryPath_by_sKeyword[sApplicationKeyword]; if sApplicationBinaryPath is None: oConsole.fPrint(ERROR, "- The main application binary for ", ERROR_INFO, sApplicationKeyword, \ ERROR, " could not be detected on your system."); oConsole.fPrint(ERROR, " Please provide the path to this binary in the arguments."); return 4; elif sApplicationKeyword in gsUWPApplicationPackageName_by_sKeyword: # This application is started as an application package. if sApplicationBinaryPath: oConsole.fPrint(ERROR, "- You cannot provide an application binary for application keyword ", \ ERROR_INFO, sApplicationKeyword, ERROR, "."); return 2; sUWPApplicationPackageName = gsUWPApplicationPackageName_by_sKeyword[sApplicationKeyword]; sUWPApplicationId = gsUWPApplicationId_by_sKeyword[sApplicationKeyword]; elif not auApplicationProcessIds: # This application is attached to. oConsole.fPrint(ERROR, "- You must provide process ids for application keyword ", \ ERROR_INFO, sApplicationKeyword, ERROR, "."); return 2; elif asApplicationOptionalArguments: # Cannot provide arguments if we're attaching to processes oConsole.fPrint(ERROR, "- You cannot provide arguments for application keyword ", \ ERROR_INFO, sApplicationKeyword, ERROR, "."); return 2; if sApplicationKeyword in gasApplicationAttachToProcessesForExecutableNames_by_sKeyword: gasAttachToProcessesForExecutableNames = gasApplicationAttachToProcessesForExecutableNames_by_sKeyword[sApplicationKeyword]; # Get application arguments; fasGetApplicationStaticArguments = gdApplication_fasGetStaticArguments_by_sKeyword.get(sApplicationKeyword, None); asApplicationStaticArguments = fasGetApplicationStaticArguments and fasGetApplicationStaticArguments(bForHelp = False) or []; if asApplicationOptionalArguments is None: asApplicationOptionalArguments = [ sArgument is DEFAULT_BROWSER_TEST_URL and dxConfig["sDefaultBrowserTestURL"] or sArgument for sArgument in gdApplication_asDefaultOptionalArguments_by_sKeyword.get(sApplicationKeyword, []) ]; asApplicationArguments = asApplicationStaticArguments + asApplicationOptionalArguments; # Apply application specific settings if sApplicationKeyword in gdApplication_dxSettings_by_sKeyword: if gbVerbose: oConsole.fPrint("* Applying application specific configuration for %s:" % sApplicationKeyword); for (sSettingName, xValue) in gdApplication_dxSettings_by_sKeyword[sApplicationKeyword].items(): if sSettingName not in dxUserProvidedConfigSettings: fApplyConfigSetting(sSettingName, xValue, " "); # Apply and show result indented. if gbVerbose: oConsole.fPrint(); # Apply application specific source settings if sApplicationKeyword in gdApplication_sURLTemplate_by_srSourceFilePath_by_sKeyword: dsURLTemplate_by_srSourceFilePath = gdApplication_sURLTemplate_by_srSourceFilePath_by_sKeyword[sApplicationKeyword]; # Apply application specific stdio settings: if sApplicationKeyword in gdApplication_rImportantStdOutLines_by_sKeyword: rImportantStdOutLines = gdApplication_rImportantStdOutLines_by_sKeyword[sApplicationKeyword]; if sApplicationKeyword in gdApplication_rImportantStdErrLines_by_sKeyword: rImportantStdErrLines = gdApplication_rImportantStdErrLines_by_sKeyword[sApplicationKeyword]; if not sApplicationISA and sApplicationKeyword in gdApplication_sISA_by_sKeyword: # Apply application specific ISA sApplicationISA = gdApplication_sISA_by_sKeyword[sApplicationKeyword]; elif (auApplicationProcessIds or sUWPApplicationPackageName or sApplicationBinaryPath): fCleanup = None; # There are no static arguments if there is no application keyword, only the user-supplied optional arguments # are used if they are supplied: asApplicationArguments = asApplicationOptionalArguments or []; else: oConsole.fLock(); try: oConsole.fPrint(ERROR, "- You must provide something to debug. This can be either one or more process"); oConsole.fPrint(ERROR, " ids, an application command-line or an UWP application package name."); oConsole.fPrint("Run \"", INFO, "BugId -h", NORMAL, "\" for help on command-line arguments."); finally: oConsole.fUnlock(); return 2; # Apply user provided settings: for (sSettingName, xValue) in dxUserProvidedConfigSettings.items(): fApplyConfigSetting(sSettingName, xValue, ""); # Apply and show result if bRepeat: duNumberOfRepros_by_sBugIdAndLocation = {}; sValidStatisticsFileName = mFileSystem.fsValidName("Reproduction statistics.txt"); uRunCounter = 0; while 1: # Will only loop if bRepeat is True nStartTime = time.clock(); if fCleanup is not None: oConsole.fStatus("* Cleaning up application state..."); fCleanup(); uRunCounter += 1; oConsole.fLock(); try: if sApplicationBinaryPath: if not gbQuiet: asCommandLine = [sApplicationBinaryPath] + asApplicationArguments; oConsole.fPrint("* Command line: ", INFO, " ".join(asCommandLine)); oConsole.fStatus("* The debugger is starting the application..."); else: if auApplicationProcessIds: asProcessIdsOutput = []; for uApplicationProcessId in auApplicationProcessIds: if asProcessIdsOutput: asProcessIdsOutput.append(", "); asProcessIdsOutput.extend([INFO, str(uApplicationProcessId), NORMAL]); oConsole.fPrint("* Running process ids: ", INFO, *asProcessIdsOutput); if sUWPApplicationPackageName: if not gbQuiet: if asApplicationArguments: oConsole.fPrint("* UWP application id: ", INFO, sUWPApplicationId, NORMAL, ", package name: ", INFO, \ sUWPApplicationPackageName, NORMAL, ", Arguments: ", INFO, " ".join(asApplicationArguments)); else: oConsole.fPrint("* UWP application id: ", INFO, sUWPApplicationId, NORMAL, ", package name: ", INFO, \ sUWPApplicationPackageName); if not sUWPApplicationPackageName: oConsole.fStatus("* The debugger is attaching to running processes of the application..."); elif auApplicationProcessIds: oConsole.fStatus("* The debugger is attaching to running processes and starting the application..."); else: oConsole.fStatus("* The debugger is starting the application..."); finally: oConsole.fUnlock(); oBugId = cBugId( sCdbISA = sApplicationISA or cBugId.sOSISA, sApplicationBinaryPath = sApplicationBinaryPath or None, auApplicationProcessIds = auApplicationProcessIds or None, sUWPApplicationPackageName = sUWPApplicationPackageName or None, sUWPApplicationId = sUWPApplicationId or None, asApplicationArguments = asApplicationArguments, asLocalSymbolPaths = dxConfig["asLocalSymbolPaths"], asSymbolCachePaths = dxConfig["asSymbolCachePaths"], asSymbolServerURLs = dxConfig["asSymbolServerURLs"], dsURLTemplate_by_srSourceFilePath = dsURLTemplate_by_srSourceFilePath, rImportantStdOutLines = rImportantStdOutLines, rImportantStdErrLines = rImportantStdErrLines, bGenerateReportHTML = dxConfig["bGenerateReportHTML"], uProcessMaxMemoryUse = dxConfig["uProcessMaxMemoryUse"], uTotalMaxMemoryUse = dxConfig["uTotalMaxMemoryUse"], fFailedToDebugApplicationCallback = fFailedToDebugApplicationHandler, fFailedToApplyMemoryLimitsCallback = fFailedToApplyMemoryLimitsHandler, fApplicationRunningCallback = fApplicationRunningHandler, fApplicationSuspendedCallback = fApplicationSuspendedHandler, fApplicationResumedCallback = fApplicationResumedHandler, fMainProcessTerminatedCallback = fMainProcessTerminatedHandler, fInternalExceptionCallback = fInternalExceptionHandler, fFinishedCallback = None, fPageHeapNotEnabledCallback = fPageHeapNotEnabledHandler, fStdInInputCallback = gbVerbose and fStdInInputHandler or None, fStdOutOutputCallback = gbVerbose and fStdOutOutputHandler or None, fStdErrOutputCallback = fStdErrOutputHandler, fNewProcessCallback = fNewProcessHandler, fApplicationStdOutOrErrOutputCallback = fApplicationStdOutOrErrOutputHandler, ); if dxConfig["nApplicationMaxRunTime"] is not None: oBugId.foSetTimeout("Maximum application runtime", dxConfig["nApplicationMaxRunTime"], fApplicationRunTimeHandler); if dxConfig["bExcessiveCPUUsageCheckEnabled"] and dxConfig["nExcessiveCPUUsageCheckInitialTimeout"]: oBugId.fSetCheckForExcessiveCPUUsageTimeout(dxConfig["nExcessiveCPUUsageCheckInitialTimeout"]); oBugId.fStart(); oBugId.fWait(); if gbAnErrorOccured: return 3; oConsole.fLock(); try: if oBugId.oBugReport is not None: oConsole.fPrint(HILITE, "+ A bug was detect in the application:"); if oBugId.oBugReport.sBugLocation: oConsole.fPrint(" Id @ Location: ", INFO, oBugId.oBugReport.sId, NORMAL, " @ ", INFO, oBugId.oBugReport.sBugLocation); sBugIdAndLocation = "%s @ %s" % (oBugId.oBugReport.sId, oBugId.oBugReport.sBugLocation); else: oConsole.fPrint(" Id: ", INFO, oBugId.oBugReport.sId); sBugIdAndLocation = oBugId.oBugReport.sId; if oBugId.oBugReport.sBugSourceLocation: oConsole.fPrint(" Source: ", INFO, oBugId.oBugReport.sBugSourceLocation); oConsole.fPrint(" Description: ", INFO, oBugId.oBugReport.sBugDescription); oConsole.fPrint(" Security impact: ", INFO, oBugId.oBugReport.sSecurityImpact); oConsole.fPrint(" Version: ", HILITE, oBugId.oBugReport.asVersionInformation[0]); # There is always the process' binary. for sVersionInformation in oBugId.oBugReport.asVersionInformation[1:]: # There may be two if the crash was in a oConsole.fPrint(" ", HILITE, sVersionInformation); # different binary (e.g. a .dll) if dxConfig["bGenerateReportHTML"]: # We'd like a report file name base on the BugId, but the later may contain characters that are not valid in a file name sDesiredReportFileName = "%s.html" % sBugIdAndLocation; # Thus, we need to translate these characters to create a valid filename that looks very similar to the BugId sValidReportFileName = mFileSystem.fsValidName(sDesiredReportFileName, bUnicode = dxConfig["bUseUnicodeReportFileNames"]); if dxConfig["sReportFolderPath"] is not None: sReportFilePath = mFileSystem.fsPath(dxConfig["sReportFolderPath"], sValidReportFileName); else: sReportFilePath = mFileSystem.fsPath(sValidReportFileName); eWriteDataToFileResult = mFileSystem.feWriteDataToFile( oBugId.oBugReport.sReportHTML, sReportFilePath, fbRetryOnFailure = lambda: False, ); if eWriteDataToFileResult: oConsole.fPrint(" Bug report: ", ERROR, "Cannot be saved (", \ ERROR_INFO, repr(eWriteDataToFileResult), ERROR, ")"); else: oConsole.fPrint(" Bug report: ", HILITE, sValidReportFileName, NORMAL, " (%d bytes)" % len(oBugId.oBugReport.sReportHTML)); else: oConsole.fPrint("+ The application terminated without a bug being detected."); sBugIdAndLocation = "No crash"; if gbVerbose: oConsole.fPrint(" Application time: %s seconds" % (long(oBugId.fnApplicationRunTime() * 1000) / 1000.0)); nOverheadTime = time.clock() - nStartTime - oBugId.fnApplicationRunTime(); oConsole.fPrint(" BugId overhead: %s seconds" % (long(nOverheadTime * 1000) / 1000.0)); if not bRepeat: return oBugId.oBugReport is not None and 1 or 0; duNumberOfRepros_by_sBugIdAndLocation.setdefault(sBugIdAndLocation, 0) duNumberOfRepros_by_sBugIdAndLocation[sBugIdAndLocation] += 1; sStatistics = ""; auOrderedNumberOfRepros = sorted(list(set(duNumberOfRepros_by_sBugIdAndLocation.values()))); auOrderedNumberOfRepros.reverse(); for uNumberOfRepros in auOrderedNumberOfRepros: for sBugIdAndLocation in duNumberOfRepros_by_sBugIdAndLocation.keys(): if duNumberOfRepros_by_sBugIdAndLocation[sBugIdAndLocation] == uNumberOfRepros: sStatistics += "%d \xD7 %s (%d%%)\r\n" % (uNumberOfRepros, str(sBugIdAndLocation), round(100.0 * uNumberOfRepros / uRunCounter)); if dxConfig["sReportFolderPath"] is not None: sStatisticsFilePath = mFileSystem.fsPath(dxConfig["sReportFolderPath"], sValidStatisticsFileName); else: sStatisticsFilePath = mFileSystem.fsPath(sValidStatisticsFileName); eWriteDataToFileResult = mFileSystem.feWriteDataToFile( sStatistics, sStatisticsFilePath, fbRetryOnFailure = lambda: False, ); if eWriteDataToFileResult: oConsole.fPrint(" Statistics: ", ERROR, "Cannot be saved (", ERROR_INFO, repr(eWriteDataToFileResult), ERROR, ")"); else: oConsole.fPrint(" Statistics: ", INFO, sStatisticsFilePath, NORMAL, " (%d bytes)" % len(sStatistics)); oConsole.fPrint(); # and loop finally: oConsole.fUnlock();