mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
mozilla-central modules for debugger detection
Borrows two python modules from mozilla-central to give mach the ability to detect and pass arguments to a system's preferred debugger. Links to borrowed files: http://hg.mozilla.org/mozilla-central/file/c9cfa9b91dea/testing/mozbase/mozinfo/mozinfo/mozinfo.py http://hg.mozilla.org/mozilla-central/file/c9cfa9b91dea/testing/mozbase/mozdebug/mozdebug/mozdebug.py
This commit is contained in:
parent
4a257bc271
commit
f0d5083557
5 changed files with 489 additions and 0 deletions
|
@ -11,6 +11,8 @@ import sys
|
||||||
SEARCH_PATHS = [
|
SEARCH_PATHS = [
|
||||||
"python/mach",
|
"python/mach",
|
||||||
"python/toml",
|
"python/toml",
|
||||||
|
"python/mozinfo",
|
||||||
|
"python/mozdebug",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Individual files providing mach commands.
|
# Individual files providing mach commands.
|
||||||
|
|
30
python/mozdebug/__init__.py
Normal file
30
python/mozdebug/__init__.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# 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/.
|
||||||
|
|
||||||
|
"""
|
||||||
|
This module contains a set of function to gather information about the
|
||||||
|
debugging capabilities of the platform. It allows to look for a specific
|
||||||
|
debugger or to query the system for a compatible/default debugger.
|
||||||
|
|
||||||
|
The following simple example looks for the default debugger on the
|
||||||
|
current platform and launches a debugger process with the correct
|
||||||
|
debugger-specific arguments:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
import mozdebug
|
||||||
|
|
||||||
|
debugger = mozdebug.get_default_debugger_name()
|
||||||
|
debuggerInfo = mozdebug.get_debugger_info(debugger)
|
||||||
|
|
||||||
|
debuggeePath = "toDebug"
|
||||||
|
|
||||||
|
processArgs = [self.debuggerInfo.path] + self.debuggerInfo.args
|
||||||
|
processArgs.append(debuggeePath)
|
||||||
|
|
||||||
|
run_process(args, ...)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from mozdebug import *
|
168
python/mozdebug/mozdebug.py
Normal file
168
python/mozdebug/mozdebug.py
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
#!/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 os
|
||||||
|
import mozinfo
|
||||||
|
from collections import namedtuple
|
||||||
|
from distutils.spawn import find_executable
|
||||||
|
|
||||||
|
__all__ = ['get_debugger_info',
|
||||||
|
'get_default_debugger_name',
|
||||||
|
'DebuggerSearch']
|
||||||
|
|
||||||
|
'''
|
||||||
|
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']
|
||||||
|
},
|
||||||
|
|
||||||
|
'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']
|
||||||
|
},
|
||||||
|
|
||||||
|
# valgrind doesn't explain much about leaks unless you set the
|
||||||
|
# '--leak-check=full' flag. But there are a lot of objects that are
|
||||||
|
# semi-deliberately leaked, so we set '--show-possibly-lost=no' to avoid
|
||||||
|
# uninteresting output from those objects. We set '--smc-check==all-non-file'
|
||||||
|
# and '--vex-iropt-register-updates=allregs-at-mem-access' so that valgrind
|
||||||
|
# deals properly with JIT'd JavaScript code.
|
||||||
|
'valgrind': {
|
||||||
|
'interactive': False,
|
||||||
|
'args': ['--leak-check=full',
|
||||||
|
'--show-possibly-lost=no',
|
||||||
|
'--smc-check=all-non-file',
|
||||||
|
'--vex-iropt-register-updates=allregs-at-mem-access']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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'],
|
||||||
|
'unknown': ['gdb']
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = find_executable(debugger)
|
||||||
|
|
||||||
|
if not debuggerPath:
|
||||||
|
print 'Error: Could not find debugger %s.' % debugger
|
||||||
|
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.
|
||||||
|
DebuggerInfo = namedtuple(
|
||||||
|
'DebuggerInfo',
|
||||||
|
['path', 'interactive', 'args', 'requiresEscapedArgs']
|
||||||
|
)
|
||||||
|
|
||||||
|
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|).
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Find out which debuggers are preferred for use on this platform.
|
||||||
|
debuggerPriorities = _DEBUGGER_PRIORITIES[mozinfo.os if mozinfo.os in _DEBUGGER_PRIORITIES else 'unknown']
|
||||||
|
|
||||||
|
# Finally get the debugger information.
|
||||||
|
for debuggerName in debuggerPriorities:
|
||||||
|
debuggerPath = find_executable(debuggerName)
|
||||||
|
if debuggerPath:
|
||||||
|
return debuggerName
|
||||||
|
elif not search == DebuggerSearch.KeepLooking:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return None
|
56
python/mozinfo/__init__.py
Normal file
56
python/mozinfo/__init__.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# 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/.
|
||||||
|
|
||||||
|
"""
|
||||||
|
interface to transform introspected system information to a format palatable to
|
||||||
|
Mozilla
|
||||||
|
|
||||||
|
Module variables:
|
||||||
|
|
||||||
|
.. attribute:: bits
|
||||||
|
|
||||||
|
32 or 64
|
||||||
|
|
||||||
|
.. attribute:: isBsd
|
||||||
|
|
||||||
|
Returns ``True`` if the operating system is BSD
|
||||||
|
|
||||||
|
.. attribute:: isLinux
|
||||||
|
|
||||||
|
Returns ``True`` if the operating system is Linux
|
||||||
|
|
||||||
|
.. attribute:: isMac
|
||||||
|
|
||||||
|
Returns ``True`` if the operating system is Mac
|
||||||
|
|
||||||
|
.. attribute:: isWin
|
||||||
|
|
||||||
|
Returns ``True`` if the operating system is Windows
|
||||||
|
|
||||||
|
.. attribute:: os
|
||||||
|
|
||||||
|
Operating system [``'win'``, ``'mac'``, ``'linux'``, ...]
|
||||||
|
|
||||||
|
.. attribute:: processor
|
||||||
|
|
||||||
|
Processor architecture [``'x86'``, ``'x86_64'``, ``'ppc'``, ...]
|
||||||
|
|
||||||
|
.. attribute:: version
|
||||||
|
|
||||||
|
Operating system version string. For windows, the service pack information is also included
|
||||||
|
|
||||||
|
.. attribute:: info
|
||||||
|
|
||||||
|
Returns information identifying the current system.
|
||||||
|
|
||||||
|
* :attr:`bits`
|
||||||
|
* :attr:`os`
|
||||||
|
* :attr:`processor`
|
||||||
|
* :attr:`version`
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import mozinfo
|
||||||
|
from mozinfo import *
|
||||||
|
__all__ = mozinfo.__all__
|
233
python/mozinfo/mozinfo.py
Executable file
233
python/mozinfo/mozinfo.py
Executable file
|
@ -0,0 +1,233 @@
|
||||||
|
#!/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/.
|
||||||
|
|
||||||
|
# TODO: it might be a good idea of adding a system name (e.g. 'Ubuntu' for
|
||||||
|
# linux) to the information; I certainly wouldn't want anyone parsing this
|
||||||
|
# information and having behaviour depend on it
|
||||||
|
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# keep a copy of the os module since updating globals overrides this
|
||||||
|
_os = os
|
||||||
|
|
||||||
|
class unknown(object):
|
||||||
|
"""marker class for unknown information"""
|
||||||
|
def __nonzero__(self):
|
||||||
|
return False
|
||||||
|
def __str__(self):
|
||||||
|
return 'UNKNOWN'
|
||||||
|
unknown = unknown() # singleton
|
||||||
|
|
||||||
|
# get system information
|
||||||
|
info = {'os': unknown,
|
||||||
|
'processor': unknown,
|
||||||
|
'version': unknown,
|
||||||
|
'os_version': unknown,
|
||||||
|
'bits': unknown,
|
||||||
|
'has_sandbox': unknown }
|
||||||
|
(system, node, release, version, machine, processor) = platform.uname()
|
||||||
|
(bits, linkage) = platform.architecture()
|
||||||
|
|
||||||
|
# get os information and related data
|
||||||
|
if system in ["Microsoft", "Windows"]:
|
||||||
|
info['os'] = 'win'
|
||||||
|
# There is a Python bug on Windows to determine platform values
|
||||||
|
# http://bugs.python.org/issue7860
|
||||||
|
if "PROCESSOR_ARCHITEW6432" in os.environ:
|
||||||
|
processor = os.environ.get("PROCESSOR_ARCHITEW6432", processor)
|
||||||
|
else:
|
||||||
|
processor = os.environ.get('PROCESSOR_ARCHITECTURE', processor)
|
||||||
|
system = os.environ.get("OS", system).replace('_', ' ')
|
||||||
|
(major, minor, _, _, service_pack) = os.sys.getwindowsversion()
|
||||||
|
info['service_pack'] = service_pack
|
||||||
|
os_version = "%d.%d" % (major, minor)
|
||||||
|
elif system == "Linux":
|
||||||
|
if hasattr(platform, "linux_distribution"):
|
||||||
|
(distro, os_version, codename) = platform.linux_distribution()
|
||||||
|
else:
|
||||||
|
(distro, os_version, codename) = platform.dist()
|
||||||
|
if not processor:
|
||||||
|
processor = machine
|
||||||
|
version = "%s %s" % (distro, os_version)
|
||||||
|
info['os'] = 'linux'
|
||||||
|
info['linux_distro'] = distro
|
||||||
|
elif system in ['DragonFly', 'FreeBSD', 'NetBSD', 'OpenBSD']:
|
||||||
|
info['os'] = 'bsd'
|
||||||
|
version = os_version = sys.platform
|
||||||
|
elif system == "Darwin":
|
||||||
|
(release, versioninfo, machine) = platform.mac_ver()
|
||||||
|
version = "OS X %s" % release
|
||||||
|
versionNums = release.split('.')[:2]
|
||||||
|
os_version = "%s.%s" % (versionNums[0], versionNums[1])
|
||||||
|
info['os'] = 'mac'
|
||||||
|
elif sys.platform in ('solaris', 'sunos5'):
|
||||||
|
info['os'] = 'unix'
|
||||||
|
os_version = version = sys.platform
|
||||||
|
else:
|
||||||
|
os_version = version = unknown
|
||||||
|
|
||||||
|
info['version'] = version
|
||||||
|
info['os_version'] = os_version
|
||||||
|
|
||||||
|
# processor type and bits
|
||||||
|
if processor in ["i386", "i686"]:
|
||||||
|
if bits == "32bit":
|
||||||
|
processor = "x86"
|
||||||
|
elif bits == "64bit":
|
||||||
|
processor = "x86_64"
|
||||||
|
elif processor.upper() == "AMD64":
|
||||||
|
bits = "64bit"
|
||||||
|
processor = "x86_64"
|
||||||
|
elif processor == "Power Macintosh":
|
||||||
|
processor = "ppc"
|
||||||
|
bits = re.search('(\d+)bit', bits).group(1)
|
||||||
|
info.update({'processor': processor,
|
||||||
|
'bits': int(bits),
|
||||||
|
})
|
||||||
|
|
||||||
|
if info['os'] == 'linux':
|
||||||
|
import ctypes
|
||||||
|
import errno
|
||||||
|
PR_SET_SECCOMP = 22
|
||||||
|
SECCOMP_MODE_FILTER = 2
|
||||||
|
ctypes.CDLL("libc.so.6", use_errno=True).prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, 0)
|
||||||
|
info['has_sandbox'] = ctypes.get_errno() == errno.EFAULT
|
||||||
|
else:
|
||||||
|
info['has_sandbox'] = True
|
||||||
|
|
||||||
|
# standard value of choices, for easy inspection
|
||||||
|
choices = {'os': ['linux', 'bsd', 'win', 'mac', 'unix'],
|
||||||
|
'bits': [32, 64],
|
||||||
|
'processor': ['x86', 'x86_64', 'ppc']}
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize(info):
|
||||||
|
"""Do some sanitization of input values, primarily
|
||||||
|
to handle universal Mac builds."""
|
||||||
|
if "processor" in info and info["processor"] == "universal-x86-x86_64":
|
||||||
|
# If we're running on OS X 10.6 or newer, assume 64-bit
|
||||||
|
if release[:4] >= "10.6": # Note this is a string comparison
|
||||||
|
info["processor"] = "x86_64"
|
||||||
|
info["bits"] = 64
|
||||||
|
else:
|
||||||
|
info["processor"] = "x86"
|
||||||
|
info["bits"] = 32
|
||||||
|
|
||||||
|
# method for updating information
|
||||||
|
def update(new_info):
|
||||||
|
"""
|
||||||
|
Update the info.
|
||||||
|
|
||||||
|
:param new_info: Either a dict containing the new info or a path/url
|
||||||
|
to a json file containing the new info.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if isinstance(new_info, basestring):
|
||||||
|
# lazy import
|
||||||
|
import mozfile
|
||||||
|
import json
|
||||||
|
f = mozfile.load(new_info)
|
||||||
|
new_info = json.loads(f.read())
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
info.update(new_info)
|
||||||
|
sanitize(info)
|
||||||
|
globals().update(info)
|
||||||
|
|
||||||
|
# convenience data for os access
|
||||||
|
for os_name in choices['os']:
|
||||||
|
globals()['is' + os_name.title()] = info['os'] == os_name
|
||||||
|
# unix is special
|
||||||
|
if isLinux or isBsd:
|
||||||
|
globals()['isUnix'] = True
|
||||||
|
|
||||||
|
def find_and_update_from_json(*dirs):
|
||||||
|
"""
|
||||||
|
Find a mozinfo.json file, load it, and update the info with the
|
||||||
|
contents.
|
||||||
|
|
||||||
|
:param dirs: Directories in which to look for the file. They will be
|
||||||
|
searched after first looking in the root of the objdir
|
||||||
|
if the current script is being run from a Mozilla objdir.
|
||||||
|
|
||||||
|
Returns the full path to mozinfo.json if it was found, or None otherwise.
|
||||||
|
"""
|
||||||
|
# First, see if we're in an objdir
|
||||||
|
try:
|
||||||
|
from mozbuild.base import MozbuildObject, BuildEnvironmentNotFoundException
|
||||||
|
build = MozbuildObject.from_environment()
|
||||||
|
json_path = _os.path.join(build.topobjdir, "mozinfo.json")
|
||||||
|
if _os.path.isfile(json_path):
|
||||||
|
update(json_path)
|
||||||
|
return json_path
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
except BuildEnvironmentNotFoundException:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for d in dirs:
|
||||||
|
d = _os.path.abspath(d)
|
||||||
|
json_path = _os.path.join(d, "mozinfo.json")
|
||||||
|
if _os.path.isfile(json_path):
|
||||||
|
update(json_path)
|
||||||
|
return json_path
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
update({})
|
||||||
|
|
||||||
|
# exports
|
||||||
|
__all__ = info.keys()
|
||||||
|
__all__ += ['is' + os_name.title() for os_name in choices['os']]
|
||||||
|
__all__ += [
|
||||||
|
'info',
|
||||||
|
'unknown',
|
||||||
|
'main',
|
||||||
|
'choices',
|
||||||
|
'update',
|
||||||
|
'find_and_update_from_json',
|
||||||
|
]
|
||||||
|
|
||||||
|
def main(args=None):
|
||||||
|
|
||||||
|
# parse the command line
|
||||||
|
from optparse import OptionParser
|
||||||
|
parser = OptionParser(description=__doc__)
|
||||||
|
for key in choices:
|
||||||
|
parser.add_option('--%s' % key, dest=key,
|
||||||
|
action='store_true', default=False,
|
||||||
|
help="display choices for %s" % key)
|
||||||
|
options, args = parser.parse_args()
|
||||||
|
|
||||||
|
# args are JSON blobs to override info
|
||||||
|
if args:
|
||||||
|
# lazy import
|
||||||
|
import json
|
||||||
|
for arg in args:
|
||||||
|
if _os.path.exists(arg):
|
||||||
|
string = file(arg).read()
|
||||||
|
else:
|
||||||
|
string = arg
|
||||||
|
update(json.loads(string))
|
||||||
|
|
||||||
|
# print out choices if requested
|
||||||
|
flag = False
|
||||||
|
for key, value in options.__dict__.items():
|
||||||
|
if value is True:
|
||||||
|
print '%s choices: %s' % (key, ' '.join([str(choice)
|
||||||
|
for choice in choices[key]]))
|
||||||
|
flag = True
|
||||||
|
if flag: return
|
||||||
|
|
||||||
|
# otherwise, print out all info
|
||||||
|
for key, value in info.items():
|
||||||
|
print '%s: %s' % (key, value)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Add table
Add a link
Reference in a new issue