summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/code_coverage/coverage.py538
-rw-r--r--tools/code_coverage/process_coverage.py466
-rw-r--r--tools/memory_watcher/call_stack.cc752
3 files changed, 878 insertions, 878 deletions
diff --git a/tools/code_coverage/coverage.py b/tools/code_coverage/coverage.py
index a805cd7..ca6cd17 100644
--- a/tools/code_coverage/coverage.py
+++ b/tools/code_coverage/coverage.py
@@ -1,269 +1,269 @@
-#!/bin/env python
-# Copyright (c) 2006-2008 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.
-
-
-"""Module to setup and generate code coverage data
-
-This module first sets up the environment for code coverage, instruments the
-binaries, runs the tests and collects the code coverage data.
-
-
-Usage:
- coverage.py --upload=<upload_location>
- --revision=<revision_number>
- --src_root=<root_of_source_tree>
- [--tools_path=<tools_path>]
-"""
-
-import logging
-import optparse
-import os
-import shutil
-import subprocess
-import sys
-import tempfile
-
-import google.logging_utils
-import google.process_utils as proc
-
-
-# The list of binaries that will be instrumented for code coverage
-# TODO(niranjan): Add a complete list of binaries
-windows_binaries = ['unit_tests.exe',
- 'ui_tests.exe']
-
-# The list of tests that will be run
-#TODO(niranjan): Add more tests
-windows_tests = ['unit_tests.exe',
- 'ui_tests.exe']
-
-
-def IsWindows():
- """Checks if the current platform is Windows.
- """
- return sys.platform[:3] == 'win'
-
-
-class Coverage(object):
- """Class to set up and generate code coverage.
-
- This class contains methods that are useful to set up the environment for
- code coverage.
-
- Attributes:
- instrumented: A boolean indicating if all the binaries have been
- instrumented.
- """
-
- def __init__(self,
- revision,
- vsts_path = None,
- lcov_converter_path = None):
- google.logging_utils.config_root()
- self.revision = revision
- self.instrumented = False
- self.vsts_path = vsts_path
- self.lcov_converter_path = lcov_converter_path
- self._dir = None
-
-
- def SetUp(self, binaries):
- """Set up the platform specific environment and instrument the binaries for
- coverage.
-
- This method sets up the environment, instruments all the compiled binaries
- and sets up the code coverage counters.
-
- Args:
- binaries: List of binaries that need to be instrumented.
-
- Returns:
- Path of the file containing code coverage data on successful
- instrumentation.
- None on error.
- """
- if self.instrumented:
- logging.error('Binaries already instrumented')
- return None
- coverage_file = None
- if IsWindows():
- # Stop all previous instance of VSPerfMon counters
- counters_command = ('%s -shutdown' %
- (os.path.join(self.vsts_path, 'vsperfcmd.exe')))
- (retcode, output) = proc.RunCommandFull(counters_command,
- collect_output=True)
- # TODO(niranjan): Add a check that to verify that the binaries were built
- # using the /PROFILE linker flag.
- if self.vsts_path == None or self.lcov_converter_path == None:
- return None
- # Remove trailing slashes
- self.vsts_path = self.vsts_path.rstrip('\\')
- instrument_command = '%s /COVERAGE ' % (os.path.join(self.vsts_path,
- 'vsinstr.exe'))
- for binary in binaries:
- logging.info('binary = %s' % (binary))
- logging.info('instrument_command = %s' % (instrument_command))
- # Instrument each binary in the list
- (retcode, output) = proc.RunCommandFull(instrument_command + binary,
- collect_output=True)
- # Check if the file has been instrumented correctly.
- if output.pop().rfind('Successfully instrumented') == -1:
- logging.error('Error instrumenting %s' % (binary))
- return None
-
- # Generate the file name for the coverage results
- self._dir = tempfile.mkdtemp()
- coverage_file = os.path.join(self._dir, 'chrome_win32_%s.coverage' %
- (self.revision))
- logging.info('.coverage file: %s' % (coverage_file))
-
- # After all the binaries have been instrumented, we start the counters.
- counters_command = ('%s -start:coverage -output:%s' %
- (os.path.join(self.vsts_path, 'vsperfcmd.exe'),
- coverage_file))
- # Here we use subprocess.call() instead of the RunCommandFull because the
- # VSPerfCmd spawns another process before terminating and this confuses
- # the subprocess.Popen() used by RunCommandFull.
- retcode = subprocess.call(counters_command)
- # TODO(niranjan): Check whether the counters have been started
- # successfully.
-
- # We are now ready to run tests and measure code coverage.
- self.instrumented = True
- else:
- return None
- return coverage_file
-
-
- def TearDown(self):
- """Tear down method.
-
- This method shuts down the counters, and cleans up all the intermediate
- artifacts.
- """
- if self.instrumented == False:
- return
-
- if IsWindows():
- # Stop counters
- counters_command = ('%s -shutdown' %
- (os.path.join(self.vsts_path, 'vsperfcmd.exe')))
- (retcode, output) = proc.RunCommandFull(counters_command,
- collect_output=True)
- logging.info('Counters shut down: %s' % (output))
- # TODO(niranjan): Revert the instrumented binaries to their original
- # versions.
- else:
- return
- # Delete all the temp files and folders
- if self._dir != None:
- shutil.rmtree(self._dir, ignore_errors=True)
- logging.info('Cleaned up temporary files and folders')
- # Reset the instrumented flag.
- self.instrumented = False
-
-
- def Upload(self, coverage_file, upload_path, sym_path=None, src_root=None):
- """Upload the results to the dashboard.
-
- This method uploads the coverage data to a dashboard where it will be
- processed. On Windows, this method will first convert the .coverage file to
- the lcov format. This method needs to be called before the TearDown method.
-
- Args:
- coverage_file: The coverage data file to upload.
- upload_path: Destination where the coverage data will be processed.
- sym_path: Symbol path for the build (Win32 only)
- src_root: Root folder of the source tree (Win32 only)
-
- Returns:
- True on success.
- False on failure.
- """
- if self.IsWindows():
- # Stop counters
- counters_command = ('%s -shutdown' %
- (os.path.join(self.vsts_path, 'vsperfcmd.exe')))
- (retcode, output) = proc.RunCommandFull(counters_command,
- collect_output=True)
- logging.info('Counters shut down: %s' % (output))
- # Convert the .coverage file to lcov format
- if self.lcov_converter_path == False:
- logging.error('Lcov converter tool not found')
- return False
- self.lcov_converter_path = self.lcov_converter_path.rstrip('\\')
- convert_command = ('%s -sym_path=%s -src_root=%s ' %
- (os.path.join(self.lcov_converter_path,
- 'coverage_analyzer.exe'),
- sym_path,
- src_root))
- logging.info('Conversion to lcov complete')
- (retcode, output) = proc.RunCommandFull(convert_command + coverage_file,
- collect_output=True)
- shutil.copy(coverage_file, coverage_file.replace('.coverage', ''))
- # TODO(niranjan): Upload this somewhere!
-
-
-def main():
- # Command line parsing
- parser = optparse.OptionParser()
- # Path where the .coverage to .lcov converter tools are stored.
- parser.add_option('-t',
- '--tools_path',
- dest='tools_path',
- default=None,
- help='Location of the coverage tools (windows only)')
- parser.add_option('-u',
- '--upload',
- dest='upload_path'
- default=None,
- help='Location where the results should be uploaded')
- # We need the revision number so that we can generate the output file of the
- # format chrome_<platform>_<revision>.lcov
- parser.add_option('-r',
- '--revision',
- dest='revision',
- default=None,
- help='Revision number of the Chromium source repo')
- # Root of the source tree. Needed for converting the generated .coverage file
- # on Windows to the open source lcov format.
- parser.add_option('-s',
- '--src_root',
- dest='src_root',
- default=None,
- help='Root of the source repository')
-
- if revision == None or src_root == None or upload_path == None:
- logging.error('Invalid command line arguments')
- sys.exit(1)
-
- if IsWindows():
- # Initialize coverage
- cov = coverage.Coverage(revision,
- tools_path,
- tools_path)
- # Instrument the binaries
- coverage_file = cov.SetUp(windows_binaries)
- if coverage_file != None:
- # Run all the tests
- for test in windows_tests:
- logging.info('Executing test %s: ' % binary)
- (retcode, output) = proc.RunCommandFull(binary, collect_output=True)
- if retcode != 0:
- sys.exit(retcode)
- else:
- logging.error('Error during instrumentation.')
- sys.exit(1)
-
- cov.Upload(coverage_file,
- upload_path,
- os.path.join(src_root, 'chrome', 'Release'),
- src_root)
- cov.TearDown()
-
-
-if __name__ == '__main__':
- sys.exit(main())
-
+#!/bin/env python
+# Copyright (c) 2006-2008 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.
+
+
+"""Module to setup and generate code coverage data
+
+This module first sets up the environment for code coverage, instruments the
+binaries, runs the tests and collects the code coverage data.
+
+
+Usage:
+ coverage.py --upload=<upload_location>
+ --revision=<revision_number>
+ --src_root=<root_of_source_tree>
+ [--tools_path=<tools_path>]
+"""
+
+import logging
+import optparse
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+
+import google.logging_utils
+import google.process_utils as proc
+
+
+# The list of binaries that will be instrumented for code coverage
+# TODO(niranjan): Add a complete list of binaries
+windows_binaries = ['unit_tests.exe',
+ 'ui_tests.exe']
+
+# The list of tests that will be run
+#TODO(niranjan): Add more tests
+windows_tests = ['unit_tests.exe',
+ 'ui_tests.exe']
+
+
+def IsWindows():
+ """Checks if the current platform is Windows.
+ """
+ return sys.platform[:3] == 'win'
+
+
+class Coverage(object):
+ """Class to set up and generate code coverage.
+
+ This class contains methods that are useful to set up the environment for
+ code coverage.
+
+ Attributes:
+ instrumented: A boolean indicating if all the binaries have been
+ instrumented.
+ """
+
+ def __init__(self,
+ revision,
+ vsts_path = None,
+ lcov_converter_path = None):
+ google.logging_utils.config_root()
+ self.revision = revision
+ self.instrumented = False
+ self.vsts_path = vsts_path
+ self.lcov_converter_path = lcov_converter_path
+ self._dir = None
+
+
+ def SetUp(self, binaries):
+ """Set up the platform specific environment and instrument the binaries for
+ coverage.
+
+ This method sets up the environment, instruments all the compiled binaries
+ and sets up the code coverage counters.
+
+ Args:
+ binaries: List of binaries that need to be instrumented.
+
+ Returns:
+ Path of the file containing code coverage data on successful
+ instrumentation.
+ None on error.
+ """
+ if self.instrumented:
+ logging.error('Binaries already instrumented')
+ return None
+ coverage_file = None
+ if IsWindows():
+ # Stop all previous instance of VSPerfMon counters
+ counters_command = ('%s -shutdown' %
+ (os.path.join(self.vsts_path, 'vsperfcmd.exe')))
+ (retcode, output) = proc.RunCommandFull(counters_command,
+ collect_output=True)
+ # TODO(niranjan): Add a check that to verify that the binaries were built
+ # using the /PROFILE linker flag.
+ if self.vsts_path == None or self.lcov_converter_path == None:
+ return None
+ # Remove trailing slashes
+ self.vsts_path = self.vsts_path.rstrip('\\')
+ instrument_command = '%s /COVERAGE ' % (os.path.join(self.vsts_path,
+ 'vsinstr.exe'))
+ for binary in binaries:
+ logging.info('binary = %s' % (binary))
+ logging.info('instrument_command = %s' % (instrument_command))
+ # Instrument each binary in the list
+ (retcode, output) = proc.RunCommandFull(instrument_command + binary,
+ collect_output=True)
+ # Check if the file has been instrumented correctly.
+ if output.pop().rfind('Successfully instrumented') == -1:
+ logging.error('Error instrumenting %s' % (binary))
+ return None
+
+ # Generate the file name for the coverage results
+ self._dir = tempfile.mkdtemp()
+ coverage_file = os.path.join(self._dir, 'chrome_win32_%s.coverage' %
+ (self.revision))
+ logging.info('.coverage file: %s' % (coverage_file))
+
+ # After all the binaries have been instrumented, we start the counters.
+ counters_command = ('%s -start:coverage -output:%s' %
+ (os.path.join(self.vsts_path, 'vsperfcmd.exe'),
+ coverage_file))
+ # Here we use subprocess.call() instead of the RunCommandFull because the
+ # VSPerfCmd spawns another process before terminating and this confuses
+ # the subprocess.Popen() used by RunCommandFull.
+ retcode = subprocess.call(counters_command)
+ # TODO(niranjan): Check whether the counters have been started
+ # successfully.
+
+ # We are now ready to run tests and measure code coverage.
+ self.instrumented = True
+ else:
+ return None
+ return coverage_file
+
+
+ def TearDown(self):
+ """Tear down method.
+
+ This method shuts down the counters, and cleans up all the intermediate
+ artifacts.
+ """
+ if self.instrumented == False:
+ return
+
+ if IsWindows():
+ # Stop counters
+ counters_command = ('%s -shutdown' %
+ (os.path.join(self.vsts_path, 'vsperfcmd.exe')))
+ (retcode, output) = proc.RunCommandFull(counters_command,
+ collect_output=True)
+ logging.info('Counters shut down: %s' % (output))
+ # TODO(niranjan): Revert the instrumented binaries to their original
+ # versions.
+ else:
+ return
+ # Delete all the temp files and folders
+ if self._dir != None:
+ shutil.rmtree(self._dir, ignore_errors=True)
+ logging.info('Cleaned up temporary files and folders')
+ # Reset the instrumented flag.
+ self.instrumented = False
+
+
+ def Upload(self, coverage_file, upload_path, sym_path=None, src_root=None):
+ """Upload the results to the dashboard.
+
+ This method uploads the coverage data to a dashboard where it will be
+ processed. On Windows, this method will first convert the .coverage file to
+ the lcov format. This method needs to be called before the TearDown method.
+
+ Args:
+ coverage_file: The coverage data file to upload.
+ upload_path: Destination where the coverage data will be processed.
+ sym_path: Symbol path for the build (Win32 only)
+ src_root: Root folder of the source tree (Win32 only)
+
+ Returns:
+ True on success.
+ False on failure.
+ """
+ if self.IsWindows():
+ # Stop counters
+ counters_command = ('%s -shutdown' %
+ (os.path.join(self.vsts_path, 'vsperfcmd.exe')))
+ (retcode, output) = proc.RunCommandFull(counters_command,
+ collect_output=True)
+ logging.info('Counters shut down: %s' % (output))
+ # Convert the .coverage file to lcov format
+ if self.lcov_converter_path == False:
+ logging.error('Lcov converter tool not found')
+ return False
+ self.lcov_converter_path = self.lcov_converter_path.rstrip('\\')
+ convert_command = ('%s -sym_path=%s -src_root=%s ' %
+ (os.path.join(self.lcov_converter_path,
+ 'coverage_analyzer.exe'),
+ sym_path,
+ src_root))
+ logging.info('Conversion to lcov complete')
+ (retcode, output) = proc.RunCommandFull(convert_command + coverage_file,
+ collect_output=True)
+ shutil.copy(coverage_file, coverage_file.replace('.coverage', ''))
+ # TODO(niranjan): Upload this somewhere!
+
+
+def main():
+ # Command line parsing
+ parser = optparse.OptionParser()
+ # Path where the .coverage to .lcov converter tools are stored.
+ parser.add_option('-t',
+ '--tools_path',
+ dest='tools_path',
+ default=None,
+ help='Location of the coverage tools (windows only)')
+ parser.add_option('-u',
+ '--upload',
+ dest='upload_path'
+ default=None,
+ help='Location where the results should be uploaded')
+ # We need the revision number so that we can generate the output file of the
+ # format chrome_<platform>_<revision>.lcov
+ parser.add_option('-r',
+ '--revision',
+ dest='revision',
+ default=None,
+ help='Revision number of the Chromium source repo')
+ # Root of the source tree. Needed for converting the generated .coverage file
+ # on Windows to the open source lcov format.
+ parser.add_option('-s',
+ '--src_root',
+ dest='src_root',
+ default=None,
+ help='Root of the source repository')
+
+ if revision == None or src_root == None or upload_path == None:
+ logging.error('Invalid command line arguments')
+ sys.exit(1)
+
+ if IsWindows():
+ # Initialize coverage
+ cov = coverage.Coverage(revision,
+ tools_path,
+ tools_path)
+ # Instrument the binaries
+ coverage_file = cov.SetUp(windows_binaries)
+ if coverage_file != None:
+ # Run all the tests
+ for test in windows_tests:
+ logging.info('Executing test %s: ' % binary)
+ (retcode, output) = proc.RunCommandFull(binary, collect_output=True)
+ if retcode != 0:
+ sys.exit(retcode)
+ else:
+ logging.error('Error during instrumentation.')
+ sys.exit(1)
+
+ cov.Upload(coverage_file,
+ upload_path,
+ os.path.join(src_root, 'chrome', 'Release'),
+ src_root)
+ cov.TearDown()
+
+
+if __name__ == '__main__':
+ sys.exit(main())
+
diff --git a/tools/code_coverage/process_coverage.py b/tools/code_coverage/process_coverage.py
index c902f4d..6cec95b 100644
--- a/tools/code_coverage/process_coverage.py
+++ b/tools/code_coverage/process_coverage.py
@@ -1,233 +1,233 @@
-#!/usr/bin/python2.4
-#
-# Copyright 2008, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-"""Script to clean the lcov files and convert it to HTML
-
-TODO(niranjan): Add usage information here
-"""
-
-
-import optparse
-import os
-import shutil
-import sys
-import tempfile
-
-
-# These are source files that were generated during compile time. We want to
-# remove references to these files from the lcov file otherwise genhtml will
-# throw an error.
-win32_srcs_exclude = ['parse.y',
- 'xpathgrammar.cpp',
- 'cssgrammar.cpp']
-
-
-def CleanPathNames(dir):
- """Clean the pathnames of the HTML generated by genhtml.
-
- This method is required only for code coverage on Win32. Due to a known issue
- with reading from CIFS shares mounted on Linux, genhtml appends a ^M to every
- file name it reads from the Windows share, causing corrupt filenames in
- genhtml's output folder.
-
- Args:
- dir: Output folder of the genhtml output.
-
- Returns:
- None
- """
- # Stip off the ^M characters that get appended to the file name
- for file in os.walk(dir):
- file_clean = file.replace('\r', '')
- if file_clean != file:
- os.rename(file, file_clean)
-
-
-def GenerateHtml(lcov_path, dash_root):
- """Runs genhtml to convert lcov data to human readable HTML.
-
- This script expects the LCOV file name to be in the format:
- chrome_<platform>_<revision#>.lcov.
- This method parses the file name and then sets up the correct folder
- hierarchy for the coverage data and then runs genhtml to get the actual HTML
- formatted coverage data.
-
- Args:
- lcov_path: Path of the lcov data file.
- dash_root: Root location of the dashboard.
-
- Returns:
- Code coverage percentage on sucess.
- None on failure.
- """
- # Parse the LCOV file name.
- filename = os.path.basename(lcov_path).split('.')[0]
- buffer = filename.split('_')
- dash_root = dash_root.rstrip('/') # Remove trailing '/'
-
- # Set up correct folder heirarchy in the dashboard root
- # TODO(niranjan): Check the formatting using a regexp
- if len(buffer) >= 3: # Check if filename has right formatting
- platform = buffer[len(buffer) - 2]
- revision = buffer[len(buffer) - 1]
- if os.path.exists(os.path.join(dash_root, platform)) == False:
- os.mkdir(os.path.join(dash_root, platform))
- output_dir = os.join.path(dash_root, platform, revision)
- os.mkdir(output_dir)
- else:
- # TODO(niranjan): Add failure logging here.
- return None # File not formatted correctly
-
- # Run genhtml
- os.system('/usr/bin/genhtml -o %s %s' % (output_dir, lcov_path))
- # TODO(niranjan): Check the exit status of the genhtml command.
- # TODO(niranjan): Parse the stdout and return coverage percentage.
- CleanPathNames(output_dir)
- return 'dummy' # TODO(niranjan): Return actual percentage.
-
-
-def CleanWin32Lcov(lcov_path, src_root):
- """Cleanup the lcov data generated on Windows.
-
- This method fixes up the paths inside the lcov file from the Win32 specific
- paths to the actual paths of the mounted CIFS share. The lcov files generated
- on Windows have the following format:
-
- SF:c:\chrome_src\src\skia\sgl\skscan_antihair.cpp
- DA:97,0
- DA:106,0
- DA:107,0
- DA:109,0
- ...
- end_of_record
-
- This method changes the source-file (SF) lines to a format compatible with
- genhtml on Linux by fixing paths. This method also removes references to
- certain dynamically generated files to be excluded from the code ceverage.
-
- Args:
- lcov_path: Path of the Win32 lcov file to be cleaned.
- src_root: Location of the source and symbols dir.
- Returns:
- None
- """
- strip_flag = False
- lcov = open(lcov_path, 'r')
- (tmpfile, tmpfile_name) = tempfile.mkstemp()
- src_root = src_root.rstrip('/') # Remove trailing '/'
- for line in lcov:
- if line.startswith('SF'):
- # We want to exclude certain auto-generated files otherwise genhtml will
- # fail to convert lcov to HTML.
- for exp in win32_srcs_exclude:
- if line.rfind(exp) != -1:
- strip_flag = True # Indicates that we want to remove this section
-
- # Now we normalize the paths
- # e.g. Change SF:c:\foo\src\... to SF:/chrome_src/...
- parse_buffer = line.split(':')
- buffer = '%s:%s%s' % (parse_buffer[0],
- src_root,
- parse_buffer[2])
- buffer = buffer.replace('\\', '/')
- line = buffer
-
- # Write to the temp file if the section to write is valid
- if strip_flag == False:
- tmpfile.write('%s' % (line))
-
- # Reset the strip flag
- if line.endswith('end_of_record'):
- strip_flag = False
-
- # Close the files and replace the lcov file by the 'clean' tmpfile
- tmpfile.close()
- lcov.close()
- shutil.move(tmpfile_name, lcov_path)
-
-
-def main():
- if sys.platform[:5] != 'linux': # Run this only on Linux
- print 'This script is supported only on Linux'
- os.exit(1)
-
- # Command line parsing
- parser = optparse.OptionParser()
- parser.add_option('-p',
- '--platform',
- dest='platform',
- default=None,
- help='Platform that the locv file was generated on. Must be
- one of {win32, linux2, macosx}')
- parser.add_option('-s',
- '--source',
- dest='src_dir',
- default=None,
- help='Path to the source code and symbols')
- parser.add_option('-d',
- '--dash_root',
- dest='dash_root',
- default=None,
- help='Root directory for the dashboard')
- parser.add_option('-l',
- '--lcov',
- dest='lcov_path',
- default=None,
- help='Location of the LCOV file to process')
- (options, args) = parser.parse_args()
-
- if options.platform == None:
- parser.error('Platform not specified')
- if options.lcov_path == None:
- parser.error('lcov file path not specified')
- if options.src_dir == None:
- parser.error('Source directory not specified')
- if options.dash_root == None:
- parser.error('Dashboard root not specified')
- if options.platform == 'win32':
- CleanWin32Lcov(options.lcov_path, options.src_dir)
- percent = GenerateHtml(options.lcov_path, options.dash_root)
- if percent == None:
- # TODO(niranjan): Add logging.
- print 'Failed to generate code coverage'
- os.exit(1)
- else:
- # TODO(niranjan): Do something with the code coverage numbers
- pass
- else:
- print 'Unsupported platform'
- os.exit(1)
-
-
-if __name__ == '__main__':
- main()
-
+#!/usr/bin/python2.4
+#
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+"""Script to clean the lcov files and convert it to HTML
+
+TODO(niranjan): Add usage information here
+"""
+
+
+import optparse
+import os
+import shutil
+import sys
+import tempfile
+
+
+# These are source files that were generated during compile time. We want to
+# remove references to these files from the lcov file otherwise genhtml will
+# throw an error.
+win32_srcs_exclude = ['parse.y',
+ 'xpathgrammar.cpp',
+ 'cssgrammar.cpp']
+
+
+def CleanPathNames(dir):
+ """Clean the pathnames of the HTML generated by genhtml.
+
+ This method is required only for code coverage on Win32. Due to a known issue
+ with reading from CIFS shares mounted on Linux, genhtml appends a ^M to every
+ file name it reads from the Windows share, causing corrupt filenames in
+ genhtml's output folder.
+
+ Args:
+ dir: Output folder of the genhtml output.
+
+ Returns:
+ None
+ """
+ # Stip off the ^M characters that get appended to the file name
+ for file in os.walk(dir):
+ file_clean = file.replace('\r', '')
+ if file_clean != file:
+ os.rename(file, file_clean)
+
+
+def GenerateHtml(lcov_path, dash_root):
+ """Runs genhtml to convert lcov data to human readable HTML.
+
+ This script expects the LCOV file name to be in the format:
+ chrome_<platform>_<revision#>.lcov.
+ This method parses the file name and then sets up the correct folder
+ hierarchy for the coverage data and then runs genhtml to get the actual HTML
+ formatted coverage data.
+
+ Args:
+ lcov_path: Path of the lcov data file.
+ dash_root: Root location of the dashboard.
+
+ Returns:
+ Code coverage percentage on sucess.
+ None on failure.
+ """
+ # Parse the LCOV file name.
+ filename = os.path.basename(lcov_path).split('.')[0]
+ buffer = filename.split('_')
+ dash_root = dash_root.rstrip('/') # Remove trailing '/'
+
+ # Set up correct folder heirarchy in the dashboard root
+ # TODO(niranjan): Check the formatting using a regexp
+ if len(buffer) >= 3: # Check if filename has right formatting
+ platform = buffer[len(buffer) - 2]
+ revision = buffer[len(buffer) - 1]
+ if os.path.exists(os.path.join(dash_root, platform)) == False:
+ os.mkdir(os.path.join(dash_root, platform))
+ output_dir = os.join.path(dash_root, platform, revision)
+ os.mkdir(output_dir)
+ else:
+ # TODO(niranjan): Add failure logging here.
+ return None # File not formatted correctly
+
+ # Run genhtml
+ os.system('/usr/bin/genhtml -o %s %s' % (output_dir, lcov_path))
+ # TODO(niranjan): Check the exit status of the genhtml command.
+ # TODO(niranjan): Parse the stdout and return coverage percentage.
+ CleanPathNames(output_dir)
+ return 'dummy' # TODO(niranjan): Return actual percentage.
+
+
+def CleanWin32Lcov(lcov_path, src_root):
+ """Cleanup the lcov data generated on Windows.
+
+ This method fixes up the paths inside the lcov file from the Win32 specific
+ paths to the actual paths of the mounted CIFS share. The lcov files generated
+ on Windows have the following format:
+
+ SF:c:\chrome_src\src\skia\sgl\skscan_antihair.cpp
+ DA:97,0
+ DA:106,0
+ DA:107,0
+ DA:109,0
+ ...
+ end_of_record
+
+ This method changes the source-file (SF) lines to a format compatible with
+ genhtml on Linux by fixing paths. This method also removes references to
+ certain dynamically generated files to be excluded from the code ceverage.
+
+ Args:
+ lcov_path: Path of the Win32 lcov file to be cleaned.
+ src_root: Location of the source and symbols dir.
+ Returns:
+ None
+ """
+ strip_flag = False
+ lcov = open(lcov_path, 'r')
+ (tmpfile, tmpfile_name) = tempfile.mkstemp()
+ src_root = src_root.rstrip('/') # Remove trailing '/'
+ for line in lcov:
+ if line.startswith('SF'):
+ # We want to exclude certain auto-generated files otherwise genhtml will
+ # fail to convert lcov to HTML.
+ for exp in win32_srcs_exclude:
+ if line.rfind(exp) != -1:
+ strip_flag = True # Indicates that we want to remove this section
+
+ # Now we normalize the paths
+ # e.g. Change SF:c:\foo\src\... to SF:/chrome_src/...
+ parse_buffer = line.split(':')
+ buffer = '%s:%s%s' % (parse_buffer[0],
+ src_root,
+ parse_buffer[2])
+ buffer = buffer.replace('\\', '/')
+ line = buffer
+
+ # Write to the temp file if the section to write is valid
+ if strip_flag == False:
+ tmpfile.write('%s' % (line))
+
+ # Reset the strip flag
+ if line.endswith('end_of_record'):
+ strip_flag = False
+
+ # Close the files and replace the lcov file by the 'clean' tmpfile
+ tmpfile.close()
+ lcov.close()
+ shutil.move(tmpfile_name, lcov_path)
+
+
+def main():
+ if sys.platform[:5] != 'linux': # Run this only on Linux
+ print 'This script is supported only on Linux'
+ os.exit(1)
+
+ # Command line parsing
+ parser = optparse.OptionParser()
+ parser.add_option('-p',
+ '--platform',
+ dest='platform',
+ default=None,
+ help='Platform that the locv file was generated on. Must be
+ one of {win32, linux2, macosx}')
+ parser.add_option('-s',
+ '--source',
+ dest='src_dir',
+ default=None,
+ help='Path to the source code and symbols')
+ parser.add_option('-d',
+ '--dash_root',
+ dest='dash_root',
+ default=None,
+ help='Root directory for the dashboard')
+ parser.add_option('-l',
+ '--lcov',
+ dest='lcov_path',
+ default=None,
+ help='Location of the LCOV file to process')
+ (options, args) = parser.parse_args()
+
+ if options.platform == None:
+ parser.error('Platform not specified')
+ if options.lcov_path == None:
+ parser.error('lcov file path not specified')
+ if options.src_dir == None:
+ parser.error('Source directory not specified')
+ if options.dash_root == None:
+ parser.error('Dashboard root not specified')
+ if options.platform == 'win32':
+ CleanWin32Lcov(options.lcov_path, options.src_dir)
+ percent = GenerateHtml(options.lcov_path, options.dash_root)
+ if percent == None:
+ # TODO(niranjan): Add logging.
+ print 'Failed to generate code coverage'
+ os.exit(1)
+ else:
+ # TODO(niranjan): Do something with the code coverage numbers
+ pass
+ else:
+ print 'Unsupported platform'
+ os.exit(1)
+
+
+if __name__ == '__main__':
+ main()
+
diff --git a/tools/memory_watcher/call_stack.cc b/tools/memory_watcher/call_stack.cc
index dcbe541..ff73278 100644
--- a/tools/memory_watcher/call_stack.cc
+++ b/tools/memory_watcher/call_stack.cc
@@ -1,376 +1,376 @@
-// Copyright (c) 2006-2008 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.
-
-#include "call_stack.h"
-#include <shlwapi.h>
-#include <tlhelp32.h>
-
-#include "memory_hook.h"
-#include "base/string_util.h"
-
-// Typedefs for explicit dynamic linking with functions exported from
-// dbghelp.dll.
-typedef BOOL (__stdcall *t_StackWalk64)(DWORD, HANDLE, HANDLE,
- LPSTACKFRAME64, PVOID,
- PREAD_PROCESS_MEMORY_ROUTINE64,
- PFUNCTION_TABLE_ACCESS_ROUTINE64,
- PGET_MODULE_BASE_ROUTINE64,
- PTRANSLATE_ADDRESS_ROUTINE64);
-typedef PVOID (__stdcall *t_SymFunctionTableAccess64)(HANDLE, DWORD64);
-typedef DWORD64 (__stdcall *t_SymGetModuleBase64)(HANDLE, DWORD64);
-typedef BOOL (__stdcall *t_SymCleanup)(HANDLE);
-typedef BOOL (__stdcall *t_SymGetSymFromAddr64)(HANDLE, DWORD64,
- PDWORD64, PIMAGEHLP_SYMBOL64);
-typedef BOOL (__stdcall *t_SymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD,
- PIMAGEHLP_LINE64);
-typedef BOOL (__stdcall *t_SymInitialize)(HANDLE, PCTSTR, BOOL);
-typedef DWORD (__stdcall *t_SymGetOptions)(void);
-typedef DWORD (__stdcall *t_SymSetOptions)(DWORD);
-typedef BOOL (__stdcall *t_SymGetSearchPath)(HANDLE, PTSTR, DWORD);
-typedef DWORD64 (__stdcall *t_SymLoadModule64)(HANDLE, HANDLE, PCSTR,
- PCSTR, DWORD64, DWORD);
-typedef BOOL (__stdcall *t_SymGetModuleInfo64)(HANDLE, DWORD64,
- PIMAGEHLP_MODULE64);
-
-// According to http://msdn2.microsoft.com/en-us/library/ms680650(VS.85).aspx
-// "All DbgHelp functions, such as this one, are single threaded. Therefore,
-// calls from more than one thread to this function will likely result in
-// unexpected behavior or memory corruption. To avoid this, you must
-// synchromize all concurrent calls from one thread to this function."
-//
-// dbghelp_lock_ is used to serialize access across all calls to the DbgHelp
-// library. This may be overly conservative (serializing them all together),
-// but does guarantee correctness.
-static Lock dbghelp_lock_;
-
-static t_StackWalk64 pStackWalk64 = NULL;
-static t_SymCleanup pSymCleanup = NULL;
-static t_SymGetSymFromAddr64 pSymGetSymFromAddr64 = NULL;
-static t_SymFunctionTableAccess64 pSymFunctionTableAccess64 = NULL;
-static t_SymGetModuleBase64 pSymGetModuleBase64 = NULL;
-static t_SymGetLineFromAddr64 pSymGetLineFromAddr64 = NULL;
-static t_SymInitialize pSymInitialize = NULL;
-static t_SymGetOptions pSymGetOptions = NULL;
-static t_SymSetOptions pSymSetOptions = NULL;
-static t_SymGetModuleInfo64 pSymGetModuleInfo64 = NULL;
-static t_SymGetSearchPath pSymGetSearchPath = NULL;
-static t_SymLoadModule64 pSymLoadModule64 = NULL;
-
-#define LOADPROC(module, name) do { \
- p##name = reinterpret_cast<t_##name>(GetProcAddress(module, #name)); \
- if (p##name == NULL) return false; \
-} while (0)
-
-// Dynamically load the DbgHelp library and supporting routines that we
-// will use.
-static bool LoadDbgHelp() {
- static bool loaded = false;
- if (!loaded) {
- AutoLock Lock(dbghelp_lock_);
-
- // Re-check if we've loaded successfully now that we have the lock.
- if (loaded)
- return true;
-
- // Load dbghelp.dll, and obtain pointers to the exported functions that we
- // will be using.
- HMODULE dbghelp_module = LoadLibrary(L"dbghelp.dll");
- if (dbghelp_module) {
- LOADPROC(dbghelp_module, StackWalk64);
- LOADPROC(dbghelp_module, SymFunctionTableAccess64);
- LOADPROC(dbghelp_module, SymGetModuleBase64);
- LOADPROC(dbghelp_module, SymCleanup);
- LOADPROC(dbghelp_module, SymGetSymFromAddr64);
- LOADPROC(dbghelp_module, SymGetLineFromAddr64);
- LOADPROC(dbghelp_module, SymInitialize);
- LOADPROC(dbghelp_module, SymGetOptions);
- LOADPROC(dbghelp_module, SymSetOptions);
- LOADPROC(dbghelp_module, SymGetModuleInfo64);
- LOADPROC(dbghelp_module, SymGetSearchPath);
- LOADPROC(dbghelp_module, SymLoadModule64);
- loaded = true;
- } else {
- return false;
- }
- }
- return loaded;
-}
-
-// Load the symbols for generating stack traces.
-static bool LoadSymbols(HANDLE process_handle) {
- static bool symbols_loaded = false;
- if (symbols_loaded) return true;
-
- BOOL ok;
-
- // Initialize the symbol engine.
- ok = pSymInitialize(process_handle, /* hProcess */
- NULL, /* UserSearchPath */
- FALSE); /* fInvadeProcess */
- if (!ok) return false;
-
- DWORD options = pSymGetOptions();
- options |= SYMOPT_LOAD_LINES;
- options |= SYMOPT_FAIL_CRITICAL_ERRORS;
- options |= SYMOPT_UNDNAME;
- options = pSymSetOptions(options);
-
- const DWORD kMaxSearchPath = 1024;
- TCHAR buf[kMaxSearchPath] = {0};
- ok = pSymGetSearchPath(process_handle, buf, kMaxSearchPath);
- if (!ok)
- return false;
-
- HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,
- GetCurrentProcessId());
- if (snapshot == INVALID_HANDLE_VALUE)
- return false;
-
- MODULEENTRY32W module;
- module.dwSize = sizeof(module); // Set the size of the structure.
- BOOL cont = Module32FirstW(snapshot, &module);
- while (cont) {
- DWORD64 base;
- // NOTE the SymLoadModule64 function has the peculiarity of accepting a
- // both unicode and ASCII strings even though the parameter is PSTR.
- base = pSymLoadModule64(process_handle,
- 0,
- reinterpret_cast<PSTR>(module.szExePath),
- reinterpret_cast<PSTR>(module.szModule),
- reinterpret_cast<DWORD64>(module.modBaseAddr),
- module.modBaseSize);
- if (base == 0) {
- int err = GetLastError();
- if (err != ERROR_MOD_NOT_FOUND && err != ERROR_INVALID_HANDLE)
- return false;
- }
- cont = Module32NextW(snapshot, &module);
- }
- CloseHandle(snapshot);
-
- symbols_loaded = true;
- return true;
-}
-
-
-CallStack::SymbolCache* CallStack::symbol_cache_;
-
-bool CallStack::Initialize() {
- // We need to delay load the symbol cache until after
- // the MemoryHook heap is alive.
- symbol_cache_ = new SymbolCache();
- return LoadDbgHelp();
-}
-
-CallStack::CallStack() {
- static LONG callstack_id = 0;
- frame_count_ = 0;
- hash_ = 0;
- id_ = InterlockedIncrement(&callstack_id);
-
- LoadDbgHelp();
- CHECK(GetStackTrace());
-}
-
-bool CallStack::IsEqual(const CallStack &target) {
- if (frame_count_ != target.frame_count_)
- return false; // They can't be equal if the sizes are different.
-
- // Walk the frames array until we
- // either find a mismatch, or until we reach the end of the call stacks.
- for (int index = 0; index < frame_count_; index++) {
- if (frames_[index] != target.frames_[index])
- return false; // Found a mismatch. They are not equal.
- }
-
- // Reached the end of the call stacks. They are equal.
- return true;
-}
-
-void CallStack::AddFrame(DWORD_PTR pc) {
- DCHECK(frame_count_ < kMaxTraceFrames);
- frames_[frame_count_++] = pc;
-
- // Create a unique id for this CallStack.
- pc = pc + (frame_count_ * 13); // Alter the PC based on position in stack.
- hash_ = ~hash_ + (pc << 15);
- hash_ = hash_ ^ (pc >> 12);
- hash_ = hash_ + (pc << 2);
- hash_ = hash_ ^ (pc >> 4);
- hash_ = hash_ * 2057;
- hash_ = hash_ ^ (pc >> 16);
-}
-
-bool CallStack::GetStackTrace() {
- // Initialize the context record.
- CONTEXT context;
- memset(&context, 0, sizeof(context));
- context.ContextFlags = CONTEXT_FULL;
- __asm call x
- __asm x: pop eax
- __asm mov context.Eip, eax
- __asm mov context.Ebp, ebp
- __asm mov context.Esp, esp
-
- STACKFRAME64 frame;
- memset(&frame, 0, sizeof(frame));
-
-#ifdef _M_IX86
- DWORD image_type = IMAGE_FILE_MACHINE_I386;
- frame.AddrPC.Offset = context.Eip;
- frame.AddrPC.Mode = AddrModeFlat;
- frame.AddrFrame.Offset = context.Ebp;
- frame.AddrFrame.Mode = AddrModeFlat;
- frame.AddrStack.Offset = context.Esp;
- frame.AddrStack.Mode = AddrModeFlat;
-#elif
- NOT IMPLEMENTED!
-#endif
-
- HANDLE current_process = GetCurrentProcess();
- HANDLE current_thread = GetCurrentThread();
-
- // Walk the stack.
- unsigned int count = 0;
- {
- AutoLock lock(dbghelp_lock_);
-
- while (count < kMaxTraceFrames) {
- count++;
- if (!pStackWalk64(image_type,
- current_process,
- current_thread,
- &frame,
- &context,
- 0,
- pSymFunctionTableAccess64,
- pSymGetModuleBase64,
- NULL))
- break; // Couldn't trace back through any more frames.
-
- if (frame.AddrFrame.Offset == 0)
- continue; // End of stack.
-
- // Push this frame's program counter onto the provided CallStack.
- AddFrame((DWORD_PTR)frame.AddrPC.Offset);
- }
- }
- return true;
-}
-
-void CallStack::ToString(std::string* output) {
- static const int kStackWalkMaxNameLen = MAX_SYM_NAME;
- HANDLE current_process = GetCurrentProcess();
-
- if (!LoadSymbols(current_process)) {
- *output = "Error";
- return;
- }
-
- AutoLock lock(dbghelp_lock_);
-
- // Iterate through each frame in the call stack.
- for (int32 index = 0; index < frame_count_; index++) {
- std::string line;
-
- DWORD_PTR intruction_pointer = frame(index);
-
- SymbolCache::iterator it;
- it = symbol_cache_->find( intruction_pointer );
- if (it != symbol_cache_->end()) {
- line = it->second;
- } else {
- // Try to locate a symbol for this frame.
- DWORD64 symbol_displacement = 0;
- ULONG64 buffer[(sizeof(IMAGEHLP_SYMBOL64) +
- sizeof(TCHAR)*kStackWalkMaxNameLen +
- sizeof(ULONG64) - 1) / sizeof(ULONG64)];
- IMAGEHLP_SYMBOL64* symbol = reinterpret_cast<IMAGEHLP_SYMBOL64*>(buffer);
- memset(buffer, 0, sizeof(buffer));
- symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
- symbol->MaxNameLength = kStackWalkMaxNameLen;
- BOOL ok = pSymGetSymFromAddr64(current_process, // hProcess
- intruction_pointer, // Address
- &symbol_displacement, // Displacement
- symbol); // Symbol
- if (ok) {
- // Try to locate more source information for the symbol.
- IMAGEHLP_LINE64 Line;
- memset(&Line, 0, sizeof(Line));
- Line.SizeOfStruct = sizeof(Line);
- DWORD line_displacement;
- ok = pSymGetLineFromAddr64(current_process,
- intruction_pointer,
- &line_displacement,
- &Line);
- if (ok) {
- // Skip junk symbols from our internal stuff.
- if (strstr(symbol->Name, "CallStack::") ||
- strstr(symbol->Name, "MemoryWatcher::") ||
- strstr(symbol->Name, "Perftools_") ||
- strstr(symbol->Name, "MemoryHook::") ) {
- // Just record a blank string.
- (*symbol_cache_)[intruction_pointer] = std::string("");
- continue;
- }
-
- line += " ";
- line += static_cast<char*>(Line.FileName);
- line += " (";
- line += IntToString(Line.LineNumber);
- line += "): ";
- line += symbol->Name;
- line += "\n";
- } else {
- line += " unknown (0):";
- line += symbol->Name;
- line += "\n";
- }
- } else {
- // OK - couldn't get any info. Try for the module.
- IMAGEHLP_MODULE64 module_info;
- module_info.SizeOfStruct = sizeof(module_info);
- if (pSymGetModuleInfo64(current_process, intruction_pointer,
- &module_info)) {
- line += " (";
- line += static_cast<char*>(module_info.ModuleName);
- line += ")\n";
- } else {
- line += " ???\n";
- }
- }
- }
-
- (*symbol_cache_)[intruction_pointer] = line;
- *output += line;
- }
- *output += "==================\n";
-}
-
-
-Lock AllocationStack::freelist_lock_;
-AllocationStack* AllocationStack::freelist_ = NULL;
-
-void* AllocationStack::operator new(size_t size) {
- DCHECK(size == sizeof(AllocationStack));
- {
- AutoLock lock(freelist_lock_);
- if (freelist_ != NULL) {
- AllocationStack* stack = freelist_;
- freelist_ = freelist_->next_;
- stack->next_ = NULL;
- return stack;
- }
- }
- return MemoryHook::Alloc(size);
-}
-
-void AllocationStack::operator delete(void* ptr) {
- AllocationStack *stack = reinterpret_cast<AllocationStack*>(ptr);
- AutoLock lock(freelist_lock_);
- DCHECK(stack->next_ == NULL);
- stack->next_ = freelist_;
- freelist_ = stack;
-}
-
+// Copyright (c) 2006-2008 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.
+
+#include "call_stack.h"
+#include <shlwapi.h>
+#include <tlhelp32.h>
+
+#include "memory_hook.h"
+#include "base/string_util.h"
+
+// Typedefs for explicit dynamic linking with functions exported from
+// dbghelp.dll.
+typedef BOOL (__stdcall *t_StackWalk64)(DWORD, HANDLE, HANDLE,
+ LPSTACKFRAME64, PVOID,
+ PREAD_PROCESS_MEMORY_ROUTINE64,
+ PFUNCTION_TABLE_ACCESS_ROUTINE64,
+ PGET_MODULE_BASE_ROUTINE64,
+ PTRANSLATE_ADDRESS_ROUTINE64);
+typedef PVOID (__stdcall *t_SymFunctionTableAccess64)(HANDLE, DWORD64);
+typedef DWORD64 (__stdcall *t_SymGetModuleBase64)(HANDLE, DWORD64);
+typedef BOOL (__stdcall *t_SymCleanup)(HANDLE);
+typedef BOOL (__stdcall *t_SymGetSymFromAddr64)(HANDLE, DWORD64,
+ PDWORD64, PIMAGEHLP_SYMBOL64);
+typedef BOOL (__stdcall *t_SymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD,
+ PIMAGEHLP_LINE64);
+typedef BOOL (__stdcall *t_SymInitialize)(HANDLE, PCTSTR, BOOL);
+typedef DWORD (__stdcall *t_SymGetOptions)(void);
+typedef DWORD (__stdcall *t_SymSetOptions)(DWORD);
+typedef BOOL (__stdcall *t_SymGetSearchPath)(HANDLE, PTSTR, DWORD);
+typedef DWORD64 (__stdcall *t_SymLoadModule64)(HANDLE, HANDLE, PCSTR,
+ PCSTR, DWORD64, DWORD);
+typedef BOOL (__stdcall *t_SymGetModuleInfo64)(HANDLE, DWORD64,
+ PIMAGEHLP_MODULE64);
+
+// According to http://msdn2.microsoft.com/en-us/library/ms680650(VS.85).aspx
+// "All DbgHelp functions, such as this one, are single threaded. Therefore,
+// calls from more than one thread to this function will likely result in
+// unexpected behavior or memory corruption. To avoid this, you must
+// synchromize all concurrent calls from one thread to this function."
+//
+// dbghelp_lock_ is used to serialize access across all calls to the DbgHelp
+// library. This may be overly conservative (serializing them all together),
+// but does guarantee correctness.
+static Lock dbghelp_lock_;
+
+static t_StackWalk64 pStackWalk64 = NULL;
+static t_SymCleanup pSymCleanup = NULL;
+static t_SymGetSymFromAddr64 pSymGetSymFromAddr64 = NULL;
+static t_SymFunctionTableAccess64 pSymFunctionTableAccess64 = NULL;
+static t_SymGetModuleBase64 pSymGetModuleBase64 = NULL;
+static t_SymGetLineFromAddr64 pSymGetLineFromAddr64 = NULL;
+static t_SymInitialize pSymInitialize = NULL;
+static t_SymGetOptions pSymGetOptions = NULL;
+static t_SymSetOptions pSymSetOptions = NULL;
+static t_SymGetModuleInfo64 pSymGetModuleInfo64 = NULL;
+static t_SymGetSearchPath pSymGetSearchPath = NULL;
+static t_SymLoadModule64 pSymLoadModule64 = NULL;
+
+#define LOADPROC(module, name) do { \
+ p##name = reinterpret_cast<t_##name>(GetProcAddress(module, #name)); \
+ if (p##name == NULL) return false; \
+} while (0)
+
+// Dynamically load the DbgHelp library and supporting routines that we
+// will use.
+static bool LoadDbgHelp() {
+ static bool loaded = false;
+ if (!loaded) {
+ AutoLock Lock(dbghelp_lock_);
+
+ // Re-check if we've loaded successfully now that we have the lock.
+ if (loaded)
+ return true;
+
+ // Load dbghelp.dll, and obtain pointers to the exported functions that we
+ // will be using.
+ HMODULE dbghelp_module = LoadLibrary(L"dbghelp.dll");
+ if (dbghelp_module) {
+ LOADPROC(dbghelp_module, StackWalk64);
+ LOADPROC(dbghelp_module, SymFunctionTableAccess64);
+ LOADPROC(dbghelp_module, SymGetModuleBase64);
+ LOADPROC(dbghelp_module, SymCleanup);
+ LOADPROC(dbghelp_module, SymGetSymFromAddr64);
+ LOADPROC(dbghelp_module, SymGetLineFromAddr64);
+ LOADPROC(dbghelp_module, SymInitialize);
+ LOADPROC(dbghelp_module, SymGetOptions);
+ LOADPROC(dbghelp_module, SymSetOptions);
+ LOADPROC(dbghelp_module, SymGetModuleInfo64);
+ LOADPROC(dbghelp_module, SymGetSearchPath);
+ LOADPROC(dbghelp_module, SymLoadModule64);
+ loaded = true;
+ } else {
+ return false;
+ }
+ }
+ return loaded;
+}
+
+// Load the symbols for generating stack traces.
+static bool LoadSymbols(HANDLE process_handle) {
+ static bool symbols_loaded = false;
+ if (symbols_loaded) return true;
+
+ BOOL ok;
+
+ // Initialize the symbol engine.
+ ok = pSymInitialize(process_handle, /* hProcess */
+ NULL, /* UserSearchPath */
+ FALSE); /* fInvadeProcess */
+ if (!ok) return false;
+
+ DWORD options = pSymGetOptions();
+ options |= SYMOPT_LOAD_LINES;
+ options |= SYMOPT_FAIL_CRITICAL_ERRORS;
+ options |= SYMOPT_UNDNAME;
+ options = pSymSetOptions(options);
+
+ const DWORD kMaxSearchPath = 1024;
+ TCHAR buf[kMaxSearchPath] = {0};
+ ok = pSymGetSearchPath(process_handle, buf, kMaxSearchPath);
+ if (!ok)
+ return false;
+
+ HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,
+ GetCurrentProcessId());
+ if (snapshot == INVALID_HANDLE_VALUE)
+ return false;
+
+ MODULEENTRY32W module;
+ module.dwSize = sizeof(module); // Set the size of the structure.
+ BOOL cont = Module32FirstW(snapshot, &module);
+ while (cont) {
+ DWORD64 base;
+ // NOTE the SymLoadModule64 function has the peculiarity of accepting a
+ // both unicode and ASCII strings even though the parameter is PSTR.
+ base = pSymLoadModule64(process_handle,
+ 0,
+ reinterpret_cast<PSTR>(module.szExePath),
+ reinterpret_cast<PSTR>(module.szModule),
+ reinterpret_cast<DWORD64>(module.modBaseAddr),
+ module.modBaseSize);
+ if (base == 0) {
+ int err = GetLastError();
+ if (err != ERROR_MOD_NOT_FOUND && err != ERROR_INVALID_HANDLE)
+ return false;
+ }
+ cont = Module32NextW(snapshot, &module);
+ }
+ CloseHandle(snapshot);
+
+ symbols_loaded = true;
+ return true;
+}
+
+
+CallStack::SymbolCache* CallStack::symbol_cache_;
+
+bool CallStack::Initialize() {
+ // We need to delay load the symbol cache until after
+ // the MemoryHook heap is alive.
+ symbol_cache_ = new SymbolCache();
+ return LoadDbgHelp();
+}
+
+CallStack::CallStack() {
+ static LONG callstack_id = 0;
+ frame_count_ = 0;
+ hash_ = 0;
+ id_ = InterlockedIncrement(&callstack_id);
+
+ LoadDbgHelp();
+ CHECK(GetStackTrace());
+}
+
+bool CallStack::IsEqual(const CallStack &target) {
+ if (frame_count_ != target.frame_count_)
+ return false; // They can't be equal if the sizes are different.
+
+ // Walk the frames array until we
+ // either find a mismatch, or until we reach the end of the call stacks.
+ for (int index = 0; index < frame_count_; index++) {
+ if (frames_[index] != target.frames_[index])
+ return false; // Found a mismatch. They are not equal.
+ }
+
+ // Reached the end of the call stacks. They are equal.
+ return true;
+}
+
+void CallStack::AddFrame(DWORD_PTR pc) {
+ DCHECK(frame_count_ < kMaxTraceFrames);
+ frames_[frame_count_++] = pc;
+
+ // Create a unique id for this CallStack.
+ pc = pc + (frame_count_ * 13); // Alter the PC based on position in stack.
+ hash_ = ~hash_ + (pc << 15);
+ hash_ = hash_ ^ (pc >> 12);
+ hash_ = hash_ + (pc << 2);
+ hash_ = hash_ ^ (pc >> 4);
+ hash_ = hash_ * 2057;
+ hash_ = hash_ ^ (pc >> 16);
+}
+
+bool CallStack::GetStackTrace() {
+ // Initialize the context record.
+ CONTEXT context;
+ memset(&context, 0, sizeof(context));
+ context.ContextFlags = CONTEXT_FULL;
+ __asm call x
+ __asm x: pop eax
+ __asm mov context.Eip, eax
+ __asm mov context.Ebp, ebp
+ __asm mov context.Esp, esp
+
+ STACKFRAME64 frame;
+ memset(&frame, 0, sizeof(frame));
+
+#ifdef _M_IX86
+ DWORD image_type = IMAGE_FILE_MACHINE_I386;
+ frame.AddrPC.Offset = context.Eip;
+ frame.AddrPC.Mode = AddrModeFlat;
+ frame.AddrFrame.Offset = context.Ebp;
+ frame.AddrFrame.Mode = AddrModeFlat;
+ frame.AddrStack.Offset = context.Esp;
+ frame.AddrStack.Mode = AddrModeFlat;
+#elif
+ NOT IMPLEMENTED!
+#endif
+
+ HANDLE current_process = GetCurrentProcess();
+ HANDLE current_thread = GetCurrentThread();
+
+ // Walk the stack.
+ unsigned int count = 0;
+ {
+ AutoLock lock(dbghelp_lock_);
+
+ while (count < kMaxTraceFrames) {
+ count++;
+ if (!pStackWalk64(image_type,
+ current_process,
+ current_thread,
+ &frame,
+ &context,
+ 0,
+ pSymFunctionTableAccess64,
+ pSymGetModuleBase64,
+ NULL))
+ break; // Couldn't trace back through any more frames.
+
+ if (frame.AddrFrame.Offset == 0)
+ continue; // End of stack.
+
+ // Push this frame's program counter onto the provided CallStack.
+ AddFrame((DWORD_PTR)frame.AddrPC.Offset);
+ }
+ }
+ return true;
+}
+
+void CallStack::ToString(std::string* output) {
+ static const int kStackWalkMaxNameLen = MAX_SYM_NAME;
+ HANDLE current_process = GetCurrentProcess();
+
+ if (!LoadSymbols(current_process)) {
+ *output = "Error";
+ return;
+ }
+
+ AutoLock lock(dbghelp_lock_);
+
+ // Iterate through each frame in the call stack.
+ for (int32 index = 0; index < frame_count_; index++) {
+ std::string line;
+
+ DWORD_PTR intruction_pointer = frame(index);
+
+ SymbolCache::iterator it;
+ it = symbol_cache_->find( intruction_pointer );
+ if (it != symbol_cache_->end()) {
+ line = it->second;
+ } else {
+ // Try to locate a symbol for this frame.
+ DWORD64 symbol_displacement = 0;
+ ULONG64 buffer[(sizeof(IMAGEHLP_SYMBOL64) +
+ sizeof(TCHAR)*kStackWalkMaxNameLen +
+ sizeof(ULONG64) - 1) / sizeof(ULONG64)];
+ IMAGEHLP_SYMBOL64* symbol = reinterpret_cast<IMAGEHLP_SYMBOL64*>(buffer);
+ memset(buffer, 0, sizeof(buffer));
+ symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
+ symbol->MaxNameLength = kStackWalkMaxNameLen;
+ BOOL ok = pSymGetSymFromAddr64(current_process, // hProcess
+ intruction_pointer, // Address
+ &symbol_displacement, // Displacement
+ symbol); // Symbol
+ if (ok) {
+ // Try to locate more source information for the symbol.
+ IMAGEHLP_LINE64 Line;
+ memset(&Line, 0, sizeof(Line));
+ Line.SizeOfStruct = sizeof(Line);
+ DWORD line_displacement;
+ ok = pSymGetLineFromAddr64(current_process,
+ intruction_pointer,
+ &line_displacement,
+ &Line);
+ if (ok) {
+ // Skip junk symbols from our internal stuff.
+ if (strstr(symbol->Name, "CallStack::") ||
+ strstr(symbol->Name, "MemoryWatcher::") ||
+ strstr(symbol->Name, "Perftools_") ||
+ strstr(symbol->Name, "MemoryHook::") ) {
+ // Just record a blank string.
+ (*symbol_cache_)[intruction_pointer] = std::string("");
+ continue;
+ }
+
+ line += " ";
+ line += static_cast<char*>(Line.FileName);
+ line += " (";
+ line += IntToString(Line.LineNumber);
+ line += "): ";
+ line += symbol->Name;
+ line += "\n";
+ } else {
+ line += " unknown (0):";
+ line += symbol->Name;
+ line += "\n";
+ }
+ } else {
+ // OK - couldn't get any info. Try for the module.
+ IMAGEHLP_MODULE64 module_info;
+ module_info.SizeOfStruct = sizeof(module_info);
+ if (pSymGetModuleInfo64(current_process, intruction_pointer,
+ &module_info)) {
+ line += " (";
+ line += static_cast<char*>(module_info.ModuleName);
+ line += ")\n";
+ } else {
+ line += " ???\n";
+ }
+ }
+ }
+
+ (*symbol_cache_)[intruction_pointer] = line;
+ *output += line;
+ }
+ *output += "==================\n";
+}
+
+
+Lock AllocationStack::freelist_lock_;
+AllocationStack* AllocationStack::freelist_ = NULL;
+
+void* AllocationStack::operator new(size_t size) {
+ DCHECK(size == sizeof(AllocationStack));
+ {
+ AutoLock lock(freelist_lock_);
+ if (freelist_ != NULL) {
+ AllocationStack* stack = freelist_;
+ freelist_ = freelist_->next_;
+ stack->next_ = NULL;
+ return stack;
+ }
+ }
+ return MemoryHook::Alloc(size);
+}
+
+void AllocationStack::operator delete(void* ptr) {
+ AllocationStack *stack = reinterpret_cast<AllocationStack*>(ptr);
+ AutoLock lock(freelist_lock_);
+ DCHECK(stack->next_ == NULL);
+ stack->next_ = freelist_;
+ freelist_ = stack;
+}
+