1424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# found in the LICENSE file.
43551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)import glob
53551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)import heapq
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import logging
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import os
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import subprocess as subprocess
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import shutil
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import sys
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import tempfile
123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)import time
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)from telemetry.core import util
15a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)from telemetry.core.backends import browser_backend
16a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)from telemetry.core.backends.chrome import chrome_browser_backend
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
18a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)class DesktopBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """The backend for controlling a locally-executed browser instance, on Linux,
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Mac or Windows.
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  """
2258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  def __init__(self, browser_options, executable, flash_path, is_content_shell,
2358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)               browser_directory, output_profile_path, extensions_to_load):
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    super(DesktopBrowserBackend, self).__init__(
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        is_content_shell=is_content_shell,
2690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        supports_extensions=not is_content_shell,
2758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        browser_options=browser_options,
2858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        output_profile_path=output_profile_path,
2958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        extensions_to_load=extensions_to_load)
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # Initialize fields so that an explosion during init doesn't break in Close.
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._proc = None
333551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    self._tmp_profile_dir = None
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._tmp_output_file = None
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._executable = executable
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not self._executable:
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      raise Exception('Cannot create browser, no executable found!')
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
40eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    self._flash_path = flash_path
417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if self._flash_path and not os.path.exists(self._flash_path):
427dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      logging.warning(('Could not find flash at %s. Running without flash.\n\n'
437dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                       'To fix this see http://go/read-src-internal') %
447dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                      self._flash_path)
457dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      self._flash_path = None
46eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
4758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if len(extensions_to_load) > 0 and is_content_shell:
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      raise browser_backend.ExtensionsNotSupportedException(
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          'Content shell does not support extensions.')
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
51bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    self._browser_directory = browser_directory
52f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._port = None
5390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    self._profile_dir = None
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._supports_net_benchmarking = True
553551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    self._tmp_minidump_dir = tempfile.mkdtemp()
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
57a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    self._SetupProfile()
58a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
59a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  def _SetupProfile(self):
6058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if not self.browser_options.dont_override_profile:
61424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      if self._output_profile_path:
62424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        # If both |_output_profile_path| and |profile_dir| are specified then
63424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        # the calling code will throw an exception, so we don't need to worry
64424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        # about that case here.
65424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        self._tmp_profile_dir = self._output_profile_path
66424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      else:
67424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        self._tmp_profile_dir = tempfile.mkdtemp()
6858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      profile_dir = self._profile_dir or self.browser_options.profile_dir
69a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      if profile_dir:
70a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        if self.is_content_shell:
71a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)          logging.critical('Profiles cannot be used with content shell')
72a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)          sys.exit(1)
73424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        logging.info("Using profile directory:'%s'." % profile_dir)
743551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        shutil.rmtree(self._tmp_profile_dir)
753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        shutil.copytree(profile_dir, self._tmp_profile_dir)
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
77a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  def _LaunchBrowser(self):
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    args = [self._executable]
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    args.extend(self.GetBrowserStartupArgs())
801e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    if self.browser_options.startup_url:
811e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      args.append(self.browser_options.startup_url)
823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    env = os.environ.copy()
833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    env['CHROME_HEADLESS'] = '1'  # Don't upload minidumps.
843551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    env['BREAKPAD_DUMP_LOCATION'] = self._tmp_minidump_dir
85f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    logging.debug('Starting Chrome %s', args)
8658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if not self.browser_options.show_stdout:
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0)
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._proc = subprocess.Popen(
893551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          args, stdout=self._tmp_output_file, stderr=subprocess.STDOUT, env=env)
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    else:
913551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      self._proc = subprocess.Popen(args, env=env)
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    try:
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._WaitForBrowserToComeUp()
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._PostBrowserStartupInitialization()
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    except:
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self.Close()
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      raise
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def GetBrowserStartupArgs(self):
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    args = super(DesktopBrowserBackend, self).GetBrowserStartupArgs()
102f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self._port = util.GetUnreservedAvailableLocalPort()
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    args.append('--remote-debugging-port=%i' % self._port)
1043551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    args.append('--enable-crash-reporter-for-testing')
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not self.is_content_shell:
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      args.append('--window-size=1280,1024')
107eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      if self._flash_path:
108eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        args.append('--ppapi-flash-path=%s' % self._flash_path)
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if self._supports_net_benchmarking:
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        args.append('--enable-net-benchmarking')
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      else:
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        args.append('--enable-benchmarking')
11358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      if not self.browser_options.dont_override_profile:
1143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        args.append('--user-data-dir=%s' % self._tmp_profile_dir)
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return args
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  def SetProfileDirectory(self, profile_dir):
11890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    # Make sure _profile_dir hasn't already been set.
11990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    assert self._profile_dir is None
12090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
12190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if self.is_content_shell:
12290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      logging.critical('Profile creation cannot be used with content shell')
12390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      sys.exit(1)
12490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
12590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    self._profile_dir = profile_dir
12690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
127a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  def Start(self):
128a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    self._LaunchBrowser()
129a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
130a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    # For old chrome versions, might have to relaunch to have the
131a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    # correct net_benchmarking switch.
1323551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if self.chrome_branch_number < 1418:
133a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      self.Close()
134a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      self._supports_net_benchmarking = False
135a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      self._LaunchBrowser()
136a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  @property
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def pid(self):
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if self._proc:
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return self._proc.pid
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return None
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
14390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  @property
144bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  def browser_directory(self):
145bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    return self._browser_directory
146bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch
147bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  @property
14890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  def profile_directory(self):
1493551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return self._tmp_profile_dir
15090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def IsBrowserRunning(self):
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return self._proc.poll() == None
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def GetStandardOutput(self):
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    assert self._tmp_output_file, "Can't get standard output with show_stdout"
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self._tmp_output_file.flush()
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    try:
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      with open(self._tmp_output_file.name) as f:
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return f.read()
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    except IOError:
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return ''
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1637dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  def GetStackTrace(self):
1643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    stackwalk = util.FindSupportBinary('minidump_stackwalk')
1653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if not stackwalk:
1663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      logging.warning('minidump_stackwalk binary not found. Must build it to '
1673551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                      'symbolize crash dumps. Returning browser stdout.')
1683551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      return self.GetStandardOutput()
1693551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1703551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    dumps = glob.glob(os.path.join(self._tmp_minidump_dir, '*.dmp'))
1713551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if not dumps:
1723551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      logging.warning('No crash dump found. Returning browser stdout.')
1733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      return self.GetStandardOutput()
1743551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    most_recent_dump = heapq.nlargest(1, dumps, os.path.getmtime)[0]
1753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if os.path.getmtime(most_recent_dump) < (time.time() - (5 * 60)):
176f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      logging.warning('Crash dump is older than 5 minutes. May not be correct.')
177f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
178f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    symbols = glob.glob(os.path.join(self._browser_directory, '*.breakpad*'))
179f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if not symbols:
180f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      logging.warning('No breakpad symbols found. Returning browser stdout.')
181f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      return self.GetStandardOutput()
1823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    minidump = most_recent_dump + '.stripped'
1843551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    with open(most_recent_dump, 'rb') as infile:
1853551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      with open(minidump, 'wb') as outfile:
1863551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        outfile.write(''.join(infile.read().partition('MDMP')[1:]))
1873551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1883551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    symbols_path = os.path.join(self._tmp_minidump_dir, 'symbols')
1891e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    for symbol in sorted(symbols, key=os.path.getmtime, reverse=True):
1901e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      if not os.path.isfile(symbol):
1911e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        continue
1923551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      with open(symbol, 'r') as f:
1933551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        fields = f.readline().split()
1943551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        if not fields:
1953551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          continue
1963551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        sha = fields[3]
1973551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        binary = ' '.join(fields[4:])
1983551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      symbol_path = os.path.join(symbols_path, binary, sha)
1991e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      if os.path.exists(symbol_path):
2001e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        continue
2013551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      os.makedirs(symbol_path)
2023551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      shutil.copyfile(symbol, os.path.join(symbol_path, binary + '.sym'))
2033551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
2043551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    error = tempfile.NamedTemporaryFile('w', 0)
2053551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return subprocess.Popen(
2063551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        [stackwalk, minidump, symbols_path],
2073551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        stdout=subprocess.PIPE, stderr=error).communicate()[0]
2087dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def __del__(self):
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    self.Close()
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def Close(self):
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    super(DesktopBrowserBackend, self).Close()
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if self._proc:
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      def IsClosed():
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if not self._proc:
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          return True
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return self._proc.poll() != None
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Try to politely shutdown, first.
223f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if not IsClosed():
224f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        self._proc.terminate()
225f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        try:
226f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          util.WaitFor(IsClosed, timeout=5)
227f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          self._proc = None
228f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        except util.TimeoutException:
229f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          logging.warning('Failed to gracefully shutdown. Proceeding to kill.')
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # Kill it.
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if not IsClosed():
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        self._proc.kill()
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        try:
235f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          util.WaitFor(IsClosed, timeout=10)
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        except util.TimeoutException:
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          raise Exception('Could not shutdown the browser.')
238f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        finally:
239f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          self._proc = None
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
241424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    if self._output_profile_path:
242424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      # If we need the output then double check that it exists.
243424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      if not (self._tmp_profile_dir and os.path.exists(self._tmp_profile_dir)):
244424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        raise Exception("No profile directory generated by Chrome: '%s'." %
245424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)            self._tmp_profile_dir)
246424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    else:
247424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      # If we don't need the profile after the run then cleanup.
248424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      if self._tmp_profile_dir and os.path.exists(self._tmp_profile_dir):
249424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        shutil.rmtree(self._tmp_profile_dir, ignore_errors=True)
250424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        self._tmp_profile_dir = None
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if self._tmp_output_file:
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._tmp_output_file.close()
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._tmp_output_file = None
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  def CreateForwarder(self, *port_pairs):
257c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return browser_backend.DoNothingForwarder(*port_pairs)
258