Index: tools/telemetry/telemetry/core/chrome/desktop_browser_backend.py |
diff --git a/tools/telemetry/telemetry/core/chrome/desktop_browser_backend.py b/tools/telemetry/telemetry/core/chrome/desktop_browser_backend.py |
index 834278d015f8e3e44723b97d7489260b0c03a7b0..61644c12606e7b0359882b92662750a736c6772b 100644 |
--- a/tools/telemetry/telemetry/core/chrome/desktop_browser_backend.py |
+++ b/tools/telemetry/telemetry/core/chrome/desktop_browser_backend.py |
@@ -1,12 +1,15 @@ |
# Copyright (c) 2012 The Chromium Authors. All rights reserved. |
# Use of this source code is governed by a BSD-style license that can be |
# found in the LICENSE file. |
+import glob |
+import heapq |
import logging |
import os |
import subprocess as subprocess |
import shutil |
import sys |
import tempfile |
+import time |
from telemetry.core import util |
from telemetry.core.backends import browser_backend |
@@ -25,7 +28,7 @@ class DesktopBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): |
# Initialize fields so that an explosion during init doesn't break in Close. |
self._proc = None |
- self._tmpdir = None |
+ self._tmp_profile_dir = None |
self._tmp_output_file = None |
self._executable = executable |
@@ -48,29 +51,33 @@ class DesktopBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): |
self._profile_dir = None |
self._supports_net_benchmarking = True |
self._delete_profile_dir_after_run = delete_profile_dir_after_run |
+ self._tmp_minidump_dir = tempfile.mkdtemp() |
self._SetupProfile() |
def _SetupProfile(self): |
if not self.options.dont_override_profile: |
- self._tmpdir = tempfile.mkdtemp() |
+ self._tmp_profile_dir = tempfile.mkdtemp() |
profile_dir = self._profile_dir or self.options.profile_dir |
if profile_dir: |
if self.is_content_shell: |
logging.critical('Profiles cannot be used with content shell') |
sys.exit(1) |
- shutil.rmtree(self._tmpdir) |
- shutil.copytree(profile_dir, self._tmpdir) |
+ shutil.rmtree(self._tmp_profile_dir) |
+ shutil.copytree(profile_dir, self._tmp_profile_dir) |
def _LaunchBrowser(self): |
args = [self._executable] |
args.extend(self.GetBrowserStartupArgs()) |
+ env = os.environ.copy() |
+ env['CHROME_HEADLESS'] = '1' # Don't upload minidumps. |
+ env['BREAKPAD_DUMP_LOCATION'] = self._tmp_minidump_dir |
if not self.options.show_stdout: |
self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0) |
self._proc = subprocess.Popen( |
- args, stdout=self._tmp_output_file, stderr=subprocess.STDOUT) |
+ args, stdout=self._tmp_output_file, stderr=subprocess.STDOUT, env=env) |
else: |
- self._proc = subprocess.Popen(args) |
+ self._proc = subprocess.Popen(args, env=env) |
try: |
self._WaitForBrowserToComeUp() |
@@ -82,6 +89,7 @@ class DesktopBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): |
def GetBrowserStartupArgs(self): |
args = super(DesktopBrowserBackend, self).GetBrowserStartupArgs() |
args.append('--remote-debugging-port=%i' % self._port) |
+ args.append('--enable-crash-reporter-for-testing') |
if not self.is_content_shell: |
args.append('--window-size=1280,1024') |
if self._flash_path: |
@@ -91,7 +99,7 @@ class DesktopBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): |
else: |
args.append('--enable-benchmarking') |
if not self.options.dont_override_profile: |
- args.append('--user-data-dir=%s' % self._tmpdir) |
+ args.append('--user-data-dir=%s' % self._tmp_profile_dir) |
return args |
def SetProfileDirectory(self, profile_dir): |
@@ -126,7 +134,7 @@ class DesktopBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): |
@property |
def profile_directory(self): |
- return self._tmpdir |
+ return self._tmp_profile_dir |
def IsBrowserRunning(self): |
return self._proc.poll() == None |
@@ -141,10 +149,42 @@ class DesktopBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): |
return '' |
def GetStackTrace(self): |
- # crbug.com/223572, symbolize stack trace for desktop browsers. |
- logging.warning('Stack traces not supported on desktop browsers, ' |
- 'returning stdout') |
- return self.GetStandardOutput() |
+ executable_dir = os.path.dirname(self._executable) |
+ stackwalk = os.path.join(executable_dir, 'minidump_stackwalk') |
+ if not os.path.exists(stackwalk): |
+ logging.warning('minidump_stackwalk binary not found. Must build it to ' |
+ 'symbolize crash dumps. Returning browser stdout.') |
+ return self.GetStandardOutput() |
+ |
+ dumps = glob.glob(os.path.join(self._tmp_minidump_dir, '*.dmp')) |
+ if not dumps: |
+ logging.warning('No crash dump found. Returning browser stdout.') |
+ return self.GetStandardOutput() |
+ most_recent_dump = heapq.nlargest(1, dumps, os.path.getmtime)[0] |
+ if os.path.getmtime(most_recent_dump) < (time.time() - (5 * 60)): |
+ logging.warn('Crash dump is older than 5 minutes. May not be correct.') |
+ |
+ minidump = most_recent_dump + '.stripped' |
+ with open(most_recent_dump, 'rb') as infile: |
+ with open(minidump, 'wb') as outfile: |
+ outfile.write(''.join(infile.read().partition('MDMP')[1:])) |
+ |
+ symbols = glob.glob(os.path.join(executable_dir, 'chrome.breakpad.*'))[0] |
+ if not symbols: |
+ logging.warning('No breakpad symbols found. Returning browser stdout.') |
+ return self.GetStandardOutput() |
+ |
+ symbols_path = os.path.join(self._tmp_minidump_dir, 'symbols') |
+ with open(symbols, 'r') as f: |
+ _, _, _, sha, binary = f.readline().split() |
+ symbol_path = os.path.join(symbols_path, binary, sha) |
+ os.makedirs(symbol_path) |
+ shutil.copyfile(symbols, os.path.join(symbol_path, binary + '.sym')) |
+ |
+ error = tempfile.NamedTemporaryFile('w', 0) |
+ return subprocess.Popen( |
+ [stackwalk, minidump, symbols_path], |
+ stdout=subprocess.PIPE, stderr=error).communicate()[0] |
def __del__(self): |
self.Close() |
@@ -178,9 +218,9 @@ class DesktopBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): |
raise Exception('Could not shutdown the browser.') |
if self._delete_profile_dir_after_run and \ |
- self._tmpdir and os.path.exists(self._tmpdir): |
- shutil.rmtree(self._tmpdir, ignore_errors=True) |
- self._tmpdir = None |
+ self._tmp_profile_dir and os.path.exists(self._tmp_profile_dir): |
+ shutil.rmtree(self._tmp_profile_dir, ignore_errors=True) |
+ self._tmp_profile_dir = None |
if self._tmp_output_file: |
self._tmp_output_file.close() |