java_heap_profiler.py revision 1e9bf3e0803691d0a228da41fc608347b6db4340
1# Copyright (c) 2013 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import os
6import subprocess
7import threading
8
9from telemetry.core import util
10from telemetry.core.backends.chrome import android_browser_finder
11from telemetry.core.platform import profiler
12
13class JavaHeapProfiler(profiler.Profiler):
14  """Android-specific, trigger and fetch java heap dumps."""
15
16  _DEFAULT_DEVICE_DIR = '/data/local/tmp/javaheap'
17  # TODO(bulach): expose this as a command line option somehow.
18  _DEFAULT_INTERVAL = 20
19  def __init__(self, browser_backend, platform_backend, output_path, state):
20    super(JavaHeapProfiler, self).__init__(
21        browser_backend, platform_backend, output_path, state)
22    self._run_count = 1
23
24    self._DumpJavaHeap(False)
25
26    self._timer = threading.Timer(self._DEFAULT_INTERVAL, self._OnTimer)
27    self._timer.start()
28
29  @classmethod
30  def name(cls):
31    return 'java-heap'
32
33  @classmethod
34  def is_supported(cls, browser_type):
35    if browser_type == 'any':
36      return android_browser_finder.CanFindAvailableBrowsers()
37    return browser_type.startswith('android')
38
39  def CollectProfile(self):
40    self._timer.cancel()
41    self._DumpJavaHeap(True)
42    self._browser_backend.adb.Adb().Adb().Pull(
43        self._DEFAULT_DEVICE_DIR, self._output_path)
44    self._browser_backend.adb.RunShellCommand(
45        'rm ' + os.path.join(self._DEFAULT_DEVICE_DIR, '*'))
46    output_files = []
47    for f in os.listdir(self._output_path):
48      if os.path.splitext(f)[1] == '.aprof':
49        input_file = os.path.join(self._output_path, f)
50        output_file = input_file.replace('.aprof', '.hprof')
51        subprocess.call(['hprof-conv', input_file, output_file])
52        output_files.append(output_file)
53    return output_files
54
55  def _OnTimer(self):
56    self._DumpJavaHeap(False)
57
58  def _DumpJavaHeap(self, wait_for_completion):
59    if not self._browser_backend.adb.Adb().FileExistsOnDevice(
60        self._DEFAULT_DEVICE_DIR):
61      self._browser_backend.adb.RunShellCommand(
62          'mkdir -p ' + self._DEFAULT_DEVICE_DIR)
63      self._browser_backend.adb.RunShellCommand(
64          'chmod 777 ' + self._DEFAULT_DEVICE_DIR)
65
66    device_dump_file = None
67    for pid in self._GetProcessOutputFileMap().iterkeys():
68      device_dump_file = '%s/%s.%s.aprof' % (self._DEFAULT_DEVICE_DIR, pid,
69                                             self._run_count)
70      self._browser_backend.adb.RunShellCommand('am dumpheap %s %s' %
71                                                (pid, device_dump_file))
72    if device_dump_file and wait_for_completion:
73      util.WaitFor(lambda: self._FileSize(device_dump_file) > 0, timeout=2)
74    self._run_count += 1
75
76  def _FileSize(self, file_name):
77    f = self._browser_backend.adb.Adb().ListPathContents(file_name)
78    return f.get(os.path.basename(file_name), (0, ))[0]
79