gnome-shell/src/gnome-shell-jhbuild.in
Giovanni Campagna 47afd87e84 jhbuild wrapper: move performance measurement to a separate tool
Introduce a new gnome-shell-perf-tool, which can be used instead
of the old gnome-shell-jhbuild wrapper to gather data about gnome-shell
performance and submit to shell-perf.gnome.org. This runs the
shell with no extra setup beyond the WM_CLASS filter, so it can
be used for a jhbuild setup or for an installed shell.
2012-04-24 21:20:49 +02:00

378 lines
12 KiB
Plaintext
Executable File

#!@PYTHON@
# -*- mode: Python; indent-tabs-mode: nil; -*-
import atexit
import datetime
import dbus
from dbus.mainloop.glib import DBusGMainLoop
import gobject
try:
import json
except ImportError:
try:
import simplejson as json
except ImportError:
json = None
import optparse
import os
import random
import re
import shutil
import signal
import subprocess
import sys
import tempfile
import termios
import time
import errno
def show_version(option, opt_str, value, parser):
print "GNOME Shell @VERSION@"
sys.exit()
def get_running_session_environs():
wanted_environment = ['DBUS_SESSION_BUS_ADDRESS', 'DISPLAY', 'XDG_DATA_DIRS',
'XAUTHORITY', 'XDG_SESSION_COOKIE', 'ORBIT_SOCKETDIR',
'SESSION_MANAGER']
num_re = re.compile('^[0-9]+$')
myuid = os.getuid()
if not os.path.isdir('/proc'):
return {}
for filename in os.listdir('/proc'):
if not num_re.match(filename):
continue
piddir = '/proc/' + filename
try:
stat = os.stat(piddir)
except OSError, e:
continue
if not stat.st_uid == myuid:
continue
try:
f = open(piddir + "/cmdline")
command = f.read()
f.close()
except IOError, e:
continue
# /proc/cmdline is separated and terminated by NULs
command = command.split("\x00")[0]
command = os.path.basename(command)
if command != 'gnome-session':
continue
try:
f = open(os.path.join(piddir, 'environ'))
except OSError, e:
continue
environ_data = f.read()
f.close()
# There's a trailing null at the last one, so remove the
# empty string
environs = environ_data.split('\0')[:-1]
# Rumor has it the presence of just FOO (instead of FOO=bar)
# represents a deleted environment variable
environs = filter(lambda x: '=' in x, environs)
# Turn it into a dictionary
environs = dict(map(lambda x: x.split('=', 1), environs))
result = {}
for key in wanted_environment:
if key in environs:
result[key] = environs[key]
return result
_bus = None
_bus_iface = None
_name_owner_changed_hook = None
def on_name_owner_changed(name, prev_owner, new_owner):
if _name_owner_changed_hook:
_name_owner_changed_hook(name, prev_owner, new_owner)
def get_bus():
global _bus
if _bus is None:
dbus_loop = DBusGMainLoop()
_bus = dbus.SessionBus(mainloop=dbus_loop)
return _bus
def get_bus_iface():
global _bus_iface
if _bus_iface is None:
bus = get_bus()
bus_proxy = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
_bus_iface = dbus.Interface(bus_proxy, 'org.freedesktop.DBus')
_bus_iface.connect_to_signal('NameOwnerChanged', on_name_owner_changed)
return _bus_iface
def wait_for_dbus_name(wait_name):
global _name_owner_changed_hook
bus_iface = get_bus_iface()
loop = gobject.MainLoop()
def on_name_owner_changed(name, prev_owner, new_owner):
if not (name == wait_name and new_owner != ''):
return
loop.quit()
return
_name_owner_changed_hook = on_name_owner_changed
def on_timeout():
print "\nFailed to start %s: timed out" % (wait_name,)
sys.exit(1)
gobject.timeout_add_seconds(7, on_timeout)
loop.run()
_name_owner_changed_hook = None
def start_dconf_await_service():
DCONF_NAME = 'ca.desrt.dconf'
bus = get_bus()
get_bus_iface() # connect to NameOwnerChanged signal
# See if the service is already running or normal D-Bus activation works
need_manual_activate = False
try:
dconf_proxy = bus.get_object(DCONF_NAME, '/')
dconf_proxy.Ping(dbus_interface='org.freedesktop.DBus.Peer')
except dbus.exceptions.DBusException, e:
if e.get_dbus_name() == 'org.freedesktop.DBus.Error.ServiceUnknown':
need_manual_activate = True
else:
raise e
if not need_manual_activate:
return
# At this point, it looks like we just have a jhbuild install
# of dconf, not known to the session dbus-daemon, so we start
# it manually and wait for it to join the bus
print "Starting dconf-service... ",
sys.stdout.flush()
# dconf is linked without libtool, so unlike other GNOME modules,
# won't have an embedded rpath for its library directory.
env = dict(os.environ)
if 'LD_LIBRARY_PATH' in env and env['LD_LIBRARY_PATH']:
ld_library_path = '@libdir@:' + env['LD_LIBRARY_PATH']
else:
ld_library_path = '@libdir@'
env['LD_LIBRARY_PATH'] = ld_library_path
dconf_path = os.path.join('@libexecdir@', 'dconf-service')
try:
subprocess.Popen([dconf_path, '--keep-alive'], env=env)
except OSError, e:
print "\nFailed to start %s: %s" % (dconf_path, e)
sys.exit(1)
wait_for_dbus_name (DCONF_NAME)
def start_shell():
self_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
if os.path.exists(os.path.join(self_dir, 'gnome-shell-jhbuild.in')):
running_from_source_tree = True
top_dir = os.path.dirname(self_dir)
typelib_dir = '@JHBUILD_TYPELIBDIR@:' + os.path.join(top_dir, "src")
js_dir = os.path.join(top_dir, "js")
data_dir = os.path.join(top_dir, "data")
else:
running_from_source_tree = False
js_dir = os.path.join('@pkgdatadir@', 'js')
typelib_dir = '@JHBUILD_TYPELIBDIR@'
if os.environ.has_key('GI_TYPELIB_PATH'):
typelib_dir = typelib_dir + ":" + os.environ.get('GI_TYPELIB_PATH')
# Set up environment
env = dict(os.environ)
# TODO: Fix this, since nothing prevents it from propagating to child
# processes. Why is it even here?
env.update({'GNOME_DISABLE_CRASH_DIALOG' : '1'})
# This stuff should really should only happen when running
# uninstalled, i.e. it should be in the conditional
# below. Otherwise it's just a bad reimplementation of "jhbuild
# run". See bug #642084
env.update({'GNOME_SHELL_JS' : js_dir,
'PATH' : '@bindir@:' + os.environ.get('PATH', ''),
'GI_TYPELIB_PATH' : typelib_dir,
'XDG_CONFIG_DIRS' : '@sysconfdir@/xdg:' + (os.environ.get('XDG_CONFIG_DIRS') or '/etc/xdg'),
'XDG_DATA_DIRS' : '@datadir@:' + (os.environ.get('XDG_DATA_DIRS') or '/usr/local/share:/usr/share')})
if running_from_source_tree:
env.update({'GNOME_SHELL_BINDIR' : self_dir,
'GNOME_SHELL_DATADIR' : data_dir,
'GSETTINGS_SCHEMA_DIR' : data_dir })
else:
# This is just broken to set in the installed case; see bug #642084
env.update({'GSETTINGS_SCHEMA_DIR' : os.path.join('@datadir@', 'glib-2.0', 'schemas')})
# Also plain broken to set in the normal installed case
jhbuild_gconf_source = os.path.join('@sysconfdir@', 'gconf/2/path.jhbuild')
if os.path.exists(jhbuild_gconf_source):
env['GCONF_DEFAULT_SOURCE_PATH'] = jhbuild_gconf_source
args = []
if options.debug:
debug_command = options.debug_command.split()
if running_from_source_tree:
args += [os.path.join(top_dir, 'libtool'), '--mode=execute']
args += debug_command
args.append(os.path.join(self_dir, 'gnome-shell-real'))
if options.replace:
args.append('--replace')
if options.sync:
args.append('--sync')
return subprocess.Popen(args, env=env)
def _killall(processname):
subprocess.call(['pkill', '-u', '%d' % (os.getuid(), ),
'-f', r'^([^ ]*/)?' + re.escape(processname) + '($| )'])
def ensure_desktop_infrastructure_state():
# This is a collection of random hacks necessary to make sure
# that we can run in jhbuild scenarios or when dynamically
# replacing GNOME 2.
start_dconf_await_service()
# We need to terminate notification-daemon
_killall('notification-daemon')
_killall('notify-osd')
def run_shell():
if options.debug:
# Record initial terminal state so we can reset it to that
# later, in case we kill gdb at a bad time
termattrs = termios.tcgetattr(0);
normal_exit = False
if options.verbose:
print "Starting shell"
try:
shell = start_shell()
# Wait for shell to exit
if options.verbose:
print "Waiting for shell to exit"
shell.wait()
except KeyboardInterrupt, e:
try:
os.kill(shell.pid, signal.SIGKILL)
except:
pass
shell.wait()
finally:
if shell is None:
print "Failed to start shell"
elif shell.returncode == 0:
normal_exit = True
if options.verbose:
print "Shell exited normally"
elif shell.returncode < 0:
# Python has no mapping for strsignal; not worth using
# ctypes for this.
print "Shell killed with signal %d" % - shell.returncode
else:
# Normal reason here would be losing connection the X server
if options.verbose:
print "Shell exited with return code %d" % shell.returncode
if options.debug:
termios.tcsetattr(0, termios.TCSANOW, termattrs);
return normal_exit
def restore_gnome():
# Do imports lazily to save time and memory
import gio
import gconf
# We don't want to start the new gnome-panel in the current
# directory; $HOME is better for stuff launched from it
os.chdir(os.path.expanduser("~"))
def launch_component(gconf_path):
client = gconf.client_get_default()
component = client.get_string(gconf_path)
if component == None or component == "":
return False
# See gnome-session/gsm-util.c:gsm_util_find_desktop_file_for_app_name()
# The one difference is that we don't search the autostart directories,
# and just search normal application search path. (Gio doesnt' know
# how to search the autostart dirs, so we'd have to do that ourselves.)
appinfo = None
try:
appinfo = gio.unix.DesktopAppInfo(component + ".desktop")
except:
try:
appinfo = gio.unix.DesktopAppInfo("gnome-" + component + ".desktop")
except:
pass
if appinfo:
appinfo.launch()
return True
return False
# GNOME2 fallback
wm = launch_component("/desktop/gnome/session/required_components/windowmanager")
panel = launch_component("/desktop/gnome/session/required_components/panel")
if not wm and not panel: # Probably GNOME3
subprocess.Popen(['gnome-shell'])
# Main program
parser = optparse.OptionParser()
parser.add_option("-r", "--replace", action="store_true",
help="Replace the running metacity/gnome-panel")
parser.add_option("-g", "--debug", action="store_true",
help="Run under a debugger")
parser.add_option("", "--debug-command", metavar="COMMAND",
help="Command to use for debugging (defaults to 'gdb --args')")
parser.add_option("-v", "--verbose", action="store_true")
parser.add_option("", "--sync", action="store_true")
parser.add_option("", "--version", action="callback", callback=show_version,
help="Display version and exit")
options, args = parser.parse_args()
if args:
parser.print_usage()
sys.exit(1)
# Handle ssh logins
if 'DISPLAY' not in os.environ:
running_env = get_running_session_environs()
os.environ.update(running_env)
if options.debug_command:
options.debug = True
elif options.debug:
options.debug_command = "gdb --args"
# We only respawn the previous environment on abnormal exit;
# for a clean exit, we assume that gnome-shell was replaced with
# something else.
normal_exit = False
try:
ensure_desktop_infrastructure_state()
normal_exit = run_shell()
finally:
if options.replace and not normal_exit:
restore_gnome()
if normal_exit:
sys.exit(0)
else:
sys.exit(1)