summaryrefslogtreecommitdiffstats
path: root/testing/mozbase/mozdebug/mozdebug/mozdebug.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/mozbase/mozdebug/mozdebug/mozdebug.py')
-rwxr-xr-xtesting/mozbase/mozdebug/mozdebug/mozdebug.py315
1 files changed, 315 insertions, 0 deletions
diff --git a/testing/mozbase/mozdebug/mozdebug/mozdebug.py b/testing/mozbase/mozdebug/mozdebug/mozdebug.py
new file mode 100755
index 0000000000..19f80f8d6a
--- /dev/null
+++ b/testing/mozbase/mozdebug/mozdebug/mozdebug.py
@@ -0,0 +1,315 @@
+#!/usr/bin/env python
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import json
+import os
+import sys
+from collections import namedtuple
+from distutils.spawn import find_executable
+from subprocess import check_output
+
+import mozinfo
+
+__all__ = [
+ "get_debugger_info",
+ "get_default_debugger_name",
+ "DebuggerSearch",
+ "get_default_valgrind_args",
+ "DebuggerInfo",
+]
+
+"""
+Map of debugging programs to information about them, like default arguments
+and whether or not they are interactive.
+
+To add support for a new debugger, simply add the relative entry in
+_DEBUGGER_INFO and optionally update the _DEBUGGER_PRIORITIES.
+"""
+_DEBUGGER_INFO = {
+ # gdb requires that you supply the '--args' flag in order to pass arguments
+ # after the executable name to the executable.
+ "gdb": {"interactive": True, "args": ["-q", "--args"]},
+ "cgdb": {"interactive": True, "args": ["-q", "--args"]},
+ "rust-gdb": {"interactive": True, "args": ["-q", "--args"]},
+ "lldb": {"interactive": True, "args": ["--"], "requiresEscapedArgs": True},
+ # Visual Studio Debugger Support.
+ "devenv.exe": {"interactive": True, "args": ["-debugexe"]},
+ # Visual C++ Express Debugger Support.
+ "wdexpress.exe": {"interactive": True, "args": ["-debugexe"]},
+ # Windows Development Kit super-debugger.
+ "windbg.exe": {
+ "interactive": True,
+ },
+}
+
+# Maps each OS platform to the preferred debugger programs found in _DEBUGGER_INFO.
+_DEBUGGER_PRIORITIES = {
+ "win": ["devenv.exe", "wdexpress.exe"],
+ "linux": ["gdb", "cgdb", "lldb"],
+ "mac": ["lldb", "gdb"],
+ "android": ["lldb"],
+ "unknown": ["gdb"],
+}
+
+
+DebuggerInfo = namedtuple(
+ "DebuggerInfo", ["path", "interactive", "args", "requiresEscapedArgs"]
+)
+
+
+def _windbg_installation_paths():
+ programFilesSuffixes = ["", " (x86)"]
+ programFiles = "C:/Program Files"
+ # Try the most recent versions first.
+ windowsKitsVersions = ["10", "8.1", "8"]
+
+ for suffix in programFilesSuffixes:
+ windowsKitsPrefix = os.path.join(programFiles + suffix, "Windows Kits")
+ for version in windowsKitsVersions:
+ yield os.path.join(
+ windowsKitsPrefix, version, "Debuggers", "x64", "windbg.exe"
+ )
+
+
+def _vswhere_path():
+ try:
+ import buildconfig
+
+ path = os.path.join(buildconfig.topsrcdir, "build", "win32", "vswhere.exe")
+ if os.path.isfile(path):
+ return path
+ except ImportError:
+ pass
+ # Hope it's available on PATH!
+ return "vswhere.exe"
+
+
+def get_debugger_path(debugger):
+ """
+ Get the full path of the debugger.
+
+ :param debugger: The name of the debugger.
+ """
+
+ if mozinfo.os == "mac" and debugger == "lldb":
+ # On newer OSX versions System Integrity Protections prevents us from
+ # setting certain env vars for a process such as DYLD_LIBRARY_PATH if
+ # it's in a protected directory such as /usr/bin. This is the case for
+ # lldb, so we try to find an instance under the Xcode install instead.
+
+ # Attempt to use the xcrun util to find the path.
+ try:
+ path = check_output(
+ ["xcrun", "--find", "lldb"], universal_newlines=True
+ ).strip()
+ if path:
+ return path
+ except Exception:
+ # Just default to find_executable instead.
+ pass
+
+ if mozinfo.os == "win" and debugger == "devenv.exe":
+ # Attempt to use vswhere to find the path.
+ try:
+ encoding = "mbcs" if sys.platform == "win32" else "utf-8"
+ vswhere = _vswhere_path()
+ vsinfo = check_output([vswhere, "-format", "json", "-latest"])
+ vsinfo = json.loads(vsinfo.decode(encoding, "replace"))
+ return os.path.join(
+ vsinfo[0]["installationPath"], "Common7", "IDE", "devenv.exe"
+ )
+ except Exception:
+ # Just default to find_executable instead.
+ pass
+
+ return find_executable(debugger)
+
+
+def get_debugger_info(debugger, debuggerArgs=None, debuggerInteractive=False):
+ """
+ Get the information about the requested debugger.
+
+ Returns a dictionary containing the |path| of the debugger executable,
+ if it will run in |interactive| mode, its arguments and whether it needs
+ to escape arguments it passes to the debugged program (|requiresEscapedArgs|).
+ If the debugger cannot be found in the system, returns |None|.
+
+ :param debugger: The name of the debugger.
+ :param debuggerArgs: If specified, it's the arguments to pass to the debugger,
+ as a string. Any debugger-specific separator arguments are appended after these
+ arguments.
+ :param debuggerInteractive: If specified, forces the debugger to be interactive.
+ """
+
+ debuggerPath = None
+
+ if debugger:
+ # Append '.exe' to the debugger on Windows if it's not present,
+ # so things like '--debugger=devenv' work.
+ if os.name == "nt" and not debugger.lower().endswith(".exe"):
+ debugger += ".exe"
+
+ debuggerPath = get_debugger_path(debugger)
+
+ if not debuggerPath:
+ # windbg is not installed with the standard set of tools, and it's
+ # entirely possible that the user hasn't added the install location to
+ # PATH, so we have to be a little more clever than normal to locate it.
+ # Just try to look for it in the standard installed location(s).
+ if debugger == "windbg.exe":
+ for candidate in _windbg_installation_paths():
+ if os.path.exists(candidate):
+ debuggerPath = candidate
+ break
+ else:
+ if os.path.exists(debugger):
+ debuggerPath = debugger
+
+ if not debuggerPath:
+ print("Error: Could not find debugger %s." % debugger)
+ print("Is it installed? Is it in your PATH?")
+ return None
+
+ debuggerName = os.path.basename(debuggerPath).lower()
+
+ def get_debugger_info(type, default):
+ if debuggerName in _DEBUGGER_INFO and type in _DEBUGGER_INFO[debuggerName]:
+ return _DEBUGGER_INFO[debuggerName][type]
+ return default
+
+ # Define a namedtuple to access the debugger information from the outside world.
+ debugger_arguments = []
+
+ if debuggerArgs:
+ # Append the provided debugger arguments at the end of the arguments list.
+ debugger_arguments += debuggerArgs.split()
+
+ debugger_arguments += get_debugger_info("args", [])
+
+ # Override the default debugger interactive mode if needed.
+ debugger_interactive = get_debugger_info("interactive", False)
+ if debuggerInteractive:
+ debugger_interactive = debuggerInteractive
+
+ d = DebuggerInfo(
+ debuggerPath,
+ debugger_interactive,
+ debugger_arguments,
+ get_debugger_info("requiresEscapedArgs", False),
+ )
+
+ return d
+
+
+# Defines the search policies to use in get_default_debugger_name.
+
+
+class DebuggerSearch:
+ OnlyFirst = 1
+ KeepLooking = 2
+
+
+def get_default_debugger_name(search=DebuggerSearch.OnlyFirst):
+ """
+ Get the debugger name for the default debugger on current platform.
+
+ :param search: If specified, stops looking for the debugger if the
+ default one is not found (|DebuggerSearch.OnlyFirst|) or keeps
+ looking for other compatible debuggers (|DebuggerSearch.KeepLooking|).
+ """
+
+ mozinfo.find_and_update_from_json()
+ os = mozinfo.info["os"]
+
+ # Find out which debuggers are preferred for use on this platform.
+ debuggerPriorities = _DEBUGGER_PRIORITIES[
+ os if os in _DEBUGGER_PRIORITIES else "unknown"
+ ]
+
+ # Finally get the debugger information.
+ for debuggerName in debuggerPriorities:
+ debuggerPath = get_debugger_path(debuggerName)
+ if debuggerPath:
+ return debuggerName
+ elif not search == DebuggerSearch.KeepLooking:
+ return None
+
+ return None
+
+
+# Defines default values for Valgrind flags.
+#
+# --smc-check=all-non-file is required to deal with code generation and
+# patching by the various JITS. Note that this is only necessary on
+# x86 and x86_64, but not on ARM. This flag is only necessary for
+# Valgrind versions prior to 3.11.
+#
+# --vex-iropt-register-updates=allregs-at-mem-access is required so that
+# Valgrind generates correct register values whenever there is a
+# segfault that is caught and handled. In particular OdinMonkey
+# requires this. More recent Valgrinds (3.11 and later) provide
+# --px-default=allregs-at-mem-access and
+# --px-file-backed=unwindregs-at-mem-access
+# which provide a significantly cheaper alternative, by restricting the
+# precise exception behaviour to JIT generated code only.
+#
+# --trace-children=yes is required to get Valgrind to follow into
+# content and other child processes. The resulting output can be
+# difficult to make sense of, and --child-silent-after-fork=yes
+# helps by causing Valgrind to be silent for the child in the period
+# after fork() but before its subsequent exec().
+#
+# --trace-children-skip lists processes that we are not interested
+# in tracing into.
+#
+# --leak-check=full requests full stack traces for all leaked blocks
+# detected at process exit.
+#
+# --show-possibly-lost=no requests blocks for which only an interior
+# pointer was found to be considered not leaked.
+#
+#
+# TODO: pass in the user supplied args for V (--valgrind-args=) and
+# use this to detect if a different tool has been selected. If so
+# adjust tool-specific args appropriately.
+#
+# TODO: pass in the path to the Valgrind to be used (--valgrind=), and
+# check what flags it accepts. Possible args that might be beneficial:
+#
+# --num-transtab-sectors=24 [reduces re-jitting overheads in long runs]
+# --px-default=allregs-at-mem-access
+# --px-file-backed=unwindregs-at-mem-access
+# [these reduce PX overheads as described above]
+#
+
+
+def get_default_valgrind_args():
+ return [
+ "--fair-sched=yes",
+ "--smc-check=all-non-file",
+ "--vex-iropt-register-updates=allregs-at-mem-access",
+ "--trace-children=yes",
+ "--child-silent-after-fork=yes",
+ (
+ "--trace-children-skip="
+ + "/usr/bin/hg,/bin/rm,*/bin/certutil,*/bin/pk12util,"
+ + "*/bin/ssltunnel,*/bin/uname,*/bin/which,*/bin/ps,"
+ + "*/bin/grep,*/bin/java,*/bin/lsb_release"
+ ),
+ ] + get_default_valgrind_tool_specific_args()
+
+
+# The default tool is Memcheck. Feeding these arguments to a different
+# Valgrind tool will cause it to fail at startup, so don't do that!
+
+
+def get_default_valgrind_tool_specific_args():
+ return [
+ "--partial-loads-ok=yes",
+ "--leak-check=summary",
+ "--show-possibly-lost=no",
+ "--show-mismatched-frees=no",
+ ]