summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/build/android/stacktrace
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/libwebrtc/build/android/stacktrace
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/build/android/stacktrace')
-rw-r--r--third_party/libwebrtc/build/android/stacktrace/BUILD.gn28
-rw-r--r--third_party/libwebrtc/build/android/stacktrace/README.md28
-rwxr-xr-xthird_party/libwebrtc/build/android/stacktrace/crashpad_stackwalker.py175
-rw-r--r--third_party/libwebrtc/build/android/stacktrace/java/org/chromium/build/FlushingReTrace.java116
-rw-r--r--third_party/libwebrtc/build/android/stacktrace/java_deobfuscate.jarbin0 -> 3113 bytes
-rwxr-xr-xthird_party/libwebrtc/build/android/stacktrace/java_deobfuscate.py39
-rwxr-xr-xthird_party/libwebrtc/build/android/stacktrace/java_deobfuscate_test.py172
-rwxr-xr-xthird_party/libwebrtc/build/android/stacktrace/stackwalker.py137
8 files changed, 695 insertions, 0 deletions
diff --git a/third_party/libwebrtc/build/android/stacktrace/BUILD.gn b/third_party/libwebrtc/build/android/stacktrace/BUILD.gn
new file mode 100644
index 0000000000..ce13a15b4b
--- /dev/null
+++ b/third_party/libwebrtc/build/android/stacktrace/BUILD.gn
@@ -0,0 +1,28 @@
+# Copyright 2017 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("//build/config/android/rules.gni")
+
+java_library("java_deobfuscate_java") {
+ sources = [ "java/org/chromium/build/FlushingReTrace.java" ]
+
+ # Avoid using java_prebuilt() to ensure all uses go through the checked-in
+ # wrapper script.
+ input_jars_paths = [
+ "//third_party/proguard/lib/proguard603.jar",
+ "//third_party/proguard/lib/retrace603.jar",
+ ]
+}
+
+# Use the checked-in copy of the wrapper script & .jar rather than the built
+# one to simplify usage of the tool.
+group("java_deobfuscate") {
+ data = [
+ "java_deobfuscate.py",
+ "java_deobfuscate.jar",
+ "//third_party/proguard/lib/proguard603.jar",
+ "//third_party/proguard/lib/retrace603.jar",
+ ]
+ deps = [ "//third_party/jdk:java_data" ]
+}
diff --git a/third_party/libwebrtc/build/android/stacktrace/README.md b/third_party/libwebrtc/build/android/stacktrace/README.md
new file mode 100644
index 0000000000..58ea94be98
--- /dev/null
+++ b/third_party/libwebrtc/build/android/stacktrace/README.md
@@ -0,0 +1,28 @@
+# java_deobfuscate.py
+
+A wrapper around ProGuard's ReTrace tool, which:
+
+1) Updates the regular expression used to identify stack lines, and
+2) Streams its output.
+
+The second point here is what allows you to run:
+
+ adb logcat | build/android/stacktrace/java_deobfuscate.py out/Default/apks/ChromePublic.apk.mapping
+
+And have it actually show output without logcat terminating.
+
+
+## Update Instructions:
+
+ ninja -C out/Release java_deobfuscate
+ cp out/Release/lib.java/build/android/stacktrace/java_deobfuscate.jar build/android/stacktrace
+
+# stackwalker.py
+
+Extracts Breakpad microdumps from a log file and uses `stackwalker` to symbolize
+them.
+
+
+# crashpad_stackwalker.py
+
+Fetches Crashpad dumps from a given device, walks and symbolizes the stacks.
diff --git a/third_party/libwebrtc/build/android/stacktrace/crashpad_stackwalker.py b/third_party/libwebrtc/build/android/stacktrace/crashpad_stackwalker.py
new file mode 100755
index 0000000000..ab5dfe195c
--- /dev/null
+++ b/third_party/libwebrtc/build/android/stacktrace/crashpad_stackwalker.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env vpython3
+#
+# Copyright 2019 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.
+
+# Fetches Crashpad dumps from a given device, walks and symbolizes the stacks.
+# All the non-trivial operations are performed by generate_breakpad_symbols.py,
+# dump_syms, minidump_dump and minidump_stackwalk.
+
+import argparse
+import logging
+import os
+import posixpath
+import re
+import sys
+import shutil
+import subprocess
+import tempfile
+
+_BUILD_ANDROID_PATH = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..'))
+sys.path.append(_BUILD_ANDROID_PATH)
+import devil_chromium
+from devil.android import device_utils
+from devil.utils import timeout_retry
+
+
+def _CreateSymbolsDir(build_path, dynamic_library_names):
+ generator = os.path.normpath(
+ os.path.join(_BUILD_ANDROID_PATH, '..', '..', 'components', 'crash',
+ 'content', 'tools', 'generate_breakpad_symbols.py'))
+ syms_dir = os.path.join(build_path, 'crashpad_syms')
+ shutil.rmtree(syms_dir, ignore_errors=True)
+ os.mkdir(syms_dir)
+ for lib in dynamic_library_names:
+ unstripped_library_path = os.path.join(build_path, 'lib.unstripped', lib)
+ if not os.path.exists(unstripped_library_path):
+ continue
+ logging.info('Generating symbols for: %s', unstripped_library_path)
+ cmd = [
+ generator,
+ '--symbols-dir',
+ syms_dir,
+ '--build-dir',
+ build_path,
+ '--binary',
+ unstripped_library_path,
+ '--platform',
+ 'android',
+ ]
+ return_code = subprocess.call(cmd)
+ if return_code != 0:
+ logging.error('Could not extract symbols, command failed: %s',
+ ' '.join(cmd))
+ return syms_dir
+
+
+def _ChooseLatestCrashpadDump(device, crashpad_dump_path):
+ if not device.PathExists(crashpad_dump_path):
+ logging.warning('Crashpad dump directory does not exist: %s',
+ crashpad_dump_path)
+ return None
+ latest = None
+ latest_timestamp = 0
+ for crashpad_file in device.ListDirectory(crashpad_dump_path):
+ if crashpad_file.endswith('.dmp'):
+ stat = device.StatPath(posixpath.join(crashpad_dump_path, crashpad_file))
+ current_timestamp = stat['st_mtime']
+ if current_timestamp > latest_timestamp:
+ latest_timestamp = current_timestamp
+ latest = crashpad_file
+ return latest
+
+
+def _ExtractLibraryNamesFromDump(build_path, dump_path):
+ default_library_name = 'libmonochrome.so'
+ dumper_path = os.path.join(build_path, 'minidump_dump')
+ if not os.access(dumper_path, os.X_OK):
+ logging.warning(
+ 'Cannot extract library name from dump because %s is not found, '
+ 'default to: %s', dumper_path, default_library_name)
+ return [default_library_name]
+ p = subprocess.Popen([dumper_path, dump_path],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ if p.returncode != 0:
+ # Dumper errors often do not affect stack walkability, just a warning.
+ logging.warning('Reading minidump failed with output:\n%s', stderr)
+
+ library_names = []
+ module_library_line_re = re.compile(r'[(]code_file[)]\s+= '
+ r'"(?P<library_name>lib[^. ]+.so)"')
+ in_module = False
+ for line in stdout.splitlines():
+ line = line.lstrip().rstrip('\n')
+ if line == 'MDRawModule':
+ in_module = True
+ continue
+ if line == '':
+ in_module = False
+ continue
+ if in_module:
+ m = module_library_line_re.match(line)
+ if m:
+ library_names.append(m.group('library_name'))
+ if not library_names:
+ logging.warning(
+ 'Could not find any library name in the dump, '
+ 'default to: %s', default_library_name)
+ return [default_library_name]
+ return library_names
+
+
+def main():
+ logging.basicConfig(level=logging.INFO)
+ parser = argparse.ArgumentParser(
+ description='Fetches Crashpad dumps from a given device, '
+ 'walks and symbolizes the stacks.')
+ parser.add_argument('--device', required=True, help='Device serial number')
+ parser.add_argument('--adb-path', help='Path to the "adb" command')
+ parser.add_argument(
+ '--build-path',
+ required=True,
+ help='Build output directory, equivalent to CHROMIUM_OUTPUT_DIR')
+ parser.add_argument(
+ '--chrome-cache-path',
+ required=True,
+ help='Directory on the device where Chrome stores cached files,'
+ ' crashpad stores dumps in a subdirectory of it')
+ args = parser.parse_args()
+
+ stackwalk_path = os.path.join(args.build_path, 'minidump_stackwalk')
+ if not os.path.exists(stackwalk_path):
+ logging.error('Missing minidump_stackwalk executable')
+ return 1
+
+ devil_chromium.Initialize(output_directory=args.build_path,
+ adb_path=args.adb_path)
+ device = device_utils.DeviceUtils(args.device)
+
+ device_crashpad_path = posixpath.join(args.chrome_cache_path, 'Crashpad',
+ 'pending')
+
+ def CrashpadDumpExists():
+ return _ChooseLatestCrashpadDump(device, device_crashpad_path)
+
+ crashpad_file = timeout_retry.WaitFor(
+ CrashpadDumpExists, wait_period=1, max_tries=9)
+ if not crashpad_file:
+ logging.error('Could not locate a crashpad dump')
+ return 1
+
+ dump_dir = tempfile.mkdtemp()
+ symbols_dir = None
+ try:
+ device.PullFile(
+ device_path=posixpath.join(device_crashpad_path, crashpad_file),
+ host_path=dump_dir)
+ dump_full_path = os.path.join(dump_dir, crashpad_file)
+ library_names = _ExtractLibraryNamesFromDump(args.build_path,
+ dump_full_path)
+ symbols_dir = _CreateSymbolsDir(args.build_path, library_names)
+ stackwalk_cmd = [stackwalk_path, dump_full_path, symbols_dir]
+ subprocess.call(stackwalk_cmd)
+ finally:
+ shutil.rmtree(dump_dir, ignore_errors=True)
+ if symbols_dir:
+ shutil.rmtree(symbols_dir, ignore_errors=True)
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/third_party/libwebrtc/build/android/stacktrace/java/org/chromium/build/FlushingReTrace.java b/third_party/libwebrtc/build/android/stacktrace/java/org/chromium/build/FlushingReTrace.java
new file mode 100644
index 0000000000..baa931328b
--- /dev/null
+++ b/third_party/libwebrtc/build/android/stacktrace/java/org/chromium/build/FlushingReTrace.java
@@ -0,0 +1,116 @@
+// Copyright 2017 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.
+
+package org.chromium.build;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+
+import proguard.retrace.ReTrace;
+
+/**
+ * A wrapper around ReTrace that:
+ * 1. Hardcodes a more useful line regular expression
+ * 2. Disables output buffering
+ */
+public class FlushingReTrace {
+ // E.g.: D/ConnectivityService(18029): Message
+ // E.g.: W/GCM ( 151): Message
+ // E.g.: 09-08 14:22:59.995 18029 18055 I ProcessStatsService: Message
+ // E.g.: 09-08 14:30:59.145 17731 18020 D MDnsDS : Message
+ private static final String LOGCAT_PREFIX =
+ "(?:[VDIWEF]/.*?\\( *\\d+\\): |\\d\\d-\\d\\d [0-9:. ]+[VDIWEF] .*?: )?";
+
+ // Note: Order of these sub-patterns defines their precedence.
+ // Note: Deobfuscation of methods without the presense of line numbers basically never works.
+ // There is a test for these pattern at //build/android/stacktrace/java_deobfuscate_test.py
+ private static final String LINE_PARSE_REGEX =
+ // Eagerly match logcat prefix to avoid conflicting with the patterns below.
+ LOGCAT_PREFIX
+ + "(?:"
+ // Based on default ReTrace regex, but with whitespaces allowed in file:line parentheses
+ // and "at" changed to to allow :
+ // E.g.: 06-22 13:58:02.895 4674 4674 E THREAD_STATE: bLA.a( PG : 173 )
+ // Normal stack trace lines look like:
+ // \tat org.chromium.chrome.browser.tab.Tab.handleJavaCrash(Tab.java:682)
+ + "(?:.*?(?::|\\bat)\\s+%c\\.%m\\s*\\(\\s*%s(?:\\s*:\\s*%l\\s*)?\\))|"
+ // E.g.: Caused by: java.lang.NullPointerException: Attempt to read from field 'int bLA'
+ // on a null object reference
+ + "(?:.*java\\.lang\\.NullPointerException.*[\"']%t\\s*%c\\.(?:%f|%m\\(%a\\))[\"'].*)|"
+ // E.g.: java.lang.VerifyError: bLA
+ + "(?:java\\.lang\\.VerifyError: %c)|"
+ // E.g.: java.lang.NoSuchFieldError: No instance field e of type L...; in class LbxK;
+ + "(?:java\\.lang\\.NoSuchFieldError: No instance field %f of type .*? in class L%C;)|"
+ // E.g.: Object of type Clazz was not destroyed... (See LifetimeAssert.java)
+ + "(?:.*?Object of type %c .*)|"
+ // E.g.: VFY: unable to resolve new-instance 3810 (LSome/Framework/Class;) in Lfoo/Bar;
+ + "(?:.*L%C;.*)|"
+ // E.g.: END SomeTestClass#someMethod
+ + "(?:.*?%c#%m.*?)|"
+ // Special-case for a common junit logcat message:
+ // E.g.: java.lang.NoClassDefFoundError: SomeFrameworkClass in isTestClass for Foo
+ + "(?:.* isTestClass for %c)|"
+ // E.g.: Caused by: java.lang.RuntimeException: Intentional Java Crash
+ + "(?:Caused by: %c:.*)|"
+ // Quoted values and lines that end with a class / class+method:
+ // E.g.: The class: Foo
+ // E.g.: INSTRUMENTATION_STATUS: class=Foo
+ // E.g.: NoClassDefFoundError: SomeFrameworkClass in isTestClass for Foo
+ // E.g.: Could not find class 'SomeFrameworkClass', referenced from method Foo.bar
+ // E.g.: Could not find method SomeFrameworkMethod, referenced from method Foo.bar
+ // E.g.: The member "Foo.bar"
+ // E.g.: The class "Foobar"
+ // Be careful about matching %c without %m since language tags look like class names.
+ + "(?:.*?%c\\.%m)|"
+ + "(?:.*?\"%c\\.%m\".*)|"
+ + "(?:.*\\b(?:[Cc]lass|[Tt]ype)\\b.*?\"%c\".*)|"
+ + "(?:.*\\b(?:[Cc]lass|[Tt]ype)\\b.*?%c)|"
+ // E.g.: java.lang.RuntimeException: Intentional Java Crash
+ + "(?:%c:.*)|"
+ // See if entire line matches a class name (e.g. for manual deobfuscation)
+ + "(?:%c)"
+ + ")";
+
+ private static void usage() {
+ System.err.println("Usage: echo $OBFUSCATED_CLASS | java_deobfuscate Foo.apk.mapping");
+ System.err.println("Usage: java_deobfuscate Foo.apk.mapping < foo.log");
+ System.err.println("Note: Deobfuscation of symbols outside the context of stack "
+ + "traces will work only when lines match the regular expression defined "
+ + "in FlushingReTrace.java.");
+ System.err.println("Also: Deobfuscation of method names without associated line "
+ + "numbers does not seem to work.");
+ System.exit(1);
+ }
+
+ public static void main(String[] args) {
+ if (args.length != 1 || args[0].startsWith("-")) {
+ usage();
+ }
+
+ File mappingFile = new File(args[0]);
+ try {
+ LineNumberReader reader = new LineNumberReader(
+ new BufferedReader(new InputStreamReader(System.in, "UTF-8")));
+
+ // Enabling autoFlush is the main difference from ReTrace.main().
+ boolean autoFlush = true;
+ PrintWriter writer =
+ new PrintWriter(new OutputStreamWriter(System.out, "UTF-8"), autoFlush);
+
+ boolean verbose = false;
+ new ReTrace(LINE_PARSE_REGEX, verbose, mappingFile).retrace(reader, writer);
+ } catch (IOException ex) {
+ // Print a verbose stack trace.
+ ex.printStackTrace();
+ System.exit(1);
+ }
+
+ System.exit(0);
+ }
+}
diff --git a/third_party/libwebrtc/build/android/stacktrace/java_deobfuscate.jar b/third_party/libwebrtc/build/android/stacktrace/java_deobfuscate.jar
new file mode 100644
index 0000000000..36a1b706a3
--- /dev/null
+++ b/third_party/libwebrtc/build/android/stacktrace/java_deobfuscate.jar
Binary files differ
diff --git a/third_party/libwebrtc/build/android/stacktrace/java_deobfuscate.py b/third_party/libwebrtc/build/android/stacktrace/java_deobfuscate.py
new file mode 100755
index 0000000000..8c231ecfcc
--- /dev/null
+++ b/third_party/libwebrtc/build/android/stacktrace/java_deobfuscate.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+#
+# Copyright 2020 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.
+"""Wrapper script for java_deobfuscate.
+
+This is also a buildable target, but having it pre-built here simplifies usage.
+"""
+
+import os
+import sys
+
+DIR_SOURCE_ROOT = os.path.normpath(
+ os.path.join(os.path.dirname(__file__), '../../../'))
+
+
+def main():
+ classpath = [
+ os.path.join(DIR_SOURCE_ROOT, 'build', 'android', 'stacktrace',
+ 'java_deobfuscate.jar'),
+ os.path.join(DIR_SOURCE_ROOT, 'third_party', 'proguard', 'lib',
+ 'proguard603.jar'),
+ os.path.join(DIR_SOURCE_ROOT, 'third_party', 'proguard', 'lib',
+ 'retrace603.jar'),
+ ]
+ java_path = os.path.join(DIR_SOURCE_ROOT, 'third_party', 'jdk', 'current',
+ 'bin', 'java')
+
+ cmd = [
+ java_path, '-classpath', ':'.join(classpath),
+ 'org.chromium.build.FlushingReTrace'
+ ]
+ cmd.extend(sys.argv[1:])
+ os.execvp(cmd[0], cmd)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/third_party/libwebrtc/build/android/stacktrace/java_deobfuscate_test.py b/third_party/libwebrtc/build/android/stacktrace/java_deobfuscate_test.py
new file mode 100755
index 0000000000..d68323f129
--- /dev/null
+++ b/third_party/libwebrtc/build/android/stacktrace/java_deobfuscate_test.py
@@ -0,0 +1,172 @@
+#!/usr/bin/env vpython3
+#
+# Copyright 2017 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.
+"""Tests for java_deobfuscate."""
+
+import argparse
+import os
+import subprocess
+import sys
+import tempfile
+import unittest
+
+# Set by command-line argument.
+_JAVA_DEOBFUSCATE_PATH = None
+
+LINE_PREFIXES = [
+ '',
+ # logcat -v threadtime
+ '09-08 14:38:35.535 18029 18084 E qcom_sensors_hal: ',
+ # logcat
+ 'W/GCM (15158): ',
+ 'W/GCM ( 158): ',
+]
+
+TEST_MAP = """\
+this.was.Deobfuscated -> FOO:
+ int[] mFontFamily -> a
+ 1:3:void someMethod(int,android.os.Bundle):65:67 -> bar
+never.Deobfuscated -> NOTFOO:
+ int[] mFontFamily -> a
+ 1:3:void someMethod(int,android.os.Bundle):65:67 -> bar
+"""
+
+TEST_DATA = [
+ '',
+ 'FOO',
+ 'FOO.bar',
+ 'Here is a FOO',
+ 'Here is a class FOO',
+ 'Here is a class FOO baz',
+ 'Here is a "FOO" baz',
+ 'Here is a type "FOO" baz',
+ 'Here is a "FOO.bar" baz',
+ 'SomeError: SomeFrameworkClass in isTestClass for FOO',
+ 'Here is a FOO.bar',
+ 'Here is a FOO.bar baz',
+ 'END FOO#bar',
+ 'new-instance 3810 (LSome/Framework/Class;) in LFOO;',
+ 'FOO: Error message',
+ 'Caused by: FOO: Error message',
+ '\tat FOO.bar(PG:1)',
+ '\t at\t FOO.bar\t (\t PG:\t 1\t )',
+ ('Unable to start activity ComponentInfo{garbage.in/here.test}:'
+ ' java.lang.NullPointerException: Attempt to invoke interface method'
+ ' \'void FOO.bar(int,android.os.Bundle)\' on a null object reference'),
+ ('Caused by: java.lang.NullPointerException: Attempt to read from field'
+ ' \'int[] FOO.a\' on a null object reference'),
+ 'java.lang.VerifyError: FOO',
+ ('java.lang.NoSuchFieldError: No instance field a of type '
+ 'Ljava/lang/Class; in class LFOO;'),
+ 'NOTFOO: Object of type FOO was not destroyed...',
+]
+
+EXPECTED_OUTPUT = [
+ '',
+ 'this.was.Deobfuscated',
+ 'this.was.Deobfuscated.someMethod',
+ 'Here is a FOO',
+ 'Here is a class this.was.Deobfuscated',
+ 'Here is a class FOO baz',
+ 'Here is a "FOO" baz',
+ 'Here is a type "this.was.Deobfuscated" baz',
+ 'Here is a "this.was.Deobfuscated.someMethod" baz',
+ 'SomeError: SomeFrameworkClass in isTestClass for this.was.Deobfuscated',
+ 'Here is a this.was.Deobfuscated.someMethod',
+ 'Here is a FOO.bar baz',
+ 'END this.was.Deobfuscated#someMethod',
+ 'new-instance 3810 (LSome/Framework/Class;) in Lthis/was/Deobfuscated;',
+ 'this.was.Deobfuscated: Error message',
+ 'Caused by: this.was.Deobfuscated: Error message',
+ '\tat this.was.Deobfuscated.someMethod(Deobfuscated.java:65)',
+ ('\t at\t this.was.Deobfuscated.someMethod\t '
+ '(\t Deobfuscated.java:\t 65\t )'),
+ ('Unable to start activity ComponentInfo{garbage.in/here.test}:'
+ ' java.lang.NullPointerException: Attempt to invoke interface method'
+ ' \'void this.was.Deobfuscated.someMethod(int,android.os.Bundle)\' on a'
+ ' null object reference'),
+ ('Caused by: java.lang.NullPointerException: Attempt to read from field'
+ ' \'int[] this.was.Deobfuscated.mFontFamily\' on a null object reference'),
+ 'java.lang.VerifyError: this.was.Deobfuscated',
+ ('java.lang.NoSuchFieldError: No instance field mFontFamily of type '
+ 'Ljava/lang/Class; in class Lthis/was/Deobfuscated;'),
+ 'NOTFOO: Object of type this.was.Deobfuscated was not destroyed...',
+]
+TEST_DATA = [s + '\n' for s in TEST_DATA]
+EXPECTED_OUTPUT = [s + '\n' for s in EXPECTED_OUTPUT]
+
+
+class JavaDeobfuscateTest(unittest.TestCase):
+
+ def __init__(self, *args, **kwargs):
+ super(JavaDeobfuscateTest, self).__init__(*args, **kwargs)
+ self._map_file = None
+
+ def setUp(self):
+ self._map_file = tempfile.NamedTemporaryFile()
+ self._map_file.write(TEST_MAP)
+ self._map_file.flush()
+
+ def tearDown(self):
+ if self._map_file:
+ self._map_file.close()
+
+ def _testImpl(self, input_lines=None, expected_output_lines=None,
+ prefix=''):
+ self.assertTrue(bool(input_lines) == bool(expected_output_lines))
+
+ if not input_lines:
+ input_lines = [prefix + x for x in TEST_DATA]
+ if not expected_output_lines:
+ expected_output_lines = [prefix + x for x in EXPECTED_OUTPUT]
+
+ cmd = [_JAVA_DEOBFUSCATE_PATH, self._map_file.name]
+ proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+ proc_output, _ = proc.communicate(''.join(input_lines))
+ actual_output_lines = proc_output.splitlines(True)
+ for actual, expected in zip(actual_output_lines, expected_output_lines):
+ self.assertTrue(
+ actual == expected or actual.replace('bar', 'someMethod') == expected,
+ msg=''.join([
+ 'Deobfuscation failed.\n',
+ ' actual: %s' % actual,
+ ' expected: %s' % expected]))
+
+ def testNoPrefix(self):
+ self._testImpl(prefix='')
+
+ def testThreadtimePrefix(self):
+ self._testImpl(prefix='09-08 14:38:35.535 18029 18084 E qcom_sensors_hal: ')
+
+ def testStandardPrefix(self):
+ self._testImpl(prefix='W/GCM (15158): ')
+
+ def testStandardPrefixWithPadding(self):
+ self._testImpl(prefix='W/GCM ( 158): ')
+
+ @unittest.skip('causes java_deobfuscate to hang, see crbug.com/876539')
+ def testIndefiniteHang(self):
+ # Test for crbug.com/876539.
+ self._testImpl(
+ input_lines=[
+ 'VFY: unable to resolve virtual method 2: LFOO;'
+ + '.onDescendantInvalidated '
+ + '(Landroid/view/View;Landroid/view/View;)V',
+ ],
+ expected_output_lines=[
+ 'VFY: unable to resolve virtual method 2: Lthis.was.Deobfuscated;'
+ + '.onDescendantInvalidated '
+ + '(Landroid/view/View;Landroid/view/View;)V',
+ ])
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--java-deobfuscate-path', type=os.path.realpath,
+ required=True)
+ known_args, unittest_args = parser.parse_known_args()
+ _JAVA_DEOBFUSCATE_PATH = known_args.java_deobfuscate_path
+ unittest_args = [sys.argv[0]] + unittest_args
+ unittest.main(argv=unittest_args)
diff --git a/third_party/libwebrtc/build/android/stacktrace/stackwalker.py b/third_party/libwebrtc/build/android/stacktrace/stackwalker.py
new file mode 100755
index 0000000000..16b514901b
--- /dev/null
+++ b/third_party/libwebrtc/build/android/stacktrace/stackwalker.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env vpython3
+#
+# Copyright 2016 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.
+
+from __future__ import print_function
+
+import argparse
+import os
+import re
+import sys
+import tempfile
+
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
+from pylib.constants import host_paths
+
+if host_paths.DEVIL_PATH not in sys.path:
+ sys.path.append(host_paths.DEVIL_PATH)
+from devil.utils import cmd_helper
+
+
+_MICRODUMP_BEGIN = re.compile(
+ '.*google-breakpad: -----BEGIN BREAKPAD MICRODUMP-----')
+_MICRODUMP_END = re.compile(
+ '.*google-breakpad: -----END BREAKPAD MICRODUMP-----')
+
+""" Example Microdump
+<timestamp> 6270 6131 F google-breakpad: -----BEGIN BREAKPAD MICRODUMP-----
+<timestamp> 6270 6131 F google-breakpad: V Chrome_Android:54.0.2790.0
+...
+<timestamp> 6270 6131 F google-breakpad: -----END BREAKPAD MICRODUMP-----
+
+"""
+
+
+def GetMicroDumps(dump_path):
+ """Returns all microdumps found in given log file
+
+ Args:
+ dump_path: Path to the log file.
+
+ Returns:
+ List of all microdumps as lists of lines.
+ """
+ with open(dump_path, 'r') as d:
+ data = d.read()
+ all_dumps = []
+ current_dump = None
+ for line in data.splitlines():
+ if current_dump is not None:
+ if _MICRODUMP_END.match(line):
+ current_dump.append(line)
+ all_dumps.append(current_dump)
+ current_dump = None
+ else:
+ current_dump.append(line)
+ elif _MICRODUMP_BEGIN.match(line):
+ current_dump = []
+ current_dump.append(line)
+ return all_dumps
+
+
+def SymbolizeMicroDump(stackwalker_binary_path, dump, symbols_path):
+ """Runs stackwalker on microdump.
+
+ Runs the stackwalker binary at stackwalker_binary_path on a given microdump
+ using the symbols at symbols_path.
+
+ Args:
+ stackwalker_binary_path: Path to the stackwalker binary.
+ dump: The microdump to run the stackwalker on.
+ symbols_path: Path the the symbols file to use.
+
+ Returns:
+ Output from stackwalker tool.
+ """
+ with tempfile.NamedTemporaryFile() as tf:
+ for l in dump:
+ tf.write('%s\n' % l)
+ cmd = [stackwalker_binary_path, tf.name, symbols_path]
+ return cmd_helper.GetCmdOutput(cmd)
+
+
+def AddArguments(parser):
+ parser.add_argument('--stackwalker-binary-path', required=True,
+ help='Path to stackwalker binary.')
+ parser.add_argument('--stack-trace-path', required=True,
+ help='Path to stacktrace containing microdump.')
+ parser.add_argument('--symbols-path', required=True,
+ help='Path to symbols file.')
+ parser.add_argument('--output-file',
+ help='Path to dump stacktrace output to')
+
+
+def _PrintAndLog(line, fp):
+ if fp:
+ fp.write('%s\n' % line)
+ print(line)
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ AddArguments(parser)
+ args = parser.parse_args()
+
+ micro_dumps = GetMicroDumps(args.stack_trace_path)
+ if not micro_dumps:
+ print('No microdump found. Exiting.')
+ return 0
+
+ symbolized_dumps = []
+ for micro_dump in micro_dumps:
+ symbolized_dumps.append(SymbolizeMicroDump(
+ args.stackwalker_binary_path, micro_dump, args.symbols_path))
+
+ try:
+ fp = open(args.output_file, 'w') if args.output_file else None
+ _PrintAndLog('%d microdumps found.' % len(micro_dumps), fp)
+ _PrintAndLog('---------- Start output from stackwalker ----------', fp)
+ for index, symbolized_dump in list(enumerate(symbolized_dumps)):
+ _PrintAndLog(
+ '------------------ Start dump %d ------------------' % index, fp)
+ _PrintAndLog(symbolized_dump, fp)
+ _PrintAndLog(
+ '------------------- End dump %d -------------------' % index, fp)
+ _PrintAndLog('----------- End output from stackwalker -----------', fp)
+ except Exception:
+ if fp:
+ fp.close()
+ raise
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())