提交 c8eff5e4 编写于 作者: J James Robinson

Update to mojo d259eb58aa59e14a13d5e0dc3984b855b475ba09

This updates to mojo commit d259eb58aa59 and limits the roll script to
only pull in the parts of //mojo that are currently being used. More
stuff will be dropped in the future.
上级 fd3e989c
......@@ -67,6 +67,8 @@ Thumbs.db
/examples/**/pubspec.lock
/mojo/common/dart/packages
/mojo/dart/apptest/packages
/mojo/dart/http_load_test/bin/packages
/mojo/dart/http_load_test/packages/
/mojo/dart/mojo_services/packages
/mojo/dart/mojo_services/pubspec.lock
/mojo/dart/mojom/bin/packages
......
......@@ -19,7 +19,7 @@
vars = {
'chromium_git': 'https://chromium.googlesource.com',
'mojo_sdk_revision': '9a5f81a3d2a6d027677366edb5de3ae85f6dbf16',
'mojo_sdk_revision': '76ff57433b48527dc491dac4c52c9d71a7c3a0e3',
'skia_revision': '29ccdf86ab0a1649fd775c9431891bacb1391e99',
'dart_revision': '95c951ad190f156eb61b99203c2e4948211c44a7',
'dart_observatory_packages_revision': 'cdc4b3d4c15b9c0c8e7702dff127b440afbb7485',
......
......@@ -32,6 +32,17 @@ _DO_NOT_EDIT_WARNING = """// This file is auto-generated from
"""
_MOJO_EXPOSED_EXTENSIONS = ["CHROMIUM_bind_uniform_location",
"CHROMIUM_map_sub",
"CHROMIUM_miscellaneous",
"CHROMIUM_resize",
"CHROMIUM_sync_point",
"CHROMIUM_texture_mailbox",
"EXT_debug_marker",
"OES_vertex_array_object",
"occlusion_query_EXT"]
# This string is copied directly out of the gl2.h file from GLES2.0
#
# Edits:
......@@ -2803,14 +2814,14 @@ _FUNCTION_INFO = {
},
'MapBufferSubDataCHROMIUM': {
'gen_cmd': False,
'extension': True,
'extension': "CHROMIUM_map_sub",
'chromium': True,
'client_test': False,
'pepper_interface': 'ChromiumMapSub',
},
'MapTexSubImage2DCHROMIUM': {
'gen_cmd': False,
'extension': "CHROMIUM_sub_image",
'extension': "CHROMIUM_map_sub",
'chromium': True,
'client_test': False,
'pepper_interface': 'ChromiumMapSub',
......@@ -3205,7 +3216,7 @@ _FUNCTION_INFO = {
},
'UnmapBufferSubDataCHROMIUM': {
'gen_cmd': False,
'extension': True,
'extension': "CHROMIUM_map_sub",
'chromium': True,
'client_test': False,
'pepper_interface': 'ChromiumMapSub',
......@@ -3216,7 +3227,7 @@ _FUNCTION_INFO = {
},
'UnmapTexSubImage2DCHROMIUM': {
'gen_cmd': False,
'extension': "CHROMIUM_sub_image",
'extension': "CHROMIUM_map_sub",
'chromium': True,
'client_test': False,
'pepper_interface': 'ChromiumMapSub',
......@@ -3454,7 +3465,7 @@ _FUNCTION_INFO = {
},
'BindUniformLocationCHROMIUM': {
'type': 'GLchar',
'extension': True,
'extension': "CHROMIUM_bind_uniform_location",
'data_transfer_methods': ['bucket'],
'needs_size': True,
'gl_test_func': 'DoBindUniformLocationCHROMIUM',
......@@ -4350,25 +4361,15 @@ TEST_P(%(test_name)s, %(name)sInvalidArgs%(arg_index)d_%(value_index)d) {
file.Write("%s MojoGLES2Impl::%s(%s) {\n" %
(func.return_type, func.original_name,
func.MakeTypedOriginalArgString("")))
# TODO(alhaad): Add Mojo C thunk for each of the following methods and
# remove this.
func_list = ["GenQueriesEXT", "BeginQueryEXT", "MapTexSubImage2DCHROMIUM",
"UnmapTexSubImage2DCHROMIUM", "DeleteQueriesEXT",
"EndQueryEXT", "GetQueryObjectuivEXT", "ShallowFlushCHROMIUM"]
if func.original_name in func_list:
file.Write("return static_cast<gpu::gles2::GLES2Interface*>"
"(MojoGLES2GetGLES2Interface(context_))->" +
func.original_name + "(" + func.MakeOriginalArgString("") +
");")
file.Write("}")
return
extensions = ["CHROMIUM_sync_point", "CHROMIUM_texture_mailbox"]
if func.IsCoreGLFunction() or func.GetInfo("extension") in extensions:
is_mojo_extension = func.GetInfo("extension") in _MOJO_EXPOSED_EXTENSIONS
if func.IsCoreGLFunction() or is_mojo_extension:
file.Write("MojoGLES2MakeCurrent(context_);");
func_return = "gl" + func.original_name + "(" + \
func.MakeOriginalArgString("") + ");"
if func.return_type == "void":
if func.original_name == "ResizeCHROMIUM":
file.Write("MGLResizeSurface(width, height);");
elif func.return_type == "void":
file.Write(func_return);
else:
file.Write("return " + func_return);
......@@ -10186,9 +10187,16 @@ class MojoGLES2Impl : public gpu::gles2::GLES2Interface {
#include "mojo/gpu/mojo_gles2_impl_autogen.h"
#include "base/logging.h"
#include "mojo/public/c/gles2/chromium_bind_uniform_location.h"
#include "mojo/public/c/gles2/chromium_map_sub.h"
#include "mojo/public/c/gles2/chromium_miscellaneous.h"
#include "mojo/public/c/gles2/chromium_sync_point.h"
#include "mojo/public/c/gles2/chromium_texture_mailbox.h"
#include "mojo/public/c/gles2/ext_debug_marker.h"
#include "mojo/public/c/gles2/gles2.h"
#include "mojo/public/c/gles2/occlusion_query_ext.h"
#include "mojo/public/c/gles2/oes_vertex_array_object.h"
#include "mojo/public/c/gpu/MGL/mgl_onscreen.h"
namespace mojo {
......@@ -10585,10 +10593,6 @@ def main(argv):
"gpu/command_buffer/common/gles2_cmd_format_test_autogen.h")
gen.WriteGLES2InterfaceHeader(
"gpu/command_buffer/client/gles2_interface_autogen.h")
gen.WriteMojoGLES2ImplHeader(
"mojo/gpu/mojo_gles2_impl_autogen.h")
gen.WriteMojoGLES2Impl(
"mojo/gpu/mojo_gles2_impl_autogen.cc")
gen.WriteGLES2InterfaceStub(
"gpu/command_buffer/client/gles2_interface_stub_autogen.h")
gen.WriteGLES2InterfaceStubImpl(
......@@ -10632,16 +10636,18 @@ def main(argv):
gen.WriteCommonUtilsImpl(
"gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h")
gen.WriteGLES2Header("gpu/GLES2/gl2chromium_autogen.h")
mojo_gles2_prefix = ("mojo/public/c/gles2/gles2_call_visitor")
gen.WriteMojoGLCallVisitor(mojo_gles2_prefix + "_autogen.h")
mojo_extensions = ["CHROMIUM_texture_mailbox", "CHROMIUM_sync_point",
"CHROMIUM_sub_image", "CHROMIUM_miscellaneous",
"CHROMIUM_resize", "EXT_debug_marker",
"OES_vertex_array_object", "occlusion_query_EXT"]
for extension in mojo_extensions:
for extension in _MOJO_EXPOSED_EXTENSIONS:
gen.WriteMojoGLCallVisitorForExtension(
mojo_gles2_prefix + "_" + extension.lower() + "_autogen.h", extension)
gen.WriteMojoGLES2ImplHeader(
"mojo/gpu/mojo_gles2_impl_autogen.h")
gen.WriteMojoGLES2Impl(
"mojo/gpu/mojo_gles2_impl_autogen.cc")
Format(gen.generated_cpp_filenames)
if gen.errors > 0:
......
......@@ -5,8 +5,9 @@
#ifndef GPU_COMMAND_BUFFER_SERVICE_GL_SURFACE_MOCK_H_
#define GPU_COMMAND_BUFFER_SERVICE_GL_SURFACE_MOCK_H_
#include "ui/gl/gl_surface.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/gfx/swap_result.h"
#include "ui/gl/gl_surface.h"
namespace gpu {
......@@ -18,8 +19,8 @@ class GLSurfaceMock : public gfx::GLSurface {
MOCK_METHOD0(Destroy, void());
MOCK_METHOD1(Resize, bool(const gfx::Size& size));
MOCK_METHOD0(IsOffscreen, bool());
MOCK_METHOD0(SwapBuffers, bool());
MOCK_METHOD4(PostSubBuffer, bool(int x, int y, int width, int height));
MOCK_METHOD0(SwapBuffers, gfx::SwapResult());
MOCK_METHOD4(PostSubBuffer, gfx::SwapResult(int x, int y, int width, int height));
MOCK_METHOD0(SupportsPostSubBuffer, bool());
MOCK_METHOD0(GetSize, gfx::Size());
MOCK_METHOD0(GetHandle, void*());
......
......@@ -8222,7 +8222,8 @@ error::Error GLES2DecoderImpl::HandlePostSubBufferCHROMIUM(
gpu_state_tracer_->TakeSnapshotWithCurrentFramebuffer(
is_offscreen ? offscreen_size_ : surface_->GetSize());
}
if (surface_->PostSubBuffer(c.x, c.y, c.width, c.height)) {
if (surface_->PostSubBuffer(c.x, c.y, c.width, c.height) !=
gfx::SwapResult::SWAP_FAILED) {
return error::kNoError;
} else {
LOG(ERROR) << "Context lost because PostSubBuffer failed.";
......@@ -10347,7 +10348,7 @@ void GLES2DecoderImpl::DoSwapBuffers() {
glFlush();
}
} else {
if (!surface_->SwapBuffers()) {
if (surface_->SwapBuffers() == gfx::SwapResult::SWAP_FAILED) {
LOG(ERROR) << "Context lost because SwapBuffers failed.";
if (!CheckResetStatus()) {
MarkContextLost(error::kUnknown);
......
# Copyright 2014 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/ui.gni")
import("//build/module_args/mojo.gni")
import("//mojo/public/mojo.gni")
# TODO(beng): this meta target should probably move to the root dir's BUILD.gn.
group("mojo") {
# Meta-target, don't link into production code.
testonly = true
declare_args() {
mojo_use_go = false
# TODO(ncbray): support ASAN once NaCl's GN build is unforked.
mojo_use_nacl = is_linux && !is_asan
}
deps = [
":tests",
"//benchmarks",
"//examples",
"//mojo/common",
"//mojo/dart/apptest",
"//mojo/dart/apptest:apptest_pkg",
"//mojo/dart/mojom",
"//mojo/dart/mojo_services",
"//mojo/dart/observatory_test",
"//mojo/public",
"//mojo/services",
"//services",
]
if (is_android) {
deps += [
"//mojo/android",
"//mojo/java",
"//mojo/tools/android_shortcuts",
"//mojo/tools:remote_file_reader",
]
}
if (is_linux && mojo_use_go) {
deps += [ "//mojo/go" ]
}
if (is_linux) {
deps += [ "//mojo/python" ]
}
if (mojo_use_nacl) {
deps += [
"//mojo/nacl:mojo_nacl",
"//mojo/nacl:mojo_nacl_tests",
]
}
}
group("tests") {
testonly = true
deps = [
"//mojo/common:mojo_common_unittests",
"//mojo/converters/surfaces/tests:mojo_surfaces_lib_unittests",
"//mojo/edk/system:tests",
"//mojo/edk/test:public_tests",
"//mojo/dart/embedder/test:dart_unittests",
"//mojo/public/cpp/bindings/tests:versioning_apptests",
"//mojo/services/view_manager/public/cpp/tests:mojo_view_manager_lib_unittests",
"//mojo/tests:mojo_task_tracker_perftests",
"//mojo/tools:message_generator",
"//services/asset_bundle:apptests",
"//services/clipboard:apptests",
"//services/dart/dart_apptests",
"//services/files:apptests",
"//mojo/gpu:apptests",
"//mojo/services/files/public/c:apptests",
"//services/authenticating_url_loader_interceptor:apptests",
"//services/http_server:apptests",
"//services/prediction:apptests",
"//services/reaper:tests",
"//services/url_response_disk_cache:tests",
"//services/view_manager:mojo_view_manager_client_apptests",
"//services/view_manager:view_manager_service_apptests",
"//services/view_manager:view_manager_service_unittests",
"//services/window_manager:window_manager_apptests",
"//services/window_manager:window_manager_unittests",
"//shell:apptests",
]
if (is_linux) {
deps += [ "//services/python:python_apptests" ]
}
if (is_android) {
deps += [ "//services/notifications:apptests" ]
}
# TODO(jamesr): We only support building V8 snapshot data on a linux host since it
# needs a 32 bit toolchain and we don't have one configured for mac hosts.
if (host_os == "linux") {
deps += [
"//mojo/edk/js:tests",
"//services/js:js_apptests",
"//services/js:js_services_unittests",
]
}
if (mojo_use_prebuilt_network_service) {
deps += [ "//mojo/public/tools:copy_network_service_apptests" ]
}
}
aa@chromium.org
abarth@chromium.org
ben@chromium.org
darin@chromium.org
davemoore@chromium.org
jamesr@chromium.org
mpcomplete@chromium.org
qsr@chromium.org
sky@chromium.org
viettrungluu@chromium.org
yzshen@chromium.org
# Copyright 2014 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.
"""Presubmit script for mojo
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into depot_tools.
"""
import os.path
import re
# NOTE: The EDK allows all external paths, so doesn't need a whitelist.
_PACKAGE_WHITELISTED_EXTERNAL_PATHS = {
"SDK": ["//build/module_args/mojo.gni",
"//build/module_args/dart.gni",
"//testing/gtest",
"//third_party/cython",
"//third_party/khronos"],
"services": ["//build/module_args/mojo.gni",
"//testing/gtest"],
}
# These files are not part of the exported package.
_PACKAGE_IGNORED_BUILD_FILES = {
"SDK": {},
"EDK": {},
"services": {"mojo/services/BUILD.gn"},
}
_PACKAGE_PATH_PREFIXES = {"SDK": "mojo/public/",
"EDK": "mojo/edk/",
"services": "mojo/services"}
# TODO(etiennej): python_binary_source_set added due to crbug.com/443147
_PACKAGE_SOURCE_SET_TYPES = {"SDK": ["mojo_sdk_source_set",
"python_binary_source_set"],
"EDK": ["mojo_edk_source_set"],
"services": ["mojo_sdk_source_set"]}
_ILLEGAL_EXTERNAL_PATH_WARNING_MESSAGE = \
"Found disallowed external paths within SDK buildfiles."
_ILLEGAL_SERVICES_ABSOLUTE_PATH_WARNING_MESSAGE = \
"Found references to services' public buildfiles via absolute paths " \
"within services' public buildfiles."
_ILLEGAL_EDK_ABSOLUTE_PATH_WARNING_MESSAGE = \
"Found references to the EDK via absolute paths within EDK buildfiles."
_ILLEGAL_SDK_ABSOLUTE_PATH_WARNING_MESSAGE_TEMPLATE = \
"Found references to the SDK via absolute paths within %s buildfiles."
_ILLEGAL_SDK_ABSOLUTE_PATH_WARNING_MESSAGES = {
"SDK": _ILLEGAL_SDK_ABSOLUTE_PATH_WARNING_MESSAGE_TEMPLATE % "SDK",
"EDK": _ILLEGAL_SDK_ABSOLUTE_PATH_WARNING_MESSAGE_TEMPLATE % "EDK",
"services": _ILLEGAL_SDK_ABSOLUTE_PATH_WARNING_MESSAGE_TEMPLATE
% "services' public",
}
_INCORRECT_SOURCE_SET_TYPE_WARNING_MESSAGE_TEMPLATE = \
"All source sets in %s must be constructed via %s."
_INCORRECT_SOURCE_SET_TYPE_WARNING_MESSAGES = {
"SDK": _INCORRECT_SOURCE_SET_TYPE_WARNING_MESSAGE_TEMPLATE
% ("the SDK", _PACKAGE_SOURCE_SET_TYPES["SDK"]),
"EDK": _INCORRECT_SOURCE_SET_TYPE_WARNING_MESSAGE_TEMPLATE
% ("the EDK", _PACKAGE_SOURCE_SET_TYPES["EDK"]),
"services": _INCORRECT_SOURCE_SET_TYPE_WARNING_MESSAGE_TEMPLATE
% ("services' client libs", _PACKAGE_SOURCE_SET_TYPES["services"]),
}
def _IsBuildFileWithinPackage(f, package):
"""Returns whether |f| specifies a GN build file within |package|."""
assert package in _PACKAGE_PATH_PREFIXES
package_path_prefix = _PACKAGE_PATH_PREFIXES[package]
if not f.LocalPath().startswith(package_path_prefix):
return False
if (not f.LocalPath().endswith("/BUILD.gn") and
not f.LocalPath().endswith(".gni")):
return False
if f.LocalPath() in _PACKAGE_IGNORED_BUILD_FILES[package]:
return False
return True
def _AffectedBuildFilesWithinPackage(input_api, package):
"""Returns all the affected build files within |package|."""
return [f for f in input_api.AffectedFiles()
if _IsBuildFileWithinPackage(f, package)]
def _FindIllegalAbsolutePathsInBuildFiles(input_api, package):
"""Finds illegal absolute paths within the build files in
|input_api.AffectedFiles()| that are within |package|.
An illegal absolute path within the SDK or a service's SDK is one that is to
the SDK itself or a non-whitelisted external path. An illegal absolute path
within the EDK is one that is to the SDK or the EDK.
Returns any such references in a list of (file_path, line_number,
referenced_path) tuples."""
illegal_references = []
for f in _AffectedBuildFilesWithinPackage(input_api, package):
for line_num, line in f.ChangedContents():
# Determine if this is a reference to an absolute path.
m = re.search(r'"(//[^"]*)"', line)
if not m:
continue
referenced_path = m.group(1)
if not referenced_path.startswith("//mojo"):
# In the EDK, all external absolute paths are allowed.
if package == "EDK":
continue
# Determine if this is a whitelisted external path.
if referenced_path in _PACKAGE_WHITELISTED_EXTERNAL_PATHS[package]:
continue
illegal_references.append((f.LocalPath(), line_num, referenced_path))
return illegal_references
def _PathReferenceInBuildFileWarningItem(build_file, line_num, referenced_path):
"""Returns a string expressing a warning item that |referenced_path| is
referenced at |line_num| in |build_file|."""
return "%s, line %d (%s)" % (build_file, line_num, referenced_path)
def _IncorrectSourceSetTypeWarningItem(build_file, line_num):
"""Returns a string expressing that the error occurs at |line_num| in
|build_file|."""
return "%s, line %d" % (build_file, line_num)
def _CheckNoIllegalAbsolutePathsInBuildFiles(input_api, output_api, package):
"""Makes sure that the BUILD.gn files within |package| do not reference the
SDK/EDK via absolute paths, and do not reference disallowed external
dependencies."""
sdk_references = []
edk_references = []
external_deps_references = []
services_references = []
# Categorize any illegal references.
illegal_references = _FindIllegalAbsolutePathsInBuildFiles(input_api, package)
for build_file, line_num, referenced_path in illegal_references:
reference_string = _PathReferenceInBuildFileWarningItem(build_file,
line_num,
referenced_path)
if referenced_path.startswith("//mojo/public"):
sdk_references.append(reference_string)
elif package == "SDK":
external_deps_references.append(reference_string)
elif package == "services":
if referenced_path.startswith("//mojo/services"):
services_references.append(reference_string)
else:
external_deps_references.append(reference_string)
elif referenced_path.startswith("//mojo/edk"):
edk_references.append(reference_string)
# Package up categorized illegal references into results.
results = []
if sdk_references:
results.extend([output_api.PresubmitError(
_ILLEGAL_SDK_ABSOLUTE_PATH_WARNING_MESSAGES[package],
items=sdk_references)])
if external_deps_references:
assert package == "SDK" or package == "services"
results.extend([output_api.PresubmitError(
_ILLEGAL_EXTERNAL_PATH_WARNING_MESSAGE,
items=external_deps_references)])
if services_references:
assert package == "services"
results.extend([output_api.PresubmitError(
_ILLEGAL_SERVICES_ABSOLUTE_PATH_WARNING_MESSAGE,
items=services_references)])
if edk_references:
assert package == "EDK"
results.extend([output_api.PresubmitError(
_ILLEGAL_EDK_ABSOLUTE_PATH_WARNING_MESSAGE,
items=edk_references)])
return results
def _CheckSourceSetsAreOfCorrectType(input_api, output_api, package):
"""Makes sure that the BUILD.gn files always use the correct wrapper type for
|package|, which can be one of ["SDK", "EDK"], to construct source_set
targets."""
assert package in _PACKAGE_SOURCE_SET_TYPES
required_source_set_type = _PACKAGE_SOURCE_SET_TYPES[package]
problems = []
for f in _AffectedBuildFilesWithinPackage(input_api, package):
for line_num, line in f.ChangedContents():
m = re.search(r"[a-z_]*source_set\(", line)
if not m:
continue
source_set_type = m.group(0)[:-1]
if source_set_type in required_source_set_type:
continue
problems.append(_IncorrectSourceSetTypeWarningItem(f.LocalPath(),
line_num))
if not problems:
return []
return [output_api.PresubmitError(
_INCORRECT_SOURCE_SET_TYPE_WARNING_MESSAGES[package],
items=problems)]
def _CheckChangePylintsClean(input_api, output_api):
# Additional python module paths (we're in src/mojo/); not everyone needs
# them, but it's easiest to add them to everyone's path.
# For ply and jinja2:
third_party_path = os.path.join(
input_api.PresubmitLocalPath(), "..", "third_party")
# For the bindings generator:
mojo_public_bindings_pylib_path = os.path.join(
input_api.PresubmitLocalPath(), "public", "tools", "bindings", "pylib")
# For the python bindings:
mojo_python_bindings_path = os.path.join(
input_api.PresubmitLocalPath(), "public", "python")
# For the python bindings tests:
mojo_python_bindings_tests_path = os.path.join(
input_api.PresubmitLocalPath(), "python", "tests")
# For the roll tools scripts:
mojo_roll_tools_path = os.path.join(
input_api.PresubmitLocalPath(), "tools", "roll")
# For all mojo/tools scripts:
mopy_path = os.path.join(input_api.PresubmitLocalPath(), "tools")
# For all mojo/devtools scripts:
devtools_path = os.path.join(input_api.PresubmitLocalPath(), "devtools")
# TODO(vtl): Don't lint these files until the (many) problems are fixed
# (possibly by deleting/rewriting some files).
temporary_black_list = (
r".*\bpublic[\\\/]tools[\\\/]bindings[\\\/]pylib[\\\/]mojom[\\\/]"
r"generate[\\\/].+\.py$",
r".*\bpublic[\\\/]tools[\\\/]bindings[\\\/]generators[\\\/].+\.py$")
black_list = input_api.DEFAULT_BLACK_LIST + temporary_black_list + (
# Imported from Android tools, we might want not to fix the warnings
# raised for it to make it easier to compare the code with the original.
r".*\bdevtools[\\\/]common[\\\/]android_stack_parser[\\\/].+\.py$",)
results = []
pylint_extra_paths = [
third_party_path,
mojo_public_bindings_pylib_path,
mojo_python_bindings_path,
mojo_python_bindings_tests_path,
mojo_roll_tools_path,
mopy_path,
devtools_path
]
results.extend(input_api.canned_checks.RunPylint(
input_api, output_api, extra_paths_list=pylint_extra_paths,
black_list=black_list))
return results
def _BuildFileChecks(input_api, output_api):
"""Performs checks on SDK, EDK, and services' public buildfiles."""
results = []
for package in ["SDK", "EDK", "services"]:
results.extend(_CheckNoIllegalAbsolutePathsInBuildFiles(input_api,
output_api,
package))
results.extend(_CheckSourceSetsAreOfCorrectType(input_api,
output_api,
package))
return results
def _CommonChecks(input_api, output_api):
"""Checks common to both upload and commit."""
results = []
results.extend(_BuildFileChecks(input_api, output_api))
return results
def CheckChangeOnUpload(input_api, output_api):
results = []
results.extend(_CommonChecks(input_api, output_api))
results.extend(_CheckChangePylintsClean(input_api, output_api))
return results
def CheckChangeOnCommit(input_api, output_api):
results = []
results.extend(_CommonChecks(input_api, output_api))
return results
#!/usr/bin/env python
# Copyright 2014 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 os
import sys
import unittest
import PRESUBMIT
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from PRESUBMIT_test_mocks import MockFile
from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi
_SDK_BUILD_FILE = 'mojo/public/some/path/BUILD.gn'
_EDK_BUILD_FILE = 'mojo/edk/some/path/BUILD.gn'
_SERVICE_BUILD_FILE = 'mojo/services/some_service/public/BUILD.gn'
_IRRELEVANT_BUILD_FILE = 'mojo/foo/some/path/BUILD.gn'
_PACKAGE_BUILDFILES = {
'SDK' : _SDK_BUILD_FILE,
'EDK' : _EDK_BUILD_FILE,
'services' : _SERVICE_BUILD_FILE
}
class AbsoluteReferencesInBuildFilesTest(unittest.TestCase):
"""Tests the checking for illegal absolute paths within SDK/EDK buildfiles.
"""
def setUp(self):
self.sdk_absolute_path = '//mojo/public/some/absolute/path'
self.sdk_relative_path = 'mojo/public/some/relative/path'
self.edk_absolute_path = '//mojo/edk/some/absolute/path'
self.edk_relative_path = 'mojo/edk/some/relative/path'
self.service_absolute_path = '//mojo/services/some/service'
self.service_relative_path = '../../../some/service'
self.whitelisted_external_path = '//testing/gtest'
self.non_whitelisted_external_path = '//base'
def inputApiContainingFileWithPaths(self, filename, paths):
"""Returns a MockInputApi object with a single file having |filename| as
its name and |paths| as its contents, with each path being wrapped in a
pair of double-quotes to match the syntax for strings within BUILD.gn
files."""
contents = [ '"%s"' % path for path in paths ]
mock_file = MockFile(filename, contents)
mock_input_api = MockInputApi()
mock_input_api.files.append(mock_file)
return mock_input_api
def checkWarningWithSingleItem(self,
warning,
expected_message,
build_file,
line_num,
referenced_path):
"""Checks that |warning| has a message of |expected_message| and a single
item whose contents are the absolute path warning item for
(build_file, line_num, referenced_path)."""
self.assertEqual(expected_message, warning.message)
self.assertEqual(1, len(warning.items))
expected_item = PRESUBMIT._PathReferenceInBuildFileWarningItem(
build_file, line_num, referenced_path)
self.assertEqual(expected_item, warning.items[0])
def checkSDKAbsolutePathWarningWithSingleItem(self,
warning,
package,
build_file,
line_num,
referenced_path):
"""Checks that |warning| has the message for an absolute SDK path within
|package| and a single item whose contents are the absolute path warning
item for (build_file, line_num, referenced_path)."""
expected_message = \
PRESUBMIT._ILLEGAL_SDK_ABSOLUTE_PATH_WARNING_MESSAGES[package]
self.checkWarningWithSingleItem(warning,
expected_message,
build_file,
line_num,
referenced_path)
def _testAbsoluteSDKReferenceInPackage(self, package):
"""Tests that an absolute SDK path within |package| is flagged."""
buildfile = _PACKAGE_BUILDFILES[package]
mock_input_api = self.inputApiContainingFileWithPaths(
buildfile,
[ self.sdk_relative_path, self.sdk_absolute_path ])
warnings = PRESUBMIT._BuildFileChecks(mock_input_api, MockOutputApi())
self.assertEqual(1, len(warnings))
self.checkSDKAbsolutePathWarningWithSingleItem(warnings[0],
package,
buildfile,
2,
self.sdk_absolute_path)
def _testExternalReferenceInPackage(self, package):
"""Tests that an illegal external path within |package| is flagged."""
buildfile = _PACKAGE_BUILDFILES[package]
mock_input_api = self.inputApiContainingFileWithPaths(
buildfile,
[ self.non_whitelisted_external_path, self.whitelisted_external_path ])
warnings = PRESUBMIT._BuildFileChecks(mock_input_api, MockOutputApi())
if package == 'EDK':
# All external paths are allowed in the EDK.
self.assertEqual(0, len(warnings))
return
self.assertEqual(1, len(warnings))
expected_message = PRESUBMIT._ILLEGAL_EXTERNAL_PATH_WARNING_MESSAGE
self.checkWarningWithSingleItem(warnings[0],
expected_message,
buildfile,
1,
self.non_whitelisted_external_path)
def testAbsoluteSDKReferenceInSDKBuildFile(self):
"""Tests that an absolute SDK path within an SDK buildfile is flagged."""
self._testAbsoluteSDKReferenceInPackage('SDK')
def testExternalReferenceInSDKBuildFile(self):
"""Tests that an illegal external path in an SDK buildfile is flagged."""
self._testExternalReferenceInPackage('SDK')
def testAbsoluteEDKReferenceInSDKBuildFile(self):
"""Tests that an absolute EDK path in an SDK buildfile is flagged."""
mock_input_api = self.inputApiContainingFileWithPaths(
_SDK_BUILD_FILE,
[ self.edk_absolute_path ])
warnings = PRESUBMIT._BuildFileChecks(mock_input_api, MockOutputApi())
self.assertEqual(1, len(warnings))
expected_message = PRESUBMIT._ILLEGAL_EXTERNAL_PATH_WARNING_MESSAGE
self.checkWarningWithSingleItem(warnings[0],
expected_message,
_SDK_BUILD_FILE,
1,
self.edk_absolute_path)
def testAbsoluteSDKReferenceInEDKBuildFile(self):
"""Tests that an absolute SDK path within an EDK buildfile is flagged."""
self._testAbsoluteSDKReferenceInPackage('EDK')
def testAbsoluteEDKReferenceInEDKBuildFile(self):
"""Tests that an absolute EDK path in an EDK buildfile is flagged."""
mock_input_api = self.inputApiContainingFileWithPaths(
_EDK_BUILD_FILE,
[ self.edk_absolute_path, self.edk_relative_path ])
warnings = PRESUBMIT._BuildFileChecks(mock_input_api, MockOutputApi())
self.assertEqual(1, len(warnings))
expected_message = PRESUBMIT._ILLEGAL_EDK_ABSOLUTE_PATH_WARNING_MESSAGE
self.checkWarningWithSingleItem(warnings[0],
expected_message,
_EDK_BUILD_FILE,
1,
self.edk_absolute_path)
def testExternalReferenceInEDKBuildFile(self):
"""Tests that an external path in an EDK buildfile is not flagged."""
self._testExternalReferenceInPackage('EDK')
def testAbsoluteSDKReferenceInServiceBuildFile(self):
"""Tests that an absolute SDK path within a service's public buildfile is
flagged."""
self._testAbsoluteSDKReferenceInPackage("services")
def testAbsoluteServiceReferenceInServiceBuildFile(self):
"""Tests that an absolute path to a service within a service's public
buildfile is flagged."""
mock_input_api = self.inputApiContainingFileWithPaths(
_SERVICE_BUILD_FILE,
[ self.service_relative_path, self.service_absolute_path ])
warnings = PRESUBMIT._BuildFileChecks(mock_input_api, MockOutputApi())
self.assertEqual(1, len(warnings))
expected_message = \
PRESUBMIT._ILLEGAL_SERVICES_ABSOLUTE_PATH_WARNING_MESSAGE
self.checkWarningWithSingleItem(warnings[0],
expected_message,
_SERVICE_BUILD_FILE,
2,
self.service_absolute_path)
def testExternalReferenceInServiceBuildFile(self):
"""Tests that an illegal external path in a service's buildfile is flagged
."""
self._testExternalReferenceInPackage("services")
def testIrrelevantBuildFile(self):
"""Tests that nothing is flagged in a non SDK/EDK buildfile."""
mock_input_api = self.inputApiContainingFileWithPaths(
_IRRELEVANT_BUILD_FILE,
[ self.sdk_absolute_path,
self.sdk_relative_path,
self.edk_absolute_path,
self.edk_relative_path,
self.non_whitelisted_external_path,
self.whitelisted_external_path ])
warnings = PRESUBMIT._BuildFileChecks(mock_input_api, MockOutputApi())
self.assertEqual(0, len(warnings))
class SourceSetTypesInBuildFilesTest(unittest.TestCase):
"""Tests checking of correct source set types within SDK/EDK buildfiles."""
def inputApiContainingFileWithSourceSets(self, filename, source_sets):
"""Returns a MockInputApi object containing a single file having |filename|
as its name and |source_sets| as its contents."""
mock_file = MockFile(filename, source_sets)
mock_input_api = MockInputApi()
mock_input_api.files.append(mock_file)
return mock_input_api
def checkWarningWithSingleItem(self,
warning,
package,
build_file,
line_num):
"""Checks that warning has the expected incorrect source set type message
for |package| and a single item whose contents are the incorrect source
set type item for (build_file, line_num)."""
expected_message = \
PRESUBMIT._INCORRECT_SOURCE_SET_TYPE_WARNING_MESSAGES[package]
self.assertEqual(expected_message, warning.message)
self.assertEqual(1, len(warning.items))
expected_item = PRESUBMIT._IncorrectSourceSetTypeWarningItem(
build_file, line_num)
self.assertEqual(expected_item, warning.items[0])
def _testNakedSourceSetInPackage(self, package):
"""Tests that a source_set within |package| is flagged."""
buildfile = _PACKAGE_BUILDFILES[package]
naked_source_set = 'source_set('
correct_source_set = 'mojo_sdk_source_set('
if package == 'EDK':
correct_source_set = 'mojo_edk_source_set('
mock_input_api = self.inputApiContainingFileWithSourceSets(
buildfile,
[ correct_source_set, naked_source_set ])
warnings = PRESUBMIT._BuildFileChecks(mock_input_api, MockOutputApi())
self.assertEqual(1, len(warnings))
self.checkWarningWithSingleItem(warnings[0], package, buildfile, 2)
def _testWrongTypeOfWrapperSourceSetInPackage(self, package):
"""Tests that the wrong type of wrapper source_set in |package| is flagged
(e.g., mojo_edk_source_set within the SDK)."""
buildfile = _PACKAGE_BUILDFILES[package]
mock_input_api = self.inputApiContainingFileWithSourceSets(
buildfile,
[ 'mojo_sdk_source_set(', 'mojo_edk_source_set(' ])
warnings = PRESUBMIT._BuildFileChecks(mock_input_api, MockOutputApi())
self.assertEqual(1, len(warnings))
warning_line = 2
if package == 'EDK':
warning_line = 1
self.checkWarningWithSingleItem(warnings[0],
package,
buildfile,
warning_line)
def testNakedSourceSetInSDKBuildFile(self):
"""Tests that a source_set within the SDK is flagged."""
self._testNakedSourceSetInPackage('SDK')
def testPythonSourceSetInSDKBuildFile(self):
"""Tests that a python_binary_source_set within an SDK buildfile is
accepted."""
mock_input_api = self.inputApiContainingFileWithSourceSets(
_SDK_BUILD_FILE,
[ 'mojo_sdk_source_set(', 'python_binary_source_set(' ])
warnings = PRESUBMIT._BuildFileChecks(mock_input_api, MockOutputApi())
self.assertEqual(0, len(warnings))
def testEDKSourceSetInSDKBuildFile(self):
"""Tests that a mojo_edk_source_set within an SDK buildfile is flagged."""
self._testWrongTypeOfWrapperSourceSetInPackage('SDK')
def testNakedSourceSetInEDKBuildFile(self):
"""Tests that a source_set within the EDK is flagged."""
self._testNakedSourceSetInPackage('EDK')
def testSDKSourceSetInEDKBuildFile(self):
"""Tests that a mojo_sdk_source_set within an EDK buildfile is flagged."""
self._testWrongTypeOfWrapperSourceSetInPackage('EDK')
def testNakedSourceSetInServiceBuildFile(self):
"""Tests that a source_set within a service's public buildfile is flagged.
"""
self._testNakedSourceSetInPackage("services")
def testEDKSourceSetInServiceBuildFile(self):
"""Tests that a mojo_edk_source_set within a service's public buildfile is
flagged."""
self._testWrongTypeOfWrapperSourceSetInPackage("services")
def testIrrelevantBuildFile(self):
"""Tests that a source_set in a non-SDK/EDK buildfile isn't flagged."""
mock_input_api = self.inputApiContainingFileWithSourceSets(
_IRRELEVANT_BUILD_FILE,
[ 'source_set(' ])
warnings = PRESUBMIT._BuildFileChecks(mock_input_api, MockOutputApi())
self.assertEqual(0, len(warnings))
if __name__ == '__main__':
unittest.main()
Mojo
====
Mojo is an effort to extract a common platform out of Chrome's renderer and
plugin processes that can support multiple types of sandboxed content, such as
HTML, Pepper, or NaCl.
......@@ -45,10 +45,10 @@ source_set("libsystem_java") {
deps = [
":system_java_jni_headers",
"//base",
"//mojo/public/cpp/environment",
"//mojo/common:common",
"//mojo/message_pump",
"//mojo/public/c/system:system",
"//mojo/public/cpp/bindings:callback",
"//mojo/public/cpp/environment",
]
}
......@@ -125,13 +125,12 @@ shared_library("mojo_java_unittests") {
":system_java_jni_headers",
"//base",
"//base/test/:test_support",
"//mojo/common",
"//mojo/edk/system",
"//mojo/environment:chromium",
"//mojo/message_pump",
"//mojo/public/cpp/bindings/tests:mojo_public_bindings_test_utils",
"//mojo/public/cpp/test_support:test_utils",
"//mojo/public/cpp/environment",
"//mojo/edk/system",
"//mojo/environment:chromium",
"//mojo/public/cpp/test_support:test_utils",
]
defines = [ "UNIT_TEST" ]
}
......
......@@ -13,7 +13,7 @@
#include "base/run_loop.h"
#include "base/test/test_support_android.h"
#include "jni/MojoTestCase_jni.h"
#include "mojo/common/message_pump_mojo.h"
#include "mojo/message_pump/message_pump_mojo.h"
#include "mojo/public/cpp/environment/environment.h"
......
......@@ -12,7 +12,7 @@
#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "jni/BaseRunLoop_jni.h"
#include "mojo/common/message_pump_mojo.h"
#include "mojo/message_pump/message_pump_mojo.h"
namespace mojo {
namespace android {
......
......@@ -13,7 +13,7 @@ source_set("application") {
]
deps = [
"//base",
"//mojo/common",
"//mojo/message_pump",
"//mojo/public/cpp/system",
"//mojo/environment:chromium",
]
......@@ -27,8 +27,8 @@ source_set("content_handler") {
deps = [
":application",
"//base",
"//mojo/common",
"//mojo/environment:chromium",
"//mojo/message_pump",
"//mojo/public/interfaces/application",
"//mojo/services/content_handler/public/interfaces",
"//mojo/services/network/public/interfaces",
......
......@@ -9,7 +9,7 @@
#include "base/debug/stack_trace.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "mojo/common/message_pump_mojo.h"
#include "mojo/message_pump/message_pump_mojo.h"
#include "mojo/public/cpp/application/application_delegate.h"
#include "mojo/public/cpp/application/application_impl.h"
......
......@@ -13,7 +13,7 @@
#include "base/threading/platform_thread.h"
#include "base/trace_event/trace_event.h"
#include "mojo/application/application_runner_chromium.h"
#include "mojo/common/message_pump_mojo.h"
#include "mojo/message_pump/message_pump_mojo.h"
#include "mojo/public/cpp/application/application_connection.h"
#include "mojo/public/cpp/application/application_delegate.h"
#include "mojo/public/cpp/application/application_impl.h"
......
......@@ -5,54 +5,25 @@
import("//mojo/public/tools/bindings/mojom.gni")
import("//testing/test.gni")
component("common") {
output_name = "mojo_common_lib"
source_set("common") {
sources = [
"binding_set.h",
"common_type_converters.cc",
"common_type_converters.h",
"data_pipe_drainer.cc",
"data_pipe_drainer.h",
"data_pipe_file_utils.cc",
"data_pipe_utils.cc",
"data_pipe_utils.h",
"data_pipe_utils_internal.h",
"handle_watcher.cc",
"handle_watcher.h",
"interface_ptr_set.h",
"message_pump_mojo.cc",
"message_pump_mojo.h",
"message_pump_mojo_handler.h",
"task_tracker.cc",
"task_tracker.h",
"time_helper.cc",
"time_helper.h",
]
if (is_nacl) {
sources -= [ "data_pipe_file_utils.cc" ]
}
deps = [
"//base",
"//base/third_party/dynamic_annotations",
"//mojo/public/c/system",
"//mojo/public/cpp/bindings",
"//mojo/public/cpp/environment:environment",
"//mojo/public/cpp/system",
"//url",
]
}
test("mojo_common_unittests") {
sources = [
"binding_set_unittest.cc",
"common_type_converters_unittest.cc",
"data_pipe_utils_unittest.cc",
"handle_watcher_unittest.cc",
"callback_binding_unittest.cc",
"interface_ptr_set_unittest.cc",
"message_pump_mojo_unittest.cc",
"task_tracker_unittest.cc",
]
......@@ -61,10 +32,15 @@ test("mojo_common_unittests") {
":test_interfaces",
"//base",
"//base/test:test_support",
"//base:message_loop_tests",
"//mojo/converters/array_string:tests",
"//mojo/converters/base:tests",
"//mojo/converters/url:tests",
"//mojo/data_pipe_utils:tests",
"//mojo/edk/test:run_all_unittests",
"//mojo/edk/test:test_support",
"//mojo/environment:chromium",
"//mojo/message_pump",
"//mojo/message_pump:tests",
"//mojo/public/cpp/bindings",
"//mojo/public/cpp/bindings:callback",
"//mojo/public/cpp/system",
......@@ -83,8 +59,8 @@ mojom("test_interfaces") {
source_set("tracing_impl") {
sources = [
"trace_controller_impl.cc",
"trace_controller_impl.h",
"trace_provider_impl.cc",
"trace_provider_impl.h",
"tracing_impl.cc",
"tracing_impl.h",
]
......
......@@ -5,8 +5,8 @@
#include "mojo/common/binding_set.h"
#include "base/message_loop/message_loop.h"
#include "mojo/common/message_pump_mojo.h"
#include "mojo/common/test_interfaces.mojom.h"
#include "mojo/message_pump/message_pump_mojo.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "testing/gtest/include/gtest/gtest.h"
......
......@@ -2,110 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/common/common_type_converters.h"
#include "base/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "mojo/public/cpp/bindings/callback.h"
#include "mojo/public/cpp/bindings/map.h"
#include "mojo/public/cpp/bindings/string.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace mojo {
namespace common {
namespace test {
namespace {
void ExpectEqualsStringPiece(const std::string& expected,
const base::StringPiece& str) {
EXPECT_EQ(expected, str.as_string());
}
void ExpectEqualsMojoString(const std::string& expected,
const String& str) {
EXPECT_EQ(expected, str.get());
}
void ExpectEqualsString16(const base::string16& expected,
const base::string16& actual) {
EXPECT_EQ(expected, actual);
}
void ExpectEqualsMojoString(const base::string16& expected,
const String& str) {
EXPECT_EQ(expected, str.To<base::string16>());
}
TEST(CommonTypeConvertersTest, StringPiece) {
std::string kText("hello world");
base::StringPiece string_piece(kText);
String mojo_string(String::From(string_piece));
ExpectEqualsMojoString(kText, mojo_string);
ExpectEqualsStringPiece(kText, mojo_string.To<base::StringPiece>());
// Test implicit construction and conversion:
ExpectEqualsMojoString(kText, String::From(string_piece));
ExpectEqualsStringPiece(kText, mojo_string.To<base::StringPiece>());
// Test null String:
base::StringPiece empty_string_piece = String().To<base::StringPiece>();
EXPECT_TRUE(empty_string_piece.empty());
}
TEST(CommonTypeConvertersTest, String16) {
const base::string16 string16(base::ASCIIToUTF16("hello world"));
const String mojo_string(String::From(string16));
ExpectEqualsMojoString(string16, mojo_string);
EXPECT_EQ(string16, mojo_string.To<base::string16>());
// Test implicit construction and conversion:
ExpectEqualsMojoString(string16, String::From(string16));
ExpectEqualsString16(string16, mojo_string.To<base::string16>());
// Test empty string conversion.
ExpectEqualsMojoString(base::string16(), String::From(base::string16()));
}
TEST(CommonTypeConvertersTest, URL) {
GURL url("mojo:foo");
String mojo_string(String::From(url));
ASSERT_EQ(url.spec(), mojo_string);
EXPECT_EQ(url.spec(), mojo_string.To<GURL>().spec());
EXPECT_EQ(url.spec(), String::From(url));
GURL invalid = String().To<GURL>();
ASSERT_TRUE(invalid.spec().empty());
String string_from_invalid = String::From(invalid);
EXPECT_FALSE(string_from_invalid.is_null());
ASSERT_EQ(0U, string_from_invalid.size());
}
TEST(CommonTypeConvertersTest, ArrayUint8ToStdString) {
Array<uint8_t> data(4);
data[0] = 'd';
data[1] = 'a';
data[2] = 't';
data[3] = 'a';
EXPECT_EQ("data", data.To<std::string>());
}
TEST(CommonTypeConvertersTest, StdStringToArrayUint8) {
std::string input("data");
Array<uint8_t> data = Array<uint8_t>::From(input);
ASSERT_EQ(4ul, data.size());
EXPECT_EQ('d', data[0]);
EXPECT_EQ('a', data[1]);
EXPECT_EQ('t', data[2]);
EXPECT_EQ('a', data[3]);
}
struct RunnableNoArgs {
RunnableNoArgs(int* calls) : calls(calls) {}
void Run() const { (*calls)++; }
......@@ -113,7 +17,7 @@ struct RunnableNoArgs {
int* calls;
};
TEST(CommonTypeConvertersTest, BaseBindToMojoCallbackNoParams) {
TEST(CallbackBindingTest, BaseBindToMojoCallbackNoParams) {
mojo::Callback<void()> cb;
int calls = 0;
RunnableNoArgs r(&calls);
......@@ -133,7 +37,7 @@ struct RunnableOnePrimitiveArg {
int* calls;
};
TEST(CommonTypeConvertersTest, BaseBindToMojoCallbackPrimitiveParam) {
TEST(CallbackBindingTest, BaseBindToMojoCallbackPrimitiveParam) {
mojo::Callback<void(int)> mojo_callback;
int calls = 0;
RunnableOnePrimitiveArg r(&calls);
......@@ -150,12 +54,12 @@ TEST(CommonTypeConvertersTest, BaseBindToMojoCallbackPrimitiveParam) {
struct RunnableOneMojoStringParam {
explicit RunnableOneMojoStringParam(int* calls) : calls(calls) {}
void Run(const String& s) const { (*calls)++; }
void Run(const mojo::String& s) const { (*calls)++; }
int* calls;
};
TEST(CommonTypeConvertersTest, BaseBindToMojoCallbackMojoStringParam) {
TEST(CallbackBindingTest, BaseBindToMojoCallbackMojoStringParam) {
// The mojo type is a callback on mojo::String, but it'll expect to invoke
// callbacks with a parameter of type 'const Mojo::String&'.
mojo::Callback<void(mojo::String)> mojo_callback;
......@@ -172,7 +76,7 @@ TEST(CommonTypeConvertersTest, BaseBindToMojoCallbackMojoStringParam) {
EXPECT_EQ(2, calls);
}
using ExampleMoveOnlyType = Map<int, int>;
using ExampleMoveOnlyType = mojo::Map<int, int>;
struct RunnableOneMoveOnlyParam {
explicit RunnableOneMoveOnlyParam(int* calls) : calls(calls) {}
......@@ -181,7 +85,7 @@ struct RunnableOneMoveOnlyParam {
int* calls;
};
TEST(CommonTypeConvertersTest, BaseBindToMoveOnlyParam) {
TEST(CallbackBindingTest, BaseBindToMoveOnlyParam) {
mojo::Callback<void(ExampleMoveOnlyType)> mojo_callback;
int calls = 0;
RunnableOneMoveOnlyParam r(&calls);
......@@ -198,6 +102,3 @@ TEST(CommonTypeConvertersTest, BaseBindToMoveOnlyParam) {
}
} // namespace
} // namespace test
} // namespace common
} // namespace mojo
......@@ -4,14 +4,14 @@
import("//mojo/public/dart/rules.gni")
dartzip_package("dart") {
dart_pkg("dart") {
libs = [ "lib/tracing_helper.dart" ]
sources = [
"lib/trace_controller_impl.dart",
"lib/tracing_helper.dart",
"lib/trace_provider_impl.dart",
"pubspec.yaml",
]
deps = [
"//mojo/public/dart",
"//mojo/services/tracing/public/interfaces",
"//mojo/dart/mojo_services",
]
}
......@@ -7,40 +7,40 @@ import 'dart:async';
import 'package:mojo/application.dart';
import 'package:mojo/bindings.dart';
import 'package:mojo/core.dart';
import 'package:mojom/tracing/tracing.mojom.dart';
import 'package:mojo_services/tracing/tracing.mojom.dart';
class TraceControllerImpl implements TraceController {
TraceControllerStub _stub;
TraceDataCollectorProxy _collector;
class TraceProviderImpl implements TraceProvider {
TraceProviderStub _stub;
TraceRecorderProxy _recorder;
// TODO(rudominer) We currently ignore _categories.
String _categories;
TraceControllerImpl.fromEndpoint(MojoMessagePipeEndpoint e) {
_stub = TraceControllerStub.newFromEndpoint(e);
TraceProviderImpl.fromEndpoint(MojoMessagePipeEndpoint e) {
_stub = TraceProviderStub.newFromEndpoint(e);
_stub.impl = this;
}
@override
void startTracing(String categories, TraceDataCollectorProxy collector) {
assert(_collector == null);
_collector = collector;
void startTracing(String categories, TraceRecorderProxy recorder) {
assert(_recorder == null);
_recorder = recorder;
_categories = categories;
}
@override
void stopTracing() {
assert(_collector != null);
_collector.close();
_collector = null;
assert(_recorder != null);
_recorder.close();
_recorder = null;
}
bool isActive() {
return _collector != null;
return _recorder != null;
}
void sendTraceMessage(String message) {
if (_collector != null) {
_collector.ptr.dataCollected(message);
if (_recorder != null) {
_recorder.ptr.record(message);
}
}
}
......@@ -4,7 +4,7 @@
library tracing;
import 'trace_controller_impl.dart';
import 'trace_provider_impl.dart';
import 'dart:async';
import 'dart:convert';
......@@ -15,12 +15,12 @@ import 'dart:isolate';
import 'package:mojo/application.dart';
import 'package:mojo/bindings.dart';
import 'package:mojo/core.dart';
import 'package:mojom/tracing/tracing.mojom.dart';
import 'package:mojo_services/tracing/tracing.mojom.dart';
// TracingHelper is used by Dart code running in the Mojo shell in order
// to perform tracing.
class TracingHelper {
TraceControllerImpl _impl;
TraceProviderImpl _impl;
String _tid;
// Construct an instance of TracingHelper from within your application's
......@@ -36,9 +36,9 @@ class TracingHelper {
}
_tid = "${appName}/${Isolate.current.hashCode.toString()}";
ApplicationConnection connection = app.connectToApplication("mojo:tracing");
connection.provideService(TraceControllerName, (e) {
connection.provideService(TraceProviderName, (e) {
assert(_impl == null);
_impl = new TraceControllerImpl.fromEndpoint(e);
_impl = new TraceProviderImpl.fromEndpoint(e);
});
}
......
......@@ -5,8 +5,8 @@
#include "mojo/common/interface_ptr_set.h"
#include "base/message_loop/message_loop.h"
#include "mojo/common/message_pump_mojo.h"
#include "mojo/common/test_interfaces.mojom.h"
#include "mojo/message_pump/message_pump_mojo.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "testing/gtest/include/gtest/gtest.h"
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/common/trace_controller_impl.h"
#include "mojo/common/trace_provider_impl.h"
#include "base/logging.h"
#include "base/trace_event/trace_config.h"
......@@ -12,19 +12,18 @@
namespace mojo {
TraceControllerImpl::TraceControllerImpl(
InterfaceRequest<tracing::TraceController> request)
TraceProviderImpl::TraceProviderImpl(
InterfaceRequest<tracing::TraceProvider> request)
: tracing_already_started_(false), binding_(this, request.Pass()) {
}
TraceControllerImpl::~TraceControllerImpl() {
TraceProviderImpl::~TraceProviderImpl() {
}
void TraceControllerImpl::StartTracing(
const String& categories,
tracing::TraceDataCollectorPtr collector) {
DCHECK(!collector_.get());
collector_ = collector.Pass();
void TraceProviderImpl::StartTracing(const String& categories,
tracing::TraceRecorderPtr collector) {
DCHECK(!recorder_.get());
recorder_ = collector.Pass();
if (!tracing_already_started_) {
std::string categories_str = categories.To<std::string>();
base::trace_event::TraceLog::GetInstance()->SetEnabled(
......@@ -34,21 +33,21 @@ void TraceControllerImpl::StartTracing(
}
}
void TraceControllerImpl::StopTracing() {
DCHECK(collector_);
void TraceProviderImpl::StopTracing() {
DCHECK(recorder_);
base::trace_event::TraceLog::GetInstance()->SetDisabled();
base::trace_event::TraceLog::GetInstance()->Flush(
base::Bind(&TraceControllerImpl::SendChunk, base::Unretained(this)));
base::Bind(&TraceProviderImpl::SendChunk, base::Unretained(this)));
}
void TraceControllerImpl::SendChunk(
void TraceProviderImpl::SendChunk(
const scoped_refptr<base::RefCountedString>& events_str,
bool has_more_events) {
DCHECK(collector_);
collector_->DataCollected(mojo::String(events_str->data()));
DCHECK(recorder_);
recorder_->Record(mojo::String(events_str->data()));
if (!has_more_events) {
collector_.reset();
recorder_.reset();
}
}
......
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_COMMON_TRACING_CONTROLLER_IMPL_H_
#define MOJO_COMMON_TRACING_CONTROLLER_IMPL_H_
#ifndef MOJO_COMMON_TRACE_PROVIDER_IMPL_H_
#define MOJO_COMMON_TRACE_PROVIDER_IMPL_H_
#include "base/memory/ref_counted_memory.h"
#include "mojo/public/cpp/bindings/interface_request.h"
......@@ -12,11 +12,10 @@
namespace mojo {
class TraceControllerImpl : public tracing::TraceController {
class TraceProviderImpl : public tracing::TraceProvider {
public:
explicit TraceControllerImpl(
InterfaceRequest<tracing::TraceController> request);
~TraceControllerImpl() override;
explicit TraceProviderImpl(InterfaceRequest<tracing::TraceProvider> request);
~TraceProviderImpl() override;
// Set to true if base::trace_event::TraceLog is enabled externally to this
// class. If this is set to true this class will save the collector but not
......@@ -27,21 +26,21 @@ class TraceControllerImpl : public tracing::TraceController {
}
private:
// tracing::TraceController implementation:
// tracing::TraceProvider implementation:
void StartTracing(const String& categories,
tracing::TraceDataCollectorPtr collector) override;
tracing::TraceRecorderPtr collector) override;
void StopTracing() override;
void SendChunk(const scoped_refptr<base::RefCountedString>& events_str,
bool has_more_events);
bool tracing_already_started_;
tracing::TraceDataCollectorPtr collector_;
StrongBinding<tracing::TraceController> binding_;
tracing::TraceRecorderPtr recorder_;
StrongBinding<tracing::TraceProvider> binding_;
DISALLOW_COPY_AND_ASSIGN(TraceControllerImpl);
DISALLOW_COPY_AND_ASSIGN(TraceProviderImpl);
};
} // namespace mojo
#endif // MOJO_COMMON_TRACING_CONTROLLER_IMPL_H_
#endif // MOJO_COMMON_TRACE_PROVIDER_IMPL_H_
......@@ -5,7 +5,7 @@
#include "mojo/common/tracing_impl.h"
#include "base/trace_event/trace_event_impl.h"
#include "mojo/common/trace_controller_impl.h"
#include "mojo/common/trace_provider_impl.h"
#include "mojo/public/cpp/application/application_connection.h"
#include "mojo/public/cpp/application/application_impl.h"
......@@ -23,8 +23,8 @@ void TracingImpl::Initialize(ApplicationImpl* app) {
}
void TracingImpl::Create(ApplicationConnection* connection,
InterfaceRequest<tracing::TraceController> request) {
new TraceControllerImpl(request.Pass());
InterfaceRequest<tracing::TraceProvider> request) {
new TraceProviderImpl(request.Pass());
}
} // namespace mojo
......@@ -13,7 +13,7 @@ namespace mojo {
class ApplicationImpl;
class TracingImpl : public InterfaceFactory<tracing::TraceController> {
class TracingImpl : public InterfaceFactory<tracing::TraceProvider> {
public:
TracingImpl();
~TracingImpl() override;
......@@ -23,9 +23,9 @@ class TracingImpl : public InterfaceFactory<tracing::TraceController> {
void Initialize(ApplicationImpl* app);
private:
// InterfaceFactory<tracing::TraceController> implementation.
// InterfaceFactory<tracing::TraceProvider> implementation.
void Create(ApplicationConnection* connection,
InterfaceRequest<tracing::TraceController> request) override;
InterfaceRequest<tracing::TraceProvider> request) override;
DISALLOW_COPY_AND_ASSIGN(TracingImpl);
};
......
# Copyright 2015 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.
source_set("array_string") {
sources = [
"array_string_type_converters.cc",
"array_string_type_converters.h",
]
deps = [
"//base",
"//mojo/public/cpp/bindings",
]
}
source_set("tests") {
testonly = true
sources = [
"array_string_type_converters_unittest.cc",
]
deps = [
":array_string",
"//base",
"//testing/gtest",
]
}
// Copyright 2013 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 "mojo/converters/array_string/array_string_type_converters.h"
#include <string>
#include "base/strings/utf_string_conversions.h"
namespace mojo {
std::string TypeConverter<std::string, Array<uint8_t>>::Convert(
const Array<uint8_t>& input) {
if (input.is_null())
return std::string();
return std::string(reinterpret_cast<const char*>(&input.front()),
input.size());
}
Array<uint8_t> TypeConverter<Array<uint8_t>, std::string>::Convert(
const std::string& input) {
Array<uint8_t> result(input.size());
memcpy(&result.front(), input.c_str(), input.size());
return result.Pass();
}
} // namespace mojo
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_COMMON_COMMON_TYPE_CONVERTERS_H_
#define MOJO_COMMON_COMMON_TYPE_CONVERTERS_H_
#ifndef MOJO_CONVERTERS_ARRAY_STRING_ARRAY_STRING_TYPE_CONVERTERS_H_
#define MOJO_CONVERTERS_ARRAY_STRING_ARRAY_STRING_TYPE_CONVERTERS_H_
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
......@@ -11,40 +11,8 @@
#include "mojo/public/cpp/bindings/string.h"
#include "mojo/public/cpp/bindings/type_converter.h"
class GURL;
namespace mojo {
template <>
struct TypeConverter<String, base::StringPiece> {
static String Convert(const base::StringPiece& input);
};
template <>
struct TypeConverter<base::StringPiece, String> {
static base::StringPiece Convert(const String& input);
};
template <>
struct TypeConverter<String, base::string16> {
static String Convert(const base::string16& input);
};
template <>
struct TypeConverter<base::string16, String> {
static base::string16 Convert(const String& input);
};
template <>
struct TypeConverter<String, GURL> {
static String Convert(const GURL& input);
};
template <>
struct TypeConverter<GURL, String> {
static GURL Convert(const String& input);
};
// TODO(erg): In the very long term, we will want to remove conversion between
// std::strings and arrays of unsigned bytes. However, there is too much code
// across chrome which uses std::string as a bag of bytes that we probably
......@@ -61,4 +29,4 @@ struct TypeConverter<Array<uint8_t>, std::string> {
} // namespace mojo
#endif // MOJO_COMMON_COMMON_TYPE_CONVERTERS_H_
#endif // MOJO_CONVERTERS_ARRAY_STRING_ARRAY_STRING_TYPE_CONVERTERS_H_
// Copyright 2013 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 "mojo/converters/array_string/array_string_type_converters.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace common {
namespace test {
namespace {
TEST(CommonTypeConvertersTest, ArrayUint8ToStdString) {
Array<uint8_t> data(4);
data[0] = 'd';
data[1] = 'a';
data[2] = 't';
data[3] = 'a';
EXPECT_EQ("data", data.To<std::string>());
}
TEST(CommonTypeConvertersTest, StdStringToArrayUint8) {
std::string input("data");
Array<uint8_t> data = Array<uint8_t>::From(input);
ASSERT_EQ(4ul, data.size());
EXPECT_EQ('d', data[0]);
EXPECT_EQ('a', data[1]);
EXPECT_EQ('t', data[2]);
EXPECT_EQ('a', data[3]);
}
} // namespace
} // namespace test
} // namespace common
} // namespace mojo
......@@ -2,14 +2,29 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
import sys
source_set("base") {
sources = [
"base_type_converters.cc",
"base_type_converters.h",
]
deps = [
"//base",
"//mojo/public/cpp/bindings",
]
}
def add_lib_to_path():
""" Adds the devtools pylib to path, allowing to use it in the internal
/mojo/tools/ tooling. """
sys.path.append(os.path.join(os.path.dirname(__file__),
os.pardir,
"devtools",
"common"))
source_set("tests") {
testonly = true
sources = [
"base_type_converters_unittest.cc",
]
deps = [
":base",
"//base",
"//mojo/public/cpp/bindings",
"//testing/gtest",
]
}
// Copyright 2013 The Chromium Authors. All rights reserved.
// Copyright 2015 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 "mojo/common/common_type_converters.h"
#include "mojo/converters/base/base_type_converters.h"
#include <string>
#include "base/strings/utf_string_conversions.h"
#include "url/gurl.h"
namespace mojo {
......@@ -38,28 +37,4 @@ base::string16 TypeConverter<base::string16, String>::Convert(
return base::UTF8ToUTF16(input.To<base::StringPiece>());
}
String TypeConverter<String, GURL>::Convert(const GURL& input) {
return String(input.spec());
}
GURL TypeConverter<GURL, String>::Convert(const String& input) {
return GURL(input.get());
}
std::string TypeConverter<std::string, Array<uint8_t> >::Convert(
const Array<uint8_t>& input) {
if (input.is_null())
return std::string();
return std::string(reinterpret_cast<const char*>(&input.front()),
input.size());
}
Array<uint8_t> TypeConverter<Array<uint8_t>, std::string>::Convert(
const std::string& input) {
Array<uint8_t> result(input.size());
memcpy(&result.front(), input.c_str(), input.size());
return result.Pass();
}
} // namespace mojo
// Copyright 2015 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.
#ifndef MOJO_CONVERTERS_BASE_BASE_TYPE_CONVERTERS_H_
#define MOJO_CONVERTERS_BASE_BASE_TYPE_CONVERTERS_H_
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
#include "mojo/public/cpp/bindings/array.h"
#include "mojo/public/cpp/bindings/string.h"
#include "mojo/public/cpp/bindings/type_converter.h"
namespace mojo {
template <>
struct TypeConverter<String, base::StringPiece> {
static String Convert(const base::StringPiece& input);
};
template <>
struct TypeConverter<base::StringPiece, String> {
static base::StringPiece Convert(const String& input);
};
template <>
struct TypeConverter<String, base::string16> {
static String Convert(const base::string16& input);
};
template <>
struct TypeConverter<base::string16, String> {
static base::string16 Convert(const String& input);
};
} // namespace mojo
#endif // MOJO_CONVERTERS_BASE_BASE_TYPE_CONVERTERS_H_
// Copyright 2013 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 "mojo/converters/base/base_type_converters.h"
#include "base/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace {
void ExpectEqualsStringPiece(const std::string& expected,
const base::StringPiece& str) {
EXPECT_EQ(expected, str.as_string());
}
void ExpectEqualsMojoString(const std::string& expected, const String& str) {
EXPECT_EQ(expected, str.get());
}
void ExpectEqualsString16(const base::string16& expected,
const base::string16& actual) {
EXPECT_EQ(expected, actual);
}
void ExpectEqualsMojoString(const base::string16& expected, const String& str) {
EXPECT_EQ(expected, str.To<base::string16>());
}
TEST(BaseTypeConvertersTest, StringPiece) {
std::string kText("hello world");
base::StringPiece string_piece(kText);
String mojo_string(String::From(string_piece));
ExpectEqualsMojoString(kText, mojo_string);
ExpectEqualsStringPiece(kText, mojo_string.To<base::StringPiece>());
// Test implicit construction and conversion:
ExpectEqualsMojoString(kText, String::From(string_piece));
ExpectEqualsStringPiece(kText, mojo_string.To<base::StringPiece>());
// Test null String:
base::StringPiece empty_string_piece = String().To<base::StringPiece>();
EXPECT_TRUE(empty_string_piece.empty());
}
TEST(BaseTypeConvertersTest, String16) {
const base::string16 string16(base::ASCIIToUTF16("hello world"));
const String mojo_string(String::From(string16));
ExpectEqualsMojoString(string16, mojo_string);
EXPECT_EQ(string16, mojo_string.To<base::string16>());
// Test implicit construction and conversion:
ExpectEqualsMojoString(string16, String::From(string16));
ExpectEqualsString16(string16, mojo_string.To<base::string16>());
// Test empty string conversion.
ExpectEqualsMojoString(base::string16(), String::From(base::string16()));
}
} // namespace
} // namespace mojo
......@@ -2,23 +2,29 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
This script writes a time stamp.
Has one argument - time stamp file. Usage:
python stamp.py path/to/file
"""
source_set("url") {
sources = [
"url_type_converters.cc",
"url_type_converters.h",
]
import sys
deps = [
"//mojo/public/cpp/bindings",
"//url",
]
}
def WriteStampFile(stamp_file):
with open(stamp_file, "w"):
pass
source_set("tests") {
testonly = true
sources = [
"url_type_converters_unittest.cc",
]
def main(argv):
stamp_file = argv[1]
WriteStampFile(stamp_file)
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv))
deps = [
":url",
"//mojo/public/cpp/bindings",
"//testing/gtest",
"//url",
]
}
......@@ -2,21 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_TOOLS_EMBED_DATA_H_
#define MOJO_TOOLS_EMBED_DATA_H_
#include "mojo/converters/url/url_type_converters.h"
#include <stddef.h> // For size_t.
#include "url/gurl.h"
namespace mojo {
namespace embed {
struct Data {
const char* const hash;
const char* const data;
const size_t size;
};
String TypeConverter<String, GURL>::Convert(const GURL& input) {
return String(input.spec());
}
} // namespace embed
} // namespace mojo
GURL TypeConverter<GURL, String>::Convert(const String& input) {
return GURL(input.get());
}
#endif // MOJO_TOOLS_EMBED_DATA_H_
} // namespace mojo
// Copyright 2015 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.
#ifndef MOJO_CONVERTERS_URL_URL_TYPE_CONVERTERS_H_
#define MOJO_CONVERTERS_URL_URL_TYPE_CONVERTERS_H_
#include "mojo/public/cpp/bindings/type_converter.h"
#include "mojo/public/cpp/bindings/string.h"
class GURL;
namespace mojo {
template <>
struct TypeConverter<String, GURL> {
static String Convert(const GURL& input);
};
template <>
struct TypeConverter<GURL, String> {
static GURL Convert(const String& input);
};
} // namespace mojo
#endif // MOJO_CONVERTERS_URL_URL_TYPE_CONVERTERS_H_
// Copyright 2015 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 "mojo/converters/url/url_type_converters.h"
#include "mojo/public/cpp/bindings/string.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace mojo {
namespace {
TEST(UrlTypeConvertersTest, URL) {
GURL url("mojo:foo");
String mojo_string(String::From(url));
ASSERT_EQ(url.spec(), mojo_string);
EXPECT_EQ(url.spec(), mojo_string.To<GURL>().spec());
EXPECT_EQ(url.spec(), String::From(url));
GURL invalid = String().To<GURL>();
ASSERT_TRUE(invalid.spec().empty());
String string_from_invalid = String::From(invalid);
EXPECT_FALSE(string_from_invalid.is_null());
ASSERT_EQ(0U, string_from_invalid.size());
}
} // namespace
} // namespace mojo
# Copyright 2014 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.
group("dart") {
deps = [
"//mojo/dart/apptest",
"//mojo/dart/dart_snapshotter($host_toolchain)",
"//mojo/dart/http_load_test",
"//mojo/dart/mojom",
"//mojo/dart/mojo_services",
"//mojo/dart/observatory_test",
]
}
......@@ -4,24 +4,13 @@
import("//mojo/public/dart/rules.gni")
dartzip_package("apptest") {
uses_pub = true
dart_pkg("apptest") {
libs = [ "lib/apptest.dart" ]
sources = [
"lib/apptest.dart",
"pubspec.yaml",
]
deps = [
"//mojo/public/dart",
]
}
dart_pkg("apptest_pkg") {
entrypoints = [ "lib/apptest.dart" ]
sources = [
"pubspec.yaml",
]
deps = [
"//mojo/public/dart:mojo",
"//third_party/dart-pkg",
]
}
......@@ -10,8 +10,7 @@ import 'package:mojo/application.dart';
import 'package:mojo/bindings.dart';
import 'package:mojo/core.dart';
// Import and reexport the test package. We are a *.dartzip file designed to
// be linked into your_apptest.mojo file and are your main entrypoint.
// Import and reexport the test package.
import 'package:test/test.dart';
export 'package:test/test.dart';
......
......@@ -2,27 +2,26 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
executable("message_generator") {
testonly = true
output_name = "mojo_message_generator"
executable("dart_snapshotter") {
sources = [
"message_generator.cc",
"main.cc",
"vm.cc",
"vm.h",
]
deps = [
"//base",
"//build/config/sanitizers:deps",
"//dart/runtime/vm:libdart_platform",
"//dart/runtime:libdart",
"//mojo/common",
"//mojo/dart/embedder:dart_snapshot_cc",
"//mojo/edk/system",
"//mojo/environment:chromium",
"//mojo/public/cpp/bindings",
"//testing/gtest",
]
}
executable("remote_file_reader") {
sources = [
"remote_file_reader.cc",
"//mojo/public/cpp/system",
"//mojo/public/cpp/utility",
"//mojo/public/interfaces/application",
"//third_party/zlib",
"//tonic",
]
}
// Copyright 2015 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 <iostream>
#include "base/at_exit.h"
#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/process/memory.h"
#include "dart/runtime/include/dart_api.h"
#include "mojo/dart/dart_snapshotter/vm.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/edk/embedder/simple_platform_support.h"
#include "tonic/dart_error.h"
#include "tonic/dart_isolate_scope.h"
#include "tonic/dart_library_loader.h"
#include "tonic/dart_library_provider_files.h"
#include "tonic/dart_script_loader_sync.h"
#include "tonic/dart_state.h"
const char kHelp[] = "help";
const char kPackageRoot[] = "package-root";
const char kSnapshot[] = "snapshot";
const uint8_t magic_number[] = { 0xf5, 0xf5, 0xdc, 0xdc };
void Usage() {
std::cerr << "Usage: dart_snapshotter"
<< " --" << kPackageRoot << " --" << kSnapshot
<< " <dart-app>" << std::endl;
}
void WriteSnapshot(base::FilePath path) {
uint8_t* buffer;
intptr_t size;
CHECK(!tonic::LogIfError(Dart_CreateScriptSnapshot(&buffer, &size)));
intptr_t magic_number_len = sizeof(magic_number);
CHECK_EQ(base::WriteFile(
path, reinterpret_cast<const char*>(magic_number), sizeof(magic_number)),
magic_number_len);
CHECK(base::AppendToFile(
path, reinterpret_cast<const char*>(buffer), size));
}
int main(int argc, const char* argv[]) {
base::AtExitManager exit_manager;
base::EnableTerminationOnHeapCorruption();
base::CommandLine::Init(argc, argv);
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(kHelp) || command_line.GetArgs().empty()) {
Usage();
return 0;
}
// Initialize mojo.
mojo::embedder::Init(
make_scoped_ptr(new mojo::embedder::SimplePlatformSupport()));
InitDartVM();
CHECK(command_line.HasSwitch(kPackageRoot)) << "Need --package-root";
CHECK(command_line.HasSwitch(kSnapshot)) << "Need --snapshot";
auto args = command_line.GetArgs();
CHECK(args.size() == 1);
Dart_Isolate isolate = CreateDartIsolate();
CHECK(isolate);
tonic::DartIsolateScope scope(isolate);
tonic::DartApiScope api_scope;
auto isolate_data = SnapshotterDartState::Current();
CHECK(isolate_data != nullptr);
// Use tonic's library tag handler.
CHECK(!tonic::LogIfError(Dart_SetLibraryTagHandler(
tonic::DartLibraryLoader::HandleLibraryTag)));
// Use tonic's file system library provider.
isolate_data->set_library_provider(
new tonic::DartLibraryProviderFiles(
command_line.GetSwitchValuePath(kPackageRoot)));
// Load script.
tonic::DartScriptLoaderSync::LoadScript(args[0],
isolate_data->library_provider());
// Write snapshot.
WriteSnapshot(command_line.GetSwitchValuePath(kSnapshot));
return 0;
}
#!/usr/bin/env python
# Copyright 2015 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 argparse
import hashlib
import os
import subprocess
import sys
import tempfile
SNAPSHOT_TEST_DIR = os.path.dirname(os.path.abspath(__file__))
SRC_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(
SNAPSHOT_TEST_DIR))))
DART_DIR = os.path.join(SRC_ROOT, 'dart')
VM_SNAPSHOT_FILES=[
# Header files.
'datastream.h',
'object.h',
'raw_object.h',
'snapshot.h',
'snapshot_ids.h',
'symbols.h',
# Source files.
'dart.cc',
'dart_api_impl.cc',
'object.cc',
'raw_object.cc',
'raw_object_snapshot.cc',
'snapshot.cc',
'symbols.cc',
]
def makeSnapshotHashString():
vmhash = hashlib.md5()
for vmfilename in VM_SNAPSHOT_FILES:
vmfilepath = os.path.join(DART_DIR, 'runtime', 'vm', vmfilename)
with open(vmfilepath) as vmfile:
vmhash.update(vmfile.read())
return vmhash.hexdigest()
def main():
parser = argparse.ArgumentParser(description='Tests Dart snapshotting')
parser.add_argument("--build-dir",
dest="build_dir",
metavar="<build-directory>",
type=str,
required=True,
help="The directory containing the Mojo build.")
args = parser.parse_args()
dart_snapshotter = os.path.join(args.build_dir, 'dart_snapshotter')
package_root = os.path.join(args.build_dir, 'gen', 'dart-pkg', 'packages')
main_dart = os.path.join(
args.build_dir, 'gen', 'dart-pkg', 'mojo_dart_hello', 'lib', 'main.dart')
snapshot = tempfile.mktemp()
if not os.path.isfile(dart_snapshotter):
print "file not found: " + dart_snapshotter
return 1
subprocess.check_call([
dart_snapshotter,
main_dart,
'--package-root=%s' % package_root,
'--snapshot=%s' % snapshot,
])
if not os.path.isfile(snapshot):
return 1
expected_hash = makeSnapshotHashString()
actual_hash = ""
with open(snapshot) as snapshot_file:
snapshot_file.seek(20)
actual_hash = snapshot_file.read(32)
if not actual_hash == expected_hash:
print ('wrong hash: actual = %s, expected = %s'
% (actual_hash, expected_hash))
return 1
return 0
if __name__ == '__main__':
sys.exit(main())
// Copyright 2015 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 "mojo/dart/dart_snapshotter/vm.h"
#include "base/logging.h"
#include "tonic/dart_error.h"
#include "tonic/dart_state.h"
namespace mojo {
namespace dart {
extern const uint8_t* vm_isolate_snapshot_buffer;
extern const uint8_t* isolate_snapshot_buffer;
}
}
static const char* kDartArgs[] = {
"--enable_mirrors=false",
};
void InitDartVM() {
CHECK(Dart_SetVMFlags(arraysize(kDartArgs), kDartArgs));
CHECK(Dart_Initialize(mojo::dart::vm_isolate_snapshot_buffer, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr));
}
Dart_Isolate CreateDartIsolate() {
CHECK(mojo::dart::isolate_snapshot_buffer);
char* error = nullptr;
auto isolate_data = new SnapshotterDartState();
CHECK(isolate_data != nullptr);
Dart_Isolate isolate = Dart_CreateIsolate("dart:snapshot",
"main",
mojo::dart::isolate_snapshot_buffer,
nullptr,
isolate_data,
&error);
CHECK(isolate) << error;
Dart_ExitIsolate();
isolate_data->SetIsolate(isolate);
return isolate;
}
// Copyright 2015 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.
#ifndef MOJO_DART_DART_SNAPSHOTTER_VM_H_
#define MOJO_DART_DART_SNAPSHOTTER_VM_H_
#include "dart/runtime/include/dart_api.h"
#include "tonic/dart_library_provider.h"
#include "tonic/dart_state.h"
class SnapshotterDartState : public tonic::DartState {
public:
SnapshotterDartState() : library_provider_(nullptr) {
};
tonic::DartLibraryProvider* library_provider() const {
return library_provider_.get();
}
void set_library_provider(tonic::DartLibraryProvider* library_provider) {
library_provider_.reset(library_provider);
DCHECK(library_provider_.get() == library_provider);
}
static SnapshotterDartState* From(Dart_Isolate isolate) {
return reinterpret_cast<SnapshotterDartState*>(DartState::From(isolate));
}
static SnapshotterDartState* Current() {
return reinterpret_cast<SnapshotterDartState*>(DartState::Current());
}
static SnapshotterDartState* Cast(void* data) {
return reinterpret_cast<SnapshotterDartState*>(data);
}
private:
std::unique_ptr<tonic::DartLibraryProvider> library_provider_;
};
void InitDartVM();
Dart_Isolate CreateDartIsolate();
#endif // MOJO_DART_DART_SNAPSHOTTER_VM_H_
......@@ -43,7 +43,7 @@ source_set("dart_controller_no_snapshot") {
"//dart/runtime:libdart",
"//dart/runtime/bin:libdart_embedder_noio",
"//third_party/dart-pkg",
"//mojo/common",
"//mojo/message_pump",
"//mojo/public/c/system",
"//mojo/public/cpp/system",
"//mojo/services/network/public/interfaces",
......@@ -410,3 +410,13 @@ action("generate_snapshot_file") {
rebase_path(output),
]
}
source_set("dart_snapshot_cc") {
sources = [
"$root_gen_dir/dart_snapshot.cc",
]
deps = [
":generate_snapshot_file",
]
}
......@@ -13,11 +13,11 @@
#include "base/sys_info.h"
#include "dart/runtime/include/dart_api.h"
#include "dart/runtime/include/dart_native_api.h"
#include "mojo/common/message_pump_mojo.h"
#include "mojo/dart/embedder/builtin.h"
#include "mojo/dart/embedder/dart_controller.h"
#include "mojo/dart/embedder/mojo_dart_state.h"
#include "mojo/dart/embedder/vmservice.h"
#include "mojo/message_pump/message_pump_mojo.h"
#include "mojo/public/c/system/core.h"
#include "tonic/dart_converter.h"
#include "tonic/dart_debugger.h"
......@@ -27,6 +27,7 @@
#include "tonic/dart_library_provider.h"
#include "tonic/dart_library_provider_files.h"
#include "tonic/dart_library_provider_network.h"
#include "tonic/dart_script_loader_sync.h"
namespace mojo {
namespace dart {
......@@ -39,6 +40,8 @@ static const char* kInternalLibURL = "dart:_internal";
static const char* kIsolateLibURL = "dart:isolate";
static const char* kCoreLibURL = "dart:core";
static uint8_t snapshot_magic_number[] = { 0xf5, 0xf5, 0xdc, 0xdc };
static Dart_Handle SetWorkingDirectory(Dart_Handle builtin_lib) {
base::FilePath current_dir;
PathService::Get(base::DIR_CURRENT, &current_dir);
......@@ -299,6 +302,8 @@ Dart_Isolate DartController::CreateIsolateHelper(
} else {
package_root_str = package_root.c_str();
}
isolate_data->library_loader().set_magic_number(
snapshot_magic_number, sizeof(snapshot_magic_number));
if (use_network_loader) {
mojo::NetworkService* network_service = isolate_data->network_service();
isolate_data->set_library_provider(
......@@ -349,12 +354,21 @@ Dart_Isolate DartController::CreateIsolateHelper(
// Special case for starting and stopping the the handle watcher isolate.
LoadEmptyScript(script_uri);
} else {
LoadScript(script_uri, isolate_data->library_provider());
tonic::DartScriptLoaderSync::LoadScript(
script_uri,
isolate_data->library_provider());
}
InitializeDartMojoIo();
}
if (isolate_data->library_loader().error_during_loading()) {
*error = strdup("Library loader reported error during loading. See log.");
Dart_EnterIsolate(isolate);
Dart_ShutdownIsolate();
return nullptr;
}
// Make the isolate runnable so that it is ready to handle messages.
bool retval = Dart_IsolateMakeRunnable(isolate);
if (!retval) {
......@@ -606,32 +620,22 @@ void DartController::InitVmIfNeeded(Dart_EntropySource entropy,
nullptr, nullptr, nullptr, nullptr,
entropy);
CHECK(result);
// By waiting for the load port, we ensure that the service isolate is fully
// running before returning.
Dart_ServiceWaitForLoadPort();
initialized_ = true;
service_isolate_running_ = true;
}
void DartController::BlockWaitingForDependencies(
tonic::DartLibraryLoader* loader,
const std::unordered_set<tonic::DartDependency*>& dependencies) {
{
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
base::MessageLoop::current()->task_runner();
base::RunLoop run_loop;
task_runner->PostTask(
FROM_HERE,
base::Bind(
&tonic::DartLibraryLoader::WaitForDependencies,
base::Unretained(loader),
dependencies,
base::Bind(
base::IgnoreResult(&base::SingleThreadTaskRunner::PostTask),
task_runner.get(), FROM_HERE,
run_loop.QuitClosure())));
run_loop.Run();
void DartController::BlockForServiceIsolate() {
base::AutoLock al(lock_);
BlockForServiceIsolateLocked();
}
void DartController::BlockForServiceIsolateLocked() {
if (service_isolate_running_) {
return;
}
// By waiting for the load port, we ensure that the service isolate is fully
// running before returning.
Dart_ServiceWaitForLoadPort();
service_isolate_running_ = true;
}
void DartController::LoadEmptyScript(const std::string& script_uri) {
......@@ -646,60 +650,11 @@ void DartController::LoadEmptyScript(const std::string& script_uri) {
tonic::LogIfError(Dart_FinalizeLoading(true));
}
void DartController::InnerLoadScript(
const std::string& script_uri,
tonic::DartLibraryProvider* library_provider) {
// When spawning isolates, Dart expects the script loading to be completed
// before returning from the isolate creation callback. The mojo dart
// controller also expects the isolate to be finished loading a script
// before the isolate creation callback returns.
// We block here by creating a nested message pump and waiting for the load
// to complete.
DCHECK(base::MessageLoop::current() != nullptr);
base::MessageLoop::ScopedNestableTaskAllower allow(
base::MessageLoop::current());
// Initiate the load.
auto dart_state = tonic::DartState::Current();
DCHECK(library_provider != nullptr);
tonic::DartLibraryLoader& loader = dart_state->library_loader();
loader.set_library_provider(library_provider);
std::unordered_set<tonic::DartDependency*> dependencies;
{
tonic::DartDependencyCatcher dependency_catcher(loader);
loader.LoadScript(script_uri);
// Copy dependencies before dependency_catcher goes out of scope.
dependencies = std::unordered_set<tonic::DartDependency*>(
dependency_catcher.dependencies());
}
// Run inner message loop.
BlockWaitingForDependencies(&loader, dependencies);
// Finalize loading.
tonic::LogIfError(Dart_FinalizeLoading(true));
}
void DartController::LoadScript(const std::string& script_uri,
tonic::DartLibraryProvider* library_provider) {
if (base::MessageLoop::current() == nullptr) {
// Threads running on the Dart thread pool may not have a message loop,
// we rely on a message loop during loading. Create a temporary one
// here.
base::MessageLoop message_loop(common::MessagePumpMojo::Create());
InnerLoadScript(script_uri, library_provider);
} else {
// Thread has a message loop, use it.
InnerLoadScript(script_uri, library_provider);
}
}
bool DartController::RunSingleDartScript(const DartControllerConfig& config) {
InitVmIfNeeded(config.entropy,
config.vm_flags,
config.vm_flags_count);
BlockForServiceIsolate();
Dart_Isolate isolate = CreateIsolateHelper(config.application_data,
config.strict_compilation,
config.callbacks,
......@@ -735,6 +690,7 @@ bool DartController::Initialize(
}
bool DartController::RunDartScript(const DartControllerConfig& config) {
BlockForServiceIsolate();
CHECK(service_isolate_running_);
const bool strict = strict_compilation_ || config.strict_compilation;
Dart_Isolate isolate = CreateIsolateHelper(config.application_data,
......@@ -762,6 +718,7 @@ void DartController::Shutdown() {
if (!initialized_) {
return;
}
BlockForServiceIsolateLocked();
StopHandleWatcherIsolate();
Dart_Cleanup();
service_isolate_running_ = false;
......
......@@ -156,14 +156,10 @@ class DartController {
const char** arguments,
int arguments_count);
static void BlockWaitingForDependencies(
tonic::DartLibraryLoader* loader,
const std::unordered_set<tonic::DartDependency*>& dependencies);
static void BlockForServiceIsolate();
static void BlockForServiceIsolateLocked();
static void LoadEmptyScript(const std::string& script_uri);
static void InnerLoadScript(const std::string& script_uri,
tonic::DartLibraryProvider* library_provider);
static void LoadScript(const std::string& script_uri,
tonic::DartLibraryProvider* library_provider);
static tonic::DartLibraryProvider* library_provider_;
static base::Lock lock_;
......
......@@ -91,9 +91,14 @@ class _NetworkServiceCodec {
r += '${digit}${divider}';
}
} else {
for (var i = 0; i < 16; i++) {
var digit = address[i].toRadixString(16);
var divider = (i != 15) ? ':' : '';
for (var i = 0; i < 16; i += 2) {
var first = '';
if (address[i] != 0) {
first = address[i].toRadixString(16).padLeft(2, '0');
}
var second = address[i + 1].toRadixString(16).padLeft(2, '0');
var digit = '$first$second';
var divider = (i != 14) ? ':' : '';
r += '${digit}${divider}';
}
}
......
......@@ -111,4 +111,4 @@ class MojoDartState : public tonic::DartState {
} // namespace dart
} // namespace mojo
#endif // MOJO_DART_EMBEDDER_DART_STATE_H_
\ No newline at end of file
#endif // MOJO_DART_EMBEDDER_DART_STATE_H_
......@@ -22,7 +22,7 @@ test("dart_unittests") {
"validation_unittest.cc",
]
deps = [
"//mojo/public/dart:mojo",
"//mojo/public/dart",
"//mojo/dart/testing",
":dart_controller_for_test",
":dart_to_cpp_unittests",
......
......@@ -2,15 +2,19 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Python utils."""
import("//mojo/public/dart/rules.gni")
def overrides(parent_class):
"""Inherits the docstring from the method of the same name in the indicated
parent class.
"""
def overriding(method):
assert(method.__name__ in dir(parent_class))
method.__doc__ = getattr(parent_class, method.__name__).__doc__
return method
return overriding
dart_pkg("http_load_test") {
apps = [ [
"dart_http_load_test",
"lib/main.dart",
] ]
sources = [
"lib/src/part0.dart",
"pubspec.yaml",
]
deps = [
"//mojo/public/dart",
"//mojo/dart/mojo_services",
]
}
......@@ -17,19 +17,20 @@ class Launcher {
// Completer completes once the child process exits.
var completer = new Completer();
String stdout = '';
String output = '';
process.stdout.transform(UTF8.decoder)
.transform(new LineSplitter()).listen((line) {
stdout = '$stdout\n$line';
output = '$output\n$line';
print(line);
});
process.stderr.transform(UTF8.decoder)
.transform(new LineSplitter()).listen((line) {
output = '$output\n$line';
print(line);
});
process.exitCode.then((ec) {
stdout = '$stdout\nEXIT_CODE=$ec\n';
completer.complete(stdout);
output = '$output\nEXIT_CODE=$ec\n';
completer.complete(output);
});
return completer.future;
}
......@@ -56,15 +57,18 @@ main(List<String> args) async {
}
});
var launchUrl = 'http://127.0.0.1:${server.port}/main.dart';
var stdout = await Launcher.launch(mojo_shell_executable, [launchUrl]);
var launchUrl = 'http://127.0.0.1:${server.port}/lib/main.dart';
var output = await Launcher.launch(mojo_shell_executable, [launchUrl]);
server.close();
if (!stdout.contains("\nPASS")) {
if (output.contains("ERROR")) {
throw "test failed.";
}
if (!output.contains("\nEXIT_CODE=0\n")) {
throw "Test failed.";
}
if (!stdout.contains("\nEXIT_CODE=0\n")) {
if (!output.contains("\nPASS")) {
throw "Test failed.";
}
}
......@@ -5,7 +5,9 @@ library test;
import 'dart:async';
import 'dart:mojo.internal';
part 'part0.dart';
import 'package:mojo_services/tracing/tracing.mojom.dart' as tracing;
part 'src/part0.dart';
main(List args) {
// Hang around for a sec and then close the shell handle.
......
# Generated by pub
# See http://pub.dartlang.org/doc/glossary.html#lockfile
packages: {}
name: mojo_dart_http_load_test
......@@ -40,6 +40,8 @@ if __name__ == '__main__':
help="Path to dart executable.")
args = parser.parse_args()
tester_directory = os.path.dirname(os.path.realpath(__file__))
tester_dart_script = os.path.join(tester_directory, 'tester.dart')
tester_dart_script = os.path.join(tester_directory, 'bin', 'tester.dart')
package_directory = os.path.join(
args.build_dir, 'gen', 'dart-pkg', 'mojo_dart_http_load_test')
sys.exit(main(args.build_dir, args.dart_exe, tester_dart_script,
tester_directory))
package_directory))
......@@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//mojo/public/dart/rules.gni")
import("//mojo/services/mojo_services.gni")
dart_pkg("mojo_services") {
sources = [
......
## 0.0.22
- 58 changes: https://github.com/domokit/mojo/compare/e172885...35de44e
## 0.0.16
- 89 changes: https://github.com/domokit/mojo/compare/0fd4d06...c3119f6
......
......@@ -4,4 +4,4 @@ dependencies:
description: Generated bindings for mojo services
homepage: https://github.com/domokit/mojo
name: mojo_services
version: 0.0.21
version: 0.0.22
......@@ -5,7 +5,7 @@
import("//mojo/public/dart/rules.gni")
dart_pkg("mojom") {
entrypoints = [ "lib/generate.dart" ]
libs = [ "lib/generate.dart" ]
sources = [
"CHANGELOG.md",
"README.md",
......
## 0.0.22
- 59 changes: https://github.com/domokit/mojo/compare/c73419d...35de44e
## 0.0.18
- 89 changes: https://github.com/domokit/mojo/compare/0fd4d06...c3119f6
......
......@@ -9,4 +9,4 @@ environment:
sdk: '>=1.9.0 <2.0.0'
homepage: https://github.com/domokit/mojo
name: mojom
version: 0.0.21
version: 0.0.22
......@@ -4,9 +4,13 @@
import("//mojo/public/dart/rules.gni")
dartzip_packaged_application("observatory_test") {
dart_pkg("observatory_test") {
apps = [ [
"dart_observatory_test",
"lib/main.dart",
] ]
sources = [
"main.dart",
"pubspec.yaml",
]
deps = [
"//mojo/public/dart",
......
......@@ -11,7 +11,7 @@ import subprocess
import sys
MOJO_SHELL = 'mojo_shell'
TESTEE = 'mojo:observatory_test'
TESTEE = 'mojo:dart_observatory_test'
def main(build_dir, dart_exe, tester_script):
shell_exe = os.path.join(build_dir, MOJO_SHELL)
......
......@@ -5,7 +5,7 @@
import("//mojo/public/dart/rules.gni")
dart_pkg("testing") {
entrypoints = [
libs = [
"lib/async_helper.dart",
"lib/expect.dart",
"lib/validation_test_input_parser.dart",
......
# Copyright 2015 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.
source_set("data_pipe_utils") {
sources = [
"data_pipe_drainer.cc",
"data_pipe_drainer.h",
"data_pipe_file_utils.cc",
"data_pipe_utils.cc",
"data_pipe_utils.h",
"data_pipe_utils_internal.h",
]
if (is_nacl) {
sources -= [ "data_pipe_file_utils.cc" ]
}
deps = [
"//base",
"//mojo/message_pump",
"//mojo/public/cpp/environment:environment",
"//mojo/public/cpp/system",
]
}
source_set("tests") {
testonly = true
sources = [
"data_pipe_utils_unittest.cc",
]
deps = [
":data_pipe_utils",
"//base",
"//testing/gtest",
]
}
......@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/common/data_pipe_drainer.h"
#include "mojo/data_pipe_utils/data_pipe_drainer.h"
#include "base/bind.h"
namespace mojo {
......@@ -10,21 +11,18 @@ namespace common {
DataPipeDrainer::DataPipeDrainer(Client* client,
mojo::ScopedDataPipeConsumerHandle source)
: client_(client),
source_(source.Pass()),
weak_factory_(this) {
: client_(client), source_(source.Pass()), weak_factory_(this) {
DCHECK(client_);
ReadData();
}
DataPipeDrainer::~DataPipeDrainer() {
}
DataPipeDrainer::~DataPipeDrainer() {}
void DataPipeDrainer::ReadData() {
const void* buffer = nullptr;
uint32_t num_bytes = 0;
MojoResult rv = BeginReadDataRaw(source_.get(),
&buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
MojoResult rv = BeginReadDataRaw(source_.get(), &buffer, &num_bytes,
MOJO_READ_DATA_FLAG_NONE);
if (rv == MOJO_RESULT_OK) {
client_->OnDataAvailable(buffer, num_bytes);
EndReadDataRaw(source_.get(), num_bytes);
......@@ -39,8 +37,8 @@ void DataPipeDrainer::ReadData() {
}
void DataPipeDrainer::WaitForData() {
handle_watcher_.Start(source_.get(),
MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
handle_watcher_.Start(
source_.get(), MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
base::Bind(&DataPipeDrainer::WaitComplete, weak_factory_.GetWeakPtr()));
}
......
......@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_COMMON_DATA_PIPE_DRAINER_H_
#define MOJO_COMMON_DATA_PIPE_DRAINER_H_
#ifndef MOJO_DATA_PIPE_UTILS_DATA_PIPE_DRAINER_H_
#define MOJO_DATA_PIPE_UTILS_DATA_PIPE_DRAINER_H_
#include "base/memory/weak_ptr.h"
#include "mojo/common/handle_watcher.h"
#include "mojo/message_pump/handle_watcher.h"
#include "mojo/public/cpp/system/core.h"
namespace mojo {
......@@ -20,7 +20,7 @@ class DataPipeDrainer {
virtual void OnDataComplete() = 0;
protected:
virtual ~Client() { }
virtual ~Client() {}
};
DataPipeDrainer(Client*, mojo::ScopedDataPipeConsumerHandle source);
......@@ -43,4 +43,4 @@ class DataPipeDrainer {
} // namespace common
} // namespace mojo
#endif // MOJO_COMMON_DATA_PIPE_DRAINER_H_
#endif // MOJO_DATA_PIPE_UTILS_DATA_PIPE_DRAINER_H_
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/common/data_pipe_utils.h"
#include "mojo/data_pipe_utils/data_pipe_utils.h"
#include <stdio.h>
......@@ -14,7 +14,7 @@
#include "base/files/scoped_file.h"
#include "base/location.h"
#include "base/trace_event/trace_event.h"
#include "mojo/common/data_pipe_utils_internal.h"
#include "mojo/data_pipe_utils/data_pipe_utils_internal.h"
#include "mojo/public/cpp/environment/async_waiter.h"
namespace mojo {
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/common/data_pipe_utils.h"
#include "mojo/data_pipe_utils/data_pipe_utils.h"
#include <stdio.h>
......@@ -10,18 +10,19 @@
#include "base/task_runner_util.h"
#include "base/threading/platform_thread.h"
#include "base/trace_event/trace_event.h"
#include "mojo/common/data_pipe_utils_internal.h"
#include "mojo/data_pipe_utils/data_pipe_utils_internal.h"
namespace mojo {
namespace common {
bool BlockingCopyHelper(ScopedDataPipeConsumerHandle source,
bool BlockingCopyHelper(
ScopedDataPipeConsumerHandle source,
const base::Callback<size_t(const void*, uint32_t)>& write_bytes) {
for (;;) {
const void* buffer = nullptr;
uint32_t num_bytes = 0;
MojoResult result = BeginReadDataRaw(
source.get(), &buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
MojoResult result = BeginReadDataRaw(source.get(), &buffer, &num_bytes,
MOJO_READ_DATA_FLAG_NONE);
if (result == MOJO_RESULT_OK) {
size_t bytes_written = write_bytes.Run(buffer, num_bytes);
if (bytes_written < num_bytes) {
......@@ -39,10 +40,8 @@ bool BlockingCopyHelper(ScopedDataPipeConsumerHandle source,
return false;
}
} else if (result == MOJO_RESULT_SHOULD_WAIT) {
result = Wait(source.get(),
MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE,
nullptr);
result = Wait(source.get(), MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, nullptr);
if (result != MOJO_RESULT_OK) {
// If the producer handle was closed, then treat as EOF.
return result == MOJO_RESULT_FAILED_PRECONDITION;
......@@ -60,13 +59,14 @@ bool BlockingCopyHelper(ScopedDataPipeConsumerHandle source,
namespace {
size_t CopyToStringHelper(
std::string* result, const void* buffer, uint32_t num_bytes) {
size_t CopyToStringHelper(std::string* result,
const void* buffer,
uint32_t num_bytes) {
result->append(static_cast<const char*>(buffer), num_bytes);
return num_bytes;
}
} // namespace
} // namespace
// TODO(hansmuller): Add a max_size parameter.
bool BlockingCopyToString(ScopedDataPipeConsumerHandle source,
......@@ -74,8 +74,8 @@ bool BlockingCopyToString(ScopedDataPipeConsumerHandle source,
TRACE_EVENT0("data_pipe_utils", "BlockingCopyToString");
CHECK(result);
result->clear();
return BlockingCopyHelper(
source.Pass(), base::Bind(&CopyToStringHelper, result));
return BlockingCopyHelper(source.Pass(),
base::Bind(&CopyToStringHelper, result));
}
bool BlockingCopyFromString(const std::string& source,
......
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_SHELL_DATA_PIPE_UTILS_H_
#define MOJO_SHELL_DATA_PIPE_UTILS_H_
#ifndef MOJO_DATA_PIPE_UTILS_DATA_PIPE_UTILS_H_
#define MOJO_DATA_PIPE_UTILS_DATA_PIPE_UTILS_H_
#include <string>
......@@ -51,4 +51,4 @@ bool BlockingCopyToFile(ScopedDataPipeConsumerHandle source,
} // namespace common
} // namespace mojo
#endif // MOJO_SHELL_DATA_PIPE_UTILS_H_
#endif // MOJO_DATA_PIPE_UTILS_DATA_PIPE_UTILS_H_
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_COMMON_DATA_PIPE_UTILS_INTERNAL_H_
#define MOJO_COMMON_DATA_PIPE_UTILS_INTERNAL_H_
#ifndef MOJO_DATA_PIPE_UTILS_DATA_PIPE_UTILS_INTERNAL_H_
#define MOJO_DATA_PIPE_UTILS_DATA_PIPE_UTILS_INTERNAL_H_
#include "base/callback_forward.h"
#include "mojo/public/cpp/system/core.h"
......@@ -19,4 +19,4 @@ bool BlockingCopyHelper(
} // namespace common
} // namespace mojo
#endif // MOJO_COMMON_DATA_PIPE_UTILS_INTERNAL_H_
#endif // MOJO_DATA_PIPE_UTILS_DATA_PIPE_UTILS_INTERNAL_H_
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/common/data_pipe_utils.h"
#include "mojo/data_pipe_utils/data_pipe_utils.h"
#include "base/bind.h"
#include "base/files/file_util.h"
......
# Devtools packages
The `common` subdirectory contains what we currently expose as "devtools",
mirroring it as a [separate repository](https://github.com/domokit/devtools) for
consumption without a Mojo checkout.
Further subdirectories TBD might be added in the future, to contain heavy
language-specific tooling which we will mirror / expose separately.
The toolsets are intended for consumption by Mojo consumers as **separate
checkouts**. No dependencies on files outside of devtools are allowed.
# Devtools
Unopinionated tools for **running**, **debugging** and **testing** Mojo apps.
## Install
```
git clone https://github.com/domokit/devtools.git
```
## Contents
Devtools offers the following tools:
- `mojo_run` - shell runner
- `mojo_test` - apptest runner
- `mojo_debug` - debugger supporting interactive tracing and debugging of a
running mojo shell
Additionally, `remote_adb_setup` script helps to configure adb on a remote
machine to communicate with a device attached to a local machine, forwarding the
ports used by `mojo_run`.
### Runner
`mojo_run` allows you to run a Mojo shell either on the host, or on an attached
Android device.
```sh
mojo_run APP_URL # Run on the host.
mojo_run APP_URL --android # Run on Android device.
mojo_run "APP_URL APP_ARGUMENTS" # Run an app with startup arguments
```
Unless running within a Mojo checkout, we need to indicate the path to the shell
binary:
```sh
mojo_run --shell-path path/to/shell/binary APP_URL
```
Some applications are running embedded inside a window manager. To start such an
app, you have to first start the window manager app, then have it embed the app
you are interested in. It is done as follows using the default window manager:
```sh
mojo_run "mojo:window_manager APP_URL"
```
By default, `mojo_run` uses `mojo:kiosk_wm` as the default window manager. It
can be changed using the `--window-manager` flag.
#### Sky apps
To run a [Sky](https://github.com/domokit/sky_engine) app, you need to build
`sky_viewer.mojo` in a Sky checkout, and indicate the path to the binary using
the `--map-url` parameter:
```sh
mojo_run --map-url mojo:sky_viewer=/path/to/sky/viewer "mojo:window_manager APP_URL"
```
If the app does not declare a shebang indicating that it needs to be run in
`sky_viewer`, pass `--sky` to map `sky_viewer` as a default content handler for
dart apps:
```sh
mojo_run --map-url mojo:sky_viewer=/path/to/sky/viewer "mojo:window_manager APP_URL" --sky
```
Note that Sky apps will need the --use-osmesa flag to run
over [chromoting](https://support.google.com/chrome/answer/1649523?hl=en):
### Debugger
`mojo_debug` allows you to interactively inspect a running shell, collect
performance traces and attach a gdb debugger.
#### Tracing
To collect [performance
traces](https://www.chromium.org/developers/how-tos/trace-event-profiling-tool)
and retrieve the result:
```sh
mojo_debug tracing start
mojo_debug tracing stop [result.json]
```
The trace file can be then loaded using the trace viewer in Chrome available at
`about://tracing`.
#### GDB
It is possible to inspect a Mojo Shell process using GDB. The `mojo_debug`
script can be used to launch GDB and attach it to a running shell process
(android only):
```sh
mojo_debug gdb attach
```
Once started, GDB will first stop the Mojo Shell execution, then load symbols
from loaded Mojo applications. Please note that this initial step can take some
time (up to several minutes in the worst case).
After each execution pause, GDB will update the set of loaded symbols based on
the selected thread only. If you need symbols for all threads, use the
`update-symbols` GDB command:
```sh
(gdb) update-symbols
```
If you only want to update symbols for the current selected thread (for example,
after changing threads), use the `current` option:
```sh
(gdb) update-symbols current
```
If you want to debug the startup of your application, you can pass
`--wait-for-debugger` to `mojo_run` to have the Mojo Shell stop and wait to be
attached by `gdb` before continuing.
#### Android crash stacks
When Mojo shell crashes on Android ("Unfortunately, Mojo shell has stopped.")
due to a crash in native code, `mojo_debug` can be used to find and symbolize
the stack trace present in the device log:
```sh
mojo_debug device stack
```
## Development
The library is canonically developed [in the mojo
repository](https://github.com/domokit/mojo/tree/master/mojo/devtools/common),
https://github.com/domokit/devtools is a mirror allowing to consume it
separately.
# Copyright 2015 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 android_gdb.config as config
import subprocess
import tempfile
def install(gsutil, adb='adb'):
verification_call_output = subprocess.check_output(
[adb, 'shell', 'ls', config.REMOTE_FILE_READER_DEVICE_PATH])
if config.REMOTE_FILE_READER_DEVICE_PATH != verification_call_output.strip():
with tempfile.NamedTemporaryFile() as temp_file:
subprocess.check_call([gsutil, 'cp', config.REMOTE_FILE_READER_CLOUD_PATH,
temp_file.name])
subprocess.check_call([adb, 'push', temp_file.name,
config.REMOTE_FILE_READER_DEVICE_PATH])
subprocess.check_call([adb, 'shell', 'chmod', '777',
config.REMOTE_FILE_READER_DEVICE_PATH])
# Copyright 2015 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 socket
import struct
class RemoteFileConnectionException(Exception):
def __init__(self, *args, **kwargs):
Exception.__init__(self, *args, **kwargs)
class RemoteFileConnection(object):
"""Client for remote_file_reader server, allowing to read files on an
remote device.
"""
def __init__(self, host, port):
self._host = host
self._port = port
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._size_struct = struct.Struct("!i")
def __del__(self):
self.disconnect()
def connect(self):
self._socket.connect((self._host, self._port))
def disconnect(self):
self._socket.close()
def open(self, filename):
self._send("O %s\n" % filename)
result = self._receive(1)
if result != 'O':
raise RemoteFileConnectionException("Unable to open file " + filename)
def seek(self, pos, mode=0):
self._send("S %d %d\n" % (pos, mode))
result = self._receive(1)
if result != 'O':
raise RemoteFileConnectionException("Unable to seek in file.")
def read(self, size=0):
assert size > 0
self._send("R %d\n" % size)
result = self._receive(1)
if result != 'O':
raise RemoteFileConnectionException("Unable to read file.")
read_size = self._size_struct.unpack(self._receive(4))[0]
return self._receive(read_size)
def _send(self, data):
while len(data) > 0:
sent = self._socket.send(data)
if sent == 0:
raise RemoteFileConnectionException("Socket connection broken.")
data = data[sent:]
def _receive(self, length):
result = []
while length > 0:
chunk = self._socket.recv(length)
if chunk == '':
raise RemoteFileConnectionException("Socket connection broken.")
result.append(chunk)
length -= len(chunk)
return ''.join(result)
# Copyright 2015 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.
"""
Manages a debugging session with GDB.
This module is meant to be imported from inside GDB. Once loaded, the
|DebugSession| attaches GDB to a running Mojo Shell process on an Android
device using a remote gdbserver.
At startup and each time the execution stops, |DebugSession| associates
debugging symbols for every frame. For more information, see |DebugSession|
documentation.
"""
import gdb
import glob
import itertools
import logging
import os
import os.path
import shutil
import subprocess
import sys
import tempfile
import traceback
import urllib2
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import android_gdb.config as config
from android_gdb.remote_file_connection import RemoteFileConnection
from android_gdb.signatures import get_signature
logging.getLogger().setLevel(logging.INFO)
def _gdb_execute(command):
"""Executes a GDB command."""
return gdb.execute(command, to_string=True)
class Mapping(object):
"""Represents a mapped memory region."""
def __init__(self, line):
self.start = int(line[0], 16)
self.end = int(line[1], 16)
self.size = int(line[2], 16)
self.offset = int(line[3], 16)
self.filename = line[4]
def _get_mapped_files():
"""Retrieves all the files mapped into the debugged process memory.
Returns:
List of mapped memory regions grouped by files.
"""
# info proc map returns a space-separated table with the following fields:
# start address, end address, size, offset, file path.
mappings = [Mapping(x) for x in
[x.split() for x in
_gdb_execute("info proc map").split('\n')]
if len(x) == 5 and x[4][0] == '/']
res = {}
for m in mappings:
libname = m.filename[m.filename.rfind('/') + 1:]
res[libname] = res.get(libname, []) + [m]
return res.values()
class DebugSession(object):
def __init__(self, build_directory, package_name, pyelftools_dir, adb):
self._build_directory = build_directory
if not os.path.exists(self._build_directory):
logging.fatal("Please pass a valid build directory")
sys.exit(1)
self._package_name = package_name
self._adb = adb
self._remote_file_cache = os.path.join(os.getenv('HOME'), '.mojosymbols')
if pyelftools_dir != None:
sys.path.append(pyelftools_dir)
try:
import elftools.elf.elffile as elffile
except ImportError:
logging.fatal("Unable to find elftools module; please install pyelftools "
"and specify its path on the command line using "
"--pyelftools-dir.")
sys.exit(1)
self._elffile_module = elffile
self._libraries = self._find_libraries(build_directory)
self._rfc = RemoteFileConnection('localhost', 10000)
self._remote_file_reader_process = None
if not os.path.exists(self._remote_file_cache):
os.makedirs(self._remote_file_cache)
self._done_mapping = set()
self._downloaded_files = []
def __del__(self):
# Note that, per python interpreter documentation, __del__ is not
# guaranteed to be called when the interpreter (GDB, in our case) quits.
# Also, most (all?) globals are no longer available at this time (launching
# a subprocess does not work).
self.stop()
def stop(self, _unused_return_value=None):
if self._remote_file_reader_process != None:
self._remote_file_reader_process.kill()
def _find_libraries(self, lib_dir):
"""Finds all libraries in |lib_dir| and key them by their signatures.
"""
res = {}
for fn in glob.glob('%s/*.so' % lib_dir):
with open(fn, 'r') as f:
s = get_signature(f, self._elffile_module)
if s is not None:
res[s] = fn
return res
def _associate_symbols(self, mapping, local_file):
with open(local_file, "r") as f:
elf = self._elffile_module.ELFFile(f)
s = elf.get_section_by_name(".text")
text_address = mapping[0].start + s['sh_offset']
_gdb_execute("add-symbol-file %s 0x%x" % (local_file, text_address))
def _download_file(self, signature, remote):
"""Downloads a remote file either from the cloud or through GDB connection.
Returns:
The filename of the downloaded file
"""
temp_file = tempfile.NamedTemporaryFile()
logging.info("Trying to download symbols from the cloud.")
symbols_url = "http://storage.googleapis.com/mojo/symbols/%s" % signature
try:
symbol_file = urllib2.urlopen(symbols_url)
try:
with open(temp_file.name, "w") as dst:
shutil.copyfileobj(symbol_file, dst)
logging.info("Getting symbols for %s at %s." % (remote, symbols_url))
# This allows the deletion of temporary files on disk when the
# debugging session terminates.
self._downloaded_files.append(temp_file)
return temp_file.name
finally:
symbol_file.close()
except urllib2.HTTPError:
pass
logging.info("Downloading file %s" % remote)
_gdb_execute("remote get %s %s" % (remote, temp_file.name))
# This allows the deletion of temporary files on disk when the debugging
# session terminates.
self._downloaded_files.append(temp_file)
return temp_file.name
def _find_mapping_for_address(self, mappings, address):
"""Returns the list of all mappings of the file occupying the |address|
memory address.
"""
for file_mappings in mappings:
for mapping in file_mappings:
if address >= mapping.start and address <= mapping.end:
return file_mappings
return None
def _try_to_map(self, mapping):
remote_file = mapping[0].filename
if remote_file in self._done_mapping:
return False
self._done_mapping.add(remote_file)
self._rfc.open(remote_file)
signature = get_signature(self._rfc, self._elffile_module)
if signature is not None:
if signature in self._libraries:
self._associate_symbols(mapping, self._libraries[signature])
else:
# This library file is not known locally. Download it from the device or
# the cloud and put it in cache so, if it got symbols, we can see them.
local_file = os.path.join(self._remote_file_cache, signature)
if not os.path.exists(local_file):
tmp_output = self._download_file(signature, remote_file)
shutil.move(tmp_output, local_file)
self._associate_symbols(mapping, local_file)
return True
return False
def _map_symbols_on_current_thread(self, mapped_files):
"""Updates the symbols for the current thread using files from mapped_files.
"""
frame = gdb.newest_frame()
while frame and frame.is_valid():
if frame.name() is None:
m = self._find_mapping_for_address(mapped_files, frame.pc())
if m is not None and self._try_to_map(m):
# Force gdb to recompute its frames.
_gdb_execute("info threads")
frame = gdb.newest_frame()
assert frame.is_valid()
if (frame.older() is not None and
frame.older().is_valid() and
frame.older().pc() != frame.pc()):
frame = frame.older()
else:
frame = None
def update_symbols(self, current_thread_only):
"""Updates the mapping between symbols as seen from GDB and local library
files.
If current_thread_only is True, only update symbols for the current thread.
"""
logging.info("Updating symbols")
mapped_files = _get_mapped_files()
# Map all symbols from native libraries packages with the APK.
for file_mappings in mapped_files:
filename = file_mappings[0].filename
if ((filename.startswith('/data/data/') or
filename.startswith('/data/app')) and
not filename.endswith('.apk') and
not filename.endswith('.dex')):
logging.info('Pre-mapping: %s' % file_mappings[0].filename)
self._try_to_map(file_mappings)
if current_thread_only:
self._map_symbols_on_current_thread(mapped_files)
else:
logging.info('Updating all threads\' symbols')
current_thread = gdb.selected_thread()
nb_threads = len(_gdb_execute("info threads").split("\n")) - 2
for i in xrange(nb_threads):
try:
_gdb_execute("thread %d" % (i + 1))
self._map_symbols_on_current_thread(mapped_files)
except gdb.error:
traceback.print_exc()
current_thread.switch()
def _get_device_application_pid(self, application):
"""Gets the PID of an application running on a device."""
output = subprocess.check_output([self._adb, 'shell', 'ps'])
for line in output.split('\n'):
elements = line.split()
if len(elements) > 0 and elements[-1] == application:
return elements[1]
return None
def start(self):
"""Starts a debugging session."""
gdbserver_pid = self._get_device_application_pid('gdbserver')
if gdbserver_pid is not None:
subprocess.check_call([self._adb, 'shell', 'kill', gdbserver_pid])
shell_pid = self._get_device_application_pid(self._package_name)
if shell_pid is None:
raise Exception('Unable to find a running mojo shell.')
subprocess.check_call([self._adb, 'forward', 'tcp:9999', 'tcp:9999'])
subprocess.Popen(
[self._adb, 'shell', 'gdbserver', '--attach', ':9999', shell_pid],
# os.setpgrp ensures signals passed to this file (such as SIGINT) are
# not propagated to child processes.
preexec_fn = os.setpgrp)
# Kill stray remote reader processes. See __del__ comment for more info.
remote_file_reader_pid = self._get_device_application_pid(
config.REMOTE_FILE_READER_DEVICE_PATH)
if remote_file_reader_pid is not None:
subprocess.check_call([self._adb, 'shell', 'kill',
remote_file_reader_pid])
self._remote_file_reader_process = subprocess.Popen(
[self._adb, 'shell', config.REMOTE_FILE_READER_DEVICE_PATH],
stdout=subprocess.PIPE, preexec_fn = os.setpgrp)
port = int(self._remote_file_reader_process.stdout.readline())
subprocess.check_call([self._adb, 'forward', 'tcp:10000', 'tcp:%d' % port])
self._rfc.connect()
_gdb_execute('target remote localhost:9999')
self.update_symbols(current_thread_only=False)
def on_stop(_):
self.update_symbols(current_thread_only=True)
gdb.events.stop.connect(on_stop)
gdb.events.exited.connect(self.stop)
# Register the update-symbols command.
UpdateSymbols(self)
class UpdateSymbols(gdb.Command):
"""Command to update symbols loaded into GDB.
GDB usage: update-symbols [all|current]
"""
_UPDATE_COMMAND = "update-symbols"
def __init__(self, session):
super(UpdateSymbols, self).__init__(self._UPDATE_COMMAND, gdb.COMMAND_STACK)
self._session = session
def invoke(self, arg, _unused_from_tty):
if arg == 'current':
self._session.update_symbols(current_thread_only=True)
else:
self._session.update_symbols(current_thread_only=False)
def complete(self, text, _unused_word):
if text == self._UPDATE_COMMAND:
return ('all', 'current')
elif text in self._UPDATE_COMMAND + ' all':
return ['all']
elif text in self._UPDATE_COMMAND + ' current':
return ['current']
else:
return []
# Copyright 2015 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 itertools
def get_signature(file_object, elffile_module):
"""Computes a unique signature of a library file.
We only hash the .text section of the library in order to make the hash
resistant to stripping (we want the same hash for the same library with debug
symbols kept or stripped).
"""
try:
elf = elffile_module.ELFFile(file_object)
text_section = elf.get_section_by_name('.text')
except elffile_module.common.ELFError:
return None
file_object.seek(text_section['sh_offset'])
data = file_object.read(min(4096, text_section['sh_size']))
def combine((i, c)):
return i ^ ord(c)
result = 16 * [0]
for i in xrange(0, len(data), len(result)):
result = map(combine,
itertools.izip_longest(result,
data[i:i + len(result)],
fillvalue='\0'))
return ''.join(["%02x" % x for x in result])
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Name: Android Platform engineering tools
Short Name: android platform development
URL: https://android.googlesource.com/platform/development
Version: 0
Date: 2014/05/02
Revision: 1b10ec4
License: Apache 2.0
License File: NOT_SHIPPED
Security Critical: no
Description:
Android Platform engineering tools, specifically stack symbolization scripts
and a jar containing the AOSP framework to compile the Android WebView
glue layer against. The sources used to build the AOSP framework jar can be
checked out using repo on the corresponding manifest file. The AOSP framework
jar is built by invoking make on the android_system_stubs target.
Local Modifications:
Only picked the few scripts needed by chrome.
Updated output directories to use environment variable.
When calling addr2line, check the symbol is a file (and not a directory).
Added support for parsing LOG(FATAL) and DCHECK errors and their
stack traces, as emitted by src/base/debug/stack_trace_android.cc
Added support for finding symbols when library is loaded directly from the APK.
Changed the toolchain to remove references to 4.6 toolchains.
Added support for arch=x64 as an alias to arch=x86_64
Added support for mojo. Specifically detection of mojo apps downloaded
to temporary files which are in crash stacks.
Taught `stack` to find the output directory and android_tools regardless of
where under the checkout root it is placed.
#!/usr/bin/env python
#
# Copyright (C) 2013 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""stack symbolizes native crash dumps."""
import getopt
import glob
import os
import re
import sys
import stack_core
import subprocess
import symbol
_DEFAULT_SYMROOT = '/tmp/symbols'
_DEFAULT_BUILD_DIR = 'out/android_Debug'
_DEFAULT_NDK_DIR = 'third_party/android_tools/ndk'
def PrintUsage():
"""Print usage and exit with error."""
# pylint: disable-msg=C6310
print
print " usage: " + sys.argv[0] + " [options] [FILE]"
print
print " --symbols-dir=path"
print " the path to a symbols dir, this is generally for system level"
print " symbols"
print
print " --build-dir=path"
print " the path to a directory containing Mojo symbols (can be"
print " absolute or relative to src), such as =out/android_Debug"
print " --ndk-dir=path"
print " the path to a directory containing Android NDK"
print
print " --symbols-zip=path"
print " the path to a symbols zip file, such as =dream-symbols-12345.zip"
print
print " --more-info"
print " --less-info"
print " Change the level of detail in the output."
print " --more-info is slower and more verbose, but more functions will"
print " be fully qualified with namespace/classname and have full"
print " argument information. Also, the 'stack data' section will be"
print " printed."
print
print " --arch=arm|arm64|x64|x86|mips"
print " the target architecture"
print
print " FILE should contain a stack trace in it somewhere"
print " the tool will find that and re-print it with"
print " source files and line numbers. If you don't"
print " pass FILE, or if file is -, it reads from"
print " stdin."
print
# pylint: enable-msg=C6310
sys.exit(1)
def UnzipSymbols(symbolfile, symdir=None):
"""Unzips a file to _DEFAULT_SYMROOT and returns the unzipped location.
Args:
symbolfile: The .zip file to unzip
symdir: Optional temporary directory to use for extraction
Returns:
A tuple containing (the directory into which the zip file was unzipped,
the path to the "symbols" directory in the unzipped file). To clean
up, the caller can delete the first element of the tuple.
Raises:
SymbolDownloadException: When the unzip fails.
"""
if not symdir:
symdir = "%s/%s" % (_DEFAULT_SYMROOT, hash(symbolfile))
if not os.path.exists(symdir):
os.makedirs(symdir)
print "extracting %s..." % symbolfile
saveddir = os.getcwd()
os.chdir(symdir)
try:
unzipcode = subprocess.call(["unzip", "-qq", "-o", symbolfile])
if unzipcode > 0:
os.remove(symbolfile)
raise SymbolDownloadException("failed to extract symbol files (%s)."
% symbolfile)
finally:
os.chdir(saveddir)
android_symbols = glob.glob("%s/out/target/product/*/symbols" % symdir)
if android_symbols:
return (symdir, android_symbols[0])
else:
# This is a zip of Chrome symbols, so symbol.CHROME_SYMBOLS_DIR needs to be
# updated to point here.
symbol.CHROME_SYMBOLS_DIR = symdir
return (symdir, symdir)
def GetBasenameFromMojoApp(url):
"""Used by GetSymbolMapping() to extract the basename from the location the
mojo app was downloaded from. The location is a URL, e.g.
http://foo/bar/x.so."""
index = url.rfind('/')
return url[(index + 1):] if index != -1 else url
def GetSymboledNameForMojoApp(path):
"""Used by GetSymbolMapping to get the non-stripped library name for an
installed mojo app."""
# e.g. tracing.mojo -> libtracing_library.so
name, ext = os.path.splitext(path)
if ext != '.mojo':
return path
return 'lib%s_library.so' % name
def GetSymbolMapping(lines):
"""Returns a mapping (dictionary) from download file to .so."""
regex = re.compile('Caching mojo app (\S+) at (\S+)')
mappings = {}
for line in lines:
result = regex.search(line)
if result:
url = GetBasenameFromMojoApp(result.group(1))
mappings[result.group(2)] = GetSymboledNameForMojoApp(url)
return mappings
def _LowestAncestorContainingRelpath(dir_path, relpath):
"""Returns the lowest ancestor dir of |dir_path| that contains |relpath|.
"""
cur_dir_path = os.path.abspath(dir_path)
while True:
if os.path.exists(os.path.join(cur_dir_path, relpath)):
return cur_dir_path
next_dir_path = os.path.dirname(cur_dir_path)
if next_dir_path != cur_dir_path:
cur_dir_path = next_dir_path
else:
return None
def _GuessDir(relpath):
"""Returns absolute path to location |relpath| in the lowest ancestor of this
file that contains it."""
lowest_ancestor = _LowestAncestorContainingRelpath(
os.path.dirname(__file__), relpath)
if not lowest_ancestor:
return None
return os.path.join(lowest_ancestor, relpath)
def main():
try:
options, arguments = getopt.getopt(sys.argv[1:], "",
["more-info",
"less-info",
"build-dir=",
"ndk-dir=",
"symbols-dir=",
"symbols-zip=",
"arch=",
"help"])
except getopt.GetoptError, unused_error:
PrintUsage()
zip_arg = None
more_info = False
for option, value in options:
if option == "--help":
PrintUsage()
elif option == "--symbols-dir":
symbol.SYMBOLS_DIR = os.path.expanduser(value)
elif option == "--symbols-zip":
zip_arg = os.path.expanduser(value)
elif option == "--arch":
symbol.ARCH = value
elif option == "--build-dir":
symbol.BUILD_DIR = value
elif option == "--ndk-dir":
symbol.NDK_DIR = value
elif option == "--more-info":
more_info = True
elif option == "--less-info":
more_info = False
if not symbol.BUILD_DIR:
guess = _GuessDir(_DEFAULT_BUILD_DIR)
if not guess:
print "Couldn't find the build directory, please pass --build-dir."
return 1
print "Inferring the build directory path as " + guess
symbol.BUILD_DIR = guess
if not symbol.NDK_DIR:
guess = _GuessDir(_DEFAULT_NDK_DIR)
if not guess:
print "Couldn't find the Android NDK, please pass --ndk-dir."
return 1
print "Inferring the Android NDK path as " + guess
symbol.NDK_DIR = guess
if len(arguments) > 1:
PrintUsage()
if not arguments or arguments[0] == "-":
print "Reading native crash info from stdin"
f = sys.stdin
else:
print "Searching for native crashes in %s" % arguments[0]
f = open(arguments[0], "r")
lines = f.readlines()
f.close()
rootdir = None
if zip_arg:
rootdir, symbol.SYMBOLS_DIR = UnzipSymbols(zip_arg)
if symbol.SYMBOLS_DIR:
print "Reading Android symbols from", symbol.SYMBOLS_DIR
print "Reading Mojo symbols from", symbol.BUILD_DIR
stack_core.ConvertTrace(lines, more_info, GetSymbolMapping(lines))
if rootdir:
# be a good citizen and clean up...os.rmdir and os.removedirs() don't work
cmd = "rm -rf \"%s\"" % rootdir
print "\ncleaning up (%s)" % cmd
os.system(cmd)
if __name__ == "__main__":
main()
# vi: ts=2 sw=2
# Copyright (C) 2013 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""stack symbolizes native crash dumps."""
import re
import symbol
def PrintTraceLines(trace_lines):
"""Print back trace."""
maxlen = max(map(lambda tl: len(tl[1]), trace_lines))
print
print "Stack Trace:"
print " RELADDR " + "FUNCTION".ljust(maxlen) + " FILE:LINE"
for tl in trace_lines:
(addr, symbol_with_offset, location) = tl
print " %8s %s %s" % (addr, symbol_with_offset.ljust(maxlen), location)
return
def PrintValueLines(value_lines):
"""Print stack data values."""
maxlen = max(map(lambda tl: len(tl[2]), value_lines))
print
print "Stack Data:"
print " ADDR VALUE " + "FUNCTION".ljust(maxlen) + " FILE:LINE"
for vl in value_lines:
(addr, value, symbol_with_offset, location) = vl
print " %8s %8s %s %s" % (addr, value, symbol_with_offset.ljust(maxlen), location)
return
UNKNOWN = "<unknown>"
HEAP = "[heap]"
STACK = "[stack]"
def PrintOutput(trace_lines, value_lines, more_info):
if trace_lines:
PrintTraceLines(trace_lines)
if value_lines:
# TODO(cjhopman): it seems that symbol.SymbolInformation always fails to
# find information for addresses in value_lines in chrome libraries, and so
# value_lines have little value to us and merely clutter the output.
# Since information is sometimes contained in these lines (from system
# libraries), don't completely disable them.
if more_info:
PrintValueLines(value_lines)
def PrintDivider():
print
print "-----------------------------------------------------\n"
def ConvertTrace(lines, more_info, symbol_file_mappings):
"""Convert strings containing native crash to a stack.
Args:
symbol_file_mappings: maps from library pathname to base name of file
containing symbols. If |symbol_file_mappings| doesn't contain a path than
the library pathname is used. See TranslatePathFromDeviceToLocal() for
more information.
"""
process_info_line = re.compile("(pid: [0-9]+, tid: [0-9]+.*)")
signal_line = re.compile("(signal [0-9]+ \(.*\).*)")
register_line = re.compile("(([ ]*[0-9a-z]{2} [0-9a-f]{8}){4})")
thread_line = re.compile("(.*)(\-\-\- ){15}\-\-\-")
dalvik_jni_thread_line = re.compile("(\".*\" prio=[0-9]+ tid=[0-9]+ NATIVE.*)")
dalvik_native_thread_line = re.compile("(\".*\" sysTid=[0-9]+ nice=[0-9]+.*)")
width = "{8}"
if symbol.ARCH == "arm64" or symbol.ARCH == "x86_64" or symbol.ARCH == "x64":
width = "{16}"
# Matches LOG(FATAL) lines, like the following example:
# [FATAL:source_file.cc(33)] Check failed: !instances_.empty()
log_fatal_line = re.compile("(\[FATAL\:.*\].*)$")
# Note that both trace and value line matching allow for variable amounts of
# whitespace (e.g. \t). This is because the we want to allow for the stack
# tool to operate on AndroidFeedback provided system logs. AndroidFeedback
# strips out double spaces that are found in tombsone files and logcat output.
#
# Examples of matched trace lines include lines from tombstone files like:
# #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so
# #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so (symbol)
# Or lines from AndroidFeedback crash report system logs like:
# 03-25 00:51:05.520 I/DEBUG ( 65): #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so
# Please note the spacing differences.
trace_line = re.compile("(.*)\#(?P<frame>[0-9]+)[ \t]+(..)[ \t]+(0x)?(?P<address>[0-9a-f]{0,16})[ \t]+(?P<lib>[^\r\n \t]*)(?P<symbol_present> \((?P<symbol_name>.*)\))?") # pylint: disable-msg=C6310
# Matches lines emitted by src/base/debug/stack_trace_android.cc, like:
# #00 0x7324d92d /data/app-lib/org.chromium.native_test-1/libbase.cr.so+0x0006992d
# This pattern includes the unused named capture groups <symbol_present> and
# <symbol_name> so that it can interoperate with the |trace_line| regex.
debug_trace_line = re.compile(
'(.*)(?P<frame>\#[0-9]+ 0x[0-9a-f]' + width + ') '
'(?P<lib>[^+]+)( \(deleted\))+\+0x(?P<address>[0-9a-f]' + width + ')'
'(?P<symbol_present>)(?P<symbol_name>)')
# Examples of matched value lines include:
# bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so
# bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so (symbol)
# 03-25 00:51:05.530 I/DEBUG ( 65): bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so
# Again, note the spacing differences.
value_line = re.compile("(.*)([0-9a-f]" + width + ")[ \t]+([0-9a-f]" + width + ")[ \t]+([^\r\n \t]*)( \((.*)\))?")
# Lines from 'code around' sections of the output will be matched before
# value lines because otheriwse the 'code around' sections will be confused as
# value lines.
#
# Examples include:
# 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
# 03-25 00:51:05.530 I/DEBUG ( 65): 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
code_line = re.compile("(.*)[ \t]*[a-f0-9]" + width +
"[ \t]*[a-f0-9]" + width +
"[ \t]*[a-f0-9]" + width +
"[ \t]*[a-f0-9]" + width +
"[ \t]*[a-f0-9]" + width +
"[ \t]*[ \r\n]") # pylint: disable-msg=C6310
trace_lines = []
value_lines = []
last_frame = -1
# It is faster to get symbol information with a single call rather than with
# separate calls for each line. Since symbol.SymbolInformation caches results,
# we can extract all the addresses that we will want symbol information for
# from the log and call symbol.SymbolInformation so that the results are
# cached in the following lookups.
code_addresses = {}
for ln in lines:
line = unicode(ln, errors='ignore')
lib, address = None, None
match = trace_line.match(line) or debug_trace_line.match(line)
if match:
address, lib = match.group('address', 'lib')
match = value_line.match(line)
if match and not code_line.match(line):
(_0, _1, address, lib, _2, _3) = match.groups()
if lib:
code_addresses.setdefault(lib, set()).add(address)
for lib in code_addresses:
symbol.SymbolInformationForSet(
symbol.TranslatePathFromDeviceToLocal(
symbol_file_mappings.get(lib, lib)),
code_addresses[lib], more_info)
for ln in lines:
# AndroidFeedback adds zero width spaces into its crash reports. These
# should be removed or the regular expresssions will fail to match.
line = unicode(ln, errors='ignore')
process_header = process_info_line.search(line)
signal_header = signal_line.search(line)
register_header = register_line.search(line)
thread_header = thread_line.search(line)
dalvik_jni_thread_header = dalvik_jni_thread_line.search(line)
dalvik_native_thread_header = dalvik_native_thread_line.search(line)
log_fatal_header = log_fatal_line.search(line)
if (process_header or signal_header or register_header or thread_header or
dalvik_jni_thread_header or dalvik_native_thread_header or
log_fatal_header) :
if trace_lines or value_lines:
PrintOutput(trace_lines, value_lines, more_info)
PrintDivider()
trace_lines = []
value_lines = []
last_frame = -1
if process_header:
print process_header.group(1)
if signal_header:
print signal_header.group(1)
if register_header:
print register_header.group(1)
if thread_header:
print thread_header.group(1)
if dalvik_jni_thread_header:
print dalvik_jni_thread_header.group(1)
if dalvik_native_thread_header:
print dalvik_native_thread_header.group(1)
if log_fatal_header:
print log_fatal_header.group(1)
continue
match = trace_line.match(line) or debug_trace_line.match(line)
if match:
frame, code_addr, area, symbol_present, symbol_name = match.group(
'frame', 'address', 'lib', 'symbol_present', 'symbol_name')
if frame <= last_frame and (trace_lines or value_lines):
PrintOutput(trace_lines, value_lines, more_info)
PrintDivider()
trace_lines = []
value_lines = []
last_frame = frame
if area == UNKNOWN or area == HEAP or area == STACK:
trace_lines.append((code_addr, "", area))
else:
# If a calls b which further calls c and c is inlined to b, we want to
# display "a -> b -> c" in the stack trace instead of just "a -> c"
info = symbol.SymbolInformation(symbol_file_mappings.get(area, area),
code_addr, more_info)
nest_count = len(info) - 1
for (source_symbol, source_location, object_symbol_with_offset) in info:
if not source_symbol:
if symbol_present:
source_symbol = symbol.CallCppFilt(symbol_name)
else:
source_symbol = UNKNOWN
if not source_location:
source_location = area
if nest_count > 0:
nest_count = nest_count - 1
trace_lines.append(("v------>", source_symbol, source_location))
else:
if not object_symbol_with_offset:
object_symbol_with_offset = source_symbol
trace_lines.append((code_addr,
object_symbol_with_offset,
source_location))
if code_line.match(line):
# Code lines should be ignored. If this were exluded the 'code around'
# sections would trigger value_line matches.
continue;
match = value_line.match(line)
if match:
(unused_, addr, value, area, symbol_present, symbol_name) = match.groups()
if area == UNKNOWN or area == HEAP or area == STACK or not area:
value_lines.append((addr, value, "", area))
else:
info = symbol.SymbolInformation(symbol_file_mappings.get(area, area),
value, more_info)
(source_symbol, source_location, object_symbol_with_offset) = info.pop()
if not source_symbol:
if symbol_present:
source_symbol = symbol.CallCppFilt(symbol_name)
else:
source_symbol = UNKNOWN
if not source_location:
source_location = area
if not object_symbol_with_offset:
object_symbol_with_offset = source_symbol
value_lines.append((addr,
value,
object_symbol_with_offset,
source_location))
PrintOutput(trace_lines, value_lines, more_info)
# Copyright (C) 2013 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Module for looking up symbolic debugging information.
The information can include symbol names, offsets, and source locations.
"""
import glob
import itertools
import os
import re
import subprocess
import zipfile
NDK_DIR = ""
BUILD_DIR = ""
SYMBOLS_DIR = ""
ARCH = "arm"
TOOLCHAIN_INFO = None
def Uname():
"""'uname' for constructing prebuilt/<...> and out/host/<...> paths."""
uname = os.uname()[0]
proc = os.uname()[-1]
if uname == "Darwin":
if proc == "i386":
return "darwin-x86"
elif proc == "x86_64":
return "darwin-x86_64"
return "darwin-ppc"
if uname == "Linux":
if proc == "i386":
return "linux-x86"
else:
return "linux-x86_64"
return uname
def ToolPath(tool, toolchain_info=None):
"""Return a full qualified path to the specified tool"""
# ToolPath looks for the tools in the completely incorrect directory.
# This looks in the checked in android_tools.
if ARCH == "arm":
toolchain_source = "arm-linux-androideabi-4.9"
toolchain_prefix = "arm-linux-androideabi"
elif ARCH == "arm64":
toolchain_source = "aarch64-linux-android-4.9"
toolchain_prefix = "aarch64-linux-android"
elif ARCH == "x86":
toolchain_source = "x86-4.9"
toolchain_prefix = "i686-linux-android"
elif ARCH == "x86_64" or ARCH == "x64":
toolchain_source = "x86_64-4.9"
toolchain_prefix = "x86_64-linux-android"
elif ARCH == "mips":
toolchain_source = "mipsel-linux-android-4.9"
toolchain_prefix = "mipsel-linux-android"
else:
raise Exception("Could not find tool chain")
toolchain_subdir = ("toolchains/%s/prebuilt/%s/bin" % (
toolchain_source, Uname()))
return os.path.join(NDK_DIR,
toolchain_subdir,
toolchain_prefix + "-" + tool)
def FindToolchain():
"""Look for the latest available toolchain
Args:
None
Returns:
A pair of strings containing toolchain label and target prefix.
"""
global TOOLCHAIN_INFO
if TOOLCHAIN_INFO is not None:
return TOOLCHAIN_INFO
## Known toolchains, newer ones in the front.
gcc_version = "4.9"
if ARCH == "arm64":
known_toolchains = [
("aarch64-linux-android-" + gcc_version, "aarch64", "aarch64-linux-android")
]
elif ARCH == "arm":
known_toolchains = [
("arm-linux-androideabi-" + gcc_version, "arm", "arm-linux-androideabi")
]
elif ARCH =="x86":
known_toolchains = [
("x86-" + gcc_version, "x86", "i686-linux-android")
]
elif ARCH =="x86_64" or ARCH =="x64":
known_toolchains = [
("x86_64-" + gcc_version, "x86_64", "x86_64-linux-android")
]
elif ARCH == "mips":
known_toolchains = [
("mipsel-linux-android-" + gcc_version, "mips", "mipsel-linux-android")
]
else:
known_toolchains = []
# Look for addr2line to check for valid toolchain path.
for (label, platform, target) in known_toolchains:
toolchain_info = (label, platform, target);
if os.path.exists(ToolPath("addr2line", toolchain_info)):
TOOLCHAIN_INFO = toolchain_info
print "Using toolchain from :" + ToolPath("", TOOLCHAIN_INFO)
return toolchain_info
raise Exception("Could not find tool chain")
def GetAapt():
"""Returns the path to aapt.
Args:
None
Returns:
the pathname of the 'aapt' executable.
"""
sdk_home = os.path.join('third_party', 'android_tools', 'sdk')
sdk_home = os.environ.get('SDK_HOME', sdk_home)
aapt_exe = glob.glob(os.path.join(sdk_home, 'build-tools', '*', 'aapt'))
if not aapt_exe:
return None
return sorted(aapt_exe, key=os.path.getmtime, reverse=True)[0]
def ApkMatchPackageName(aapt, apk_path, package_name):
"""Returns true the APK's package name matches package_name.
Args:
aapt: pathname for the 'aapt' executable.
apk_path: pathname of the APK file.
package_name: package name to match.
Returns:
True if the package name matches or aapt is None, False otherwise.
"""
if not aapt:
# Allow false positives
return True
aapt_output = subprocess.check_output(
[aapt, 'dump', 'badging', apk_path]).split('\n')
package_name_re = re.compile(r'package: .*name=\'(\S*)\'')
for line in aapt_output:
match = package_name_re.match(line)
if match:
return package_name == match.group(1)
return False
def PathListJoin(prefix_list, suffix_list):
"""Returns each prefix in prefix_list joined with each suffix in suffix list.
Args:
prefix_list: list of path prefixes.
suffix_list: list of path suffixes.
Returns:
List of paths each of which joins a prefix with a suffix.
"""
return [
os.path.join(prefix, suffix)
for prefix in prefix_list for suffix in suffix_list ]
def GetCandidates(filepart, candidate_fun, relative_dirs=None):
"""Returns a list of candidate filenames.
Args:
filepart: the file part of the pathname.
candidate_fun: a function to apply to each candidate, returns a list.
relative_dirs: a list of relative directory names to search from.
Returns:
A list of candidate files ordered by modification time, newest first.
"""
candidates = [BUILD_DIR]
if relative_dirs:
candidates = PathListJoin(candidates, relative_dirs)
candidates = PathListJoin(candidates, [filepart])
candidates = list(
itertools.chain.from_iterable(map(candidate_fun, candidates)))
candidates = sorted(candidates, key=os.path.getmtime, reverse=True)
return candidates
def GetCandidateApks():
"""Returns a list of APKs which could contain the library.
Args:
None
Returns:
list of APK filename which could contain the library.
"""
return GetCandidates('*.apk', glob.glob, relative_dirs=['apks'])
def GetCrazyLib(apk_filename):
"""Returns the name of the first crazy library from this APK.
Args:
apk_filename: name of an APK file.
Returns:
Name of the first library which would be crazy loaded from this APK.
"""
zip_file = zipfile.ZipFile(apk_filename, 'r')
for filename in zip_file.namelist():
match = re.match('lib/[^/]*/crazy.(lib.*[.]so)', filename)
if match:
return match.group(1)
def GetMatchingApks(device_apk_name):
"""Find any APKs which match the package indicated by the device_apk_name.
Args:
device_apk_name: name of the APK on the device.
Returns:
A list of APK filenames which could contain the desired library.
"""
match = re.match('(.*)-[0-9]+[.]apk$', device_apk_name)
if not match:
return None
package_name = match.group(1)
return filter(
lambda candidate_apk:
ApkMatchPackageName(GetAapt(), candidate_apk, package_name),
GetCandidateApks())
def MapDeviceApkToLibrary(device_apk_name):
"""Provide a library name which corresponds with device_apk_name.
Args:
device_apk_name: name of the APK on the device.
Returns:
Name of the library which corresponds to that APK.
"""
matching_apks = GetMatchingApks(device_apk_name)
for matching_apk in matching_apks:
crazy_lib = GetCrazyLib(matching_apk)
if crazy_lib:
return crazy_lib
def GetCandidateLibraries(library_name):
"""Returns a list of candidate library filenames.
Args:
library_name: basename of the library to match.
Returns:
A list of matching library filenames for library_name.
"""
return GetCandidates(
library_name,
lambda filename: filter(os.path.exists, [filename]))
def TranslatePathFromDeviceToLocal(lib):
"""Maps a path as seen on the device to a path on the local file system
containing symbols.
Args:
lib: library (or executable) pathname from device.
"""
# SymbolInformation(lib, addr) receives lib that is either a basename or
# the path from symbols root to the symbols file. This needs to be translated
# to point to the correct .so path. If the user doesn't explicitly specify
# which directory to use, then use the most recently updated one in one of
# the known directories.
library_name = os.path.basename(lib)
# The filename in the stack trace maybe an APK name rather than a library
# name. This happens when the library was loaded directly from inside the
# APK. If this is the case we try to figure out the library name by looking
# for a matching APK file and finding the name of the library in contains.
# The name of the APK file on the device is of the form
# <package_name>-<number>.apk. The APK file on the host may have any name
# so we look at the APK badging to see if the package name matches.
if re.search('-[0-9]+[.]apk$', library_name):
mapping = MapDeviceApkToLibrary(library_name)
if mapping:
library_name = mapping
candidate_libraries = GetCandidateLibraries(library_name)
return (candidate_libraries[0] if candidate_libraries else
os.path.join(SYMBOLS_DIR, lib))
def SymbolInformation(lib, addr, get_detailed_info):
"""Look up symbol information about an address.
Args:
lib: library (or executable) pathname containing symbols
addr: string hexidecimal address
Returns:
A list of the form [(source_symbol, source_location,
object_symbol_with_offset)].
If the function has been inlined then the list may contain
more than one element with the symbols for the most deeply
nested inlined location appearing first. The list is
always non-empty, even if no information is available.
Usually you want to display the source_location and
object_symbol_with_offset from the last element in the list.
"""
lib = TranslatePathFromDeviceToLocal(lib)
info = SymbolInformationForSet(lib, set([addr]), get_detailed_info)
return (info and info.get(addr)) or [(None, None, None)]
def SymbolInformationForSet(lib, unique_addrs, get_detailed_info):
"""Look up symbol information for a set of addresses from the given library.
Args:
lib: library (or executable) pathname containing symbols
unique_addrs: set of hexidecimal addresses
Returns:
A dictionary of the form {addr: [(source_symbol, source_location,
object_symbol_with_offset)]} where each address has a list of
associated symbols and locations. The list is always non-empty.
If the function has been inlined then the list may contain
more than one element with the symbols for the most deeply
nested inlined location appearing first. The list is
always non-empty, even if no information is available.
Usually you want to display the source_location and
object_symbol_with_offset from the last element in the list.
"""
if not lib:
return None
addr_to_line = CallAddr2LineForSet(lib, unique_addrs)
if not addr_to_line:
return None
if get_detailed_info:
addr_to_objdump = CallObjdumpForSet(lib, unique_addrs)
if not addr_to_objdump:
return None
else:
addr_to_objdump = dict((addr, ("", 0)) for addr in unique_addrs)
result = {}
for addr in unique_addrs:
source_info = addr_to_line.get(addr)
if not source_info:
source_info = [(None, None)]
if addr in addr_to_objdump:
(object_symbol, object_offset) = addr_to_objdump.get(addr)
object_symbol_with_offset = FormatSymbolWithOffset(object_symbol,
object_offset)
else:
object_symbol_with_offset = None
result[addr] = [(source_symbol, source_location, object_symbol_with_offset)
for (source_symbol, source_location) in source_info]
return result
class MemoizedForSet(object):
def __init__(self, fn):
self.fn = fn
self.cache = {}
def __call__(self, lib, unique_addrs):
lib_cache = self.cache.setdefault(lib, {})
no_cache = filter(lambda x: x not in lib_cache, unique_addrs)
if no_cache:
lib_cache.update((k, None) for k in no_cache)
result = self.fn(lib, no_cache)
if result:
lib_cache.update(result)
return dict((k, lib_cache[k]) for k in unique_addrs if lib_cache[k])
@MemoizedForSet
def CallAddr2LineForSet(lib, unique_addrs):
"""Look up line and symbol information for a set of addresses.
Args:
lib: library (or executable) pathname containing symbols
unique_addrs: set of string hexidecimal addresses look up.
Returns:
A dictionary of the form {addr: [(symbol, file:line)]} where
each address has a list of associated symbols and locations
or an empty list if no symbol information was found.
If the function has been inlined then the list may contain
more than one element with the symbols for the most deeply
nested inlined location appearing first.
"""
if not lib or not os.path.isfile(lib):
return None
(label, platform, target) = FindToolchain()
cmd = [ToolPath("addr2line"), "--functions", "--inlines",
"--demangle", "--exe=" + lib]
child = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
result = {}
addrs = sorted(unique_addrs)
for addr in addrs:
child.stdin.write("0x%s\n" % addr)
child.stdin.flush()
records = []
first = True
while True:
symbol = child.stdout.readline().strip()
if symbol == "??":
symbol = None
location = child.stdout.readline().strip()
if location == "??:0":
location = None
if symbol is None and location is None:
break
records.append((symbol, location))
if first:
# Write a blank line as a sentinel so we know when to stop
# reading inlines from the output.
# The blank line will cause addr2line to emit "??\n??:0\n".
child.stdin.write("\n")
first = False
result[addr] = records
child.stdin.close()
child.stdout.close()
return result
def StripPC(addr):
"""Strips the Thumb bit a program counter address when appropriate.
Args:
addr: the program counter address
Returns:
The stripped program counter address.
"""
global ARCH
if ARCH == "arm":
return addr & ~1
return addr
@MemoizedForSet
def CallObjdumpForSet(lib, unique_addrs):
"""Use objdump to find out the names of the containing functions.
Args:
lib: library (or executable) pathname containing symbols
unique_addrs: set of string hexidecimal addresses to find the functions for.
Returns:
A dictionary of the form {addr: (string symbol, offset)}.
"""
if not lib:
return None
symbols = SYMBOLS_DIR + lib
if not os.path.exists(symbols):
return None
symbols = SYMBOLS_DIR + lib
if not os.path.exists(symbols):
return None
result = {}
# Function lines look like:
# 000177b0 <android::IBinder::~IBinder()+0x2c>:
# We pull out the address and function first. Then we check for an optional
# offset. This is tricky due to functions that look like "operator+(..)+0x2c"
func_regexp = re.compile("(^[a-f0-9]*) \<(.*)\>:$")
offset_regexp = re.compile("(.*)\+0x([a-f0-9]*)")
# A disassembly line looks like:
# 177b2: b510 push {r4, lr}
asm_regexp = re.compile("(^[ a-f0-9]*):[ a-f0-0]*.*$")
for target_addr in unique_addrs:
start_addr_dec = str(StripPC(int(target_addr, 16)))
stop_addr_dec = str(StripPC(int(target_addr, 16)) + 8)
cmd = [ToolPath("objdump"),
"--section=.text",
"--demangle",
"--disassemble",
"--start-address=" + start_addr_dec,
"--stop-address=" + stop_addr_dec,
symbols]
current_symbol = None # The current function symbol in the disassembly.
current_symbol_addr = 0 # The address of the current function.
stream = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout
for line in stream:
# Is it a function line like:
# 000177b0 <android::IBinder::~IBinder()>:
components = func_regexp.match(line)
if components:
# This is a new function, so record the current function and its address.
current_symbol_addr = int(components.group(1), 16)
current_symbol = components.group(2)
# Does it have an optional offset like: "foo(..)+0x2c"?
components = offset_regexp.match(current_symbol)
if components:
current_symbol = components.group(1)
offset = components.group(2)
if offset:
current_symbol_addr -= int(offset, 16)
# Is it an disassembly line like:
# 177b2: b510 push {r4, lr}
components = asm_regexp.match(line)
if components:
addr = components.group(1)
i_addr = int(addr, 16)
i_target = StripPC(int(target_addr, 16))
if i_addr == i_target:
result[target_addr] = (current_symbol, i_target - current_symbol_addr)
stream.close()
return result
def CallCppFilt(mangled_symbol):
cmd = [ToolPath("c++filt")]
process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
process.stdin.write(mangled_symbol)
process.stdin.write("\n")
process.stdin.close()
demangled_symbol = process.stdout.readline().strip()
process.stdout.close()
return demangled_symbol
def FormatSymbolWithOffset(symbol, offset):
if offset == 0:
return symbol
return "%s+%d" % (symbol, offset)
# Copyright 2015 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.
"""Apptest is a Mojo application that interacts with another Mojo application
and verifies assumptions about behavior of the app being tested.
"""
import logging
import time
_logger = logging.getLogger()
def _build_shell_arguments(shell_args, apptest_url, apptest_args):
"""Builds the list of arguments for the shell.
Args:
shell_args: List of arguments for the shell run.
apptest_url: Url of the apptest app to run.
apptest_args: Parameters to be passed to the apptest app.
Returns:
Single list of shell arguments.
"""
result = list(shell_args)
if apptest_args:
result.append("--args-for=%s %s" % (apptest_url, " ".join(apptest_args)))
result.append(apptest_url)
return result
def run_apptest(shell, shell_args, apptest_url, apptest_args, timeout,
output_test):
"""Runs shell with the given arguments, retrieves the output and applies
|output_test| to determine if the run was successful.
Args:
shell: Wrapper around concrete Mojo shell, implementing devtools Shell
interface.
shell_args: List of arguments for the shell run.
apptest_url: Url of the apptest app to run.
apptest_args: Parameters to be passed to the apptest app.
output_test: Function accepting the shell output and returning True iff
the output indicates a successful run.
Returns:
True iff the test succeeded, False otherwise.
"""
arguments = _build_shell_arguments(shell_args, apptest_url, apptest_args)
command_line = "mojo_shell " + " ".join(["%r" % x for x in arguments])
_logger.debug("Starting: " + command_line)
start_time = time.time()
(exit_code, output, did_time_out) = shell.run_and_get_output(arguments,
timeout)
run_time = time.time() - start_time
_logger.debug("Completed: " + command_line)
# Only log if it took more than 3 second.
if run_time >= 3:
_logger.info("Test took %.3f seconds: %s" % (run_time, command_line))
if exit_code or did_time_out or not output_test(output):
print 'Failed test: %r' % command_line
if exit_code:
print ' due to shell exit code %d' % exit_code
elif did_time_out:
print ' due to exceeded timeout of %fs' % timeout
else:
print ' due to test results'
print 72 * '-'
print output
print 72 * '-'
return False
return True
# Copyright 2014 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.
"""Apptest runner specific to the particular Dart apptest framework in
/mojo/dart/apptests, built on top of the general apptest runner."""
import logging
import re
_logging = logging.getLogger()
from devtoolslib.apptest import run_apptest
SUCCESS_PATTERN = re.compile('^.+ .+: All tests passed!', re.MULTILINE)
def _dart_apptest_output_test(output):
return SUCCESS_PATTERN.search(output) is not None
# TODO(erg): Support android, launched services and fixture isolation.
def run_dart_apptest(shell, shell_args, apptest_url, apptest_args, timeout):
"""Runs a dart apptest.
Args:
shell_args: The arguments for mojo_shell.
apptest_url: Url of the apptest app to run.
apptest_args: Parameters to be passed to the apptest app.
Returns:
True iff the test succeeded, False otherwise.
"""
return run_apptest(shell, shell_args, apptest_url, apptest_args, timeout,
_dart_apptest_output_test)
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册