Update from https://crrev.com/317530
TBR=qsr@chromium.org
BUG=461092
Review URL: https://codereview.chromium.org/952893003
diff --git a/BUILD.gn b/BUILD.gn
index e417681..c63d377 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -42,7 +42,6 @@
"android/jni_android.h",
"android/jni_array.cc",
"android/jni_array.h",
- "android/jni_onload_delegate.h",
"android/jni_registrar.cc",
"android/jni_registrar.h",
"android/jni_string.cc",
@@ -676,6 +675,10 @@
"trace_event/memory_dump_provider.h",
"trace_event/process_memory_dump.cc",
"trace_event/process_memory_dump.h",
+ "trace_event/process_memory_totals.cc",
+ "trace_event/process_memory_totals.h",
+ "trace_event/process_memory_totals_dump_provider.cc",
+ "trace_event/process_memory_totals_dump_provider.h",
"trace_event/trace_event.h",
"trace_event/trace_event_android.cc",
"trace_event/trace_event_argument.cc",
@@ -869,7 +872,7 @@
]
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
- cflags = [ "/wd4267" ]
+ configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
libs = [
"cfgmgr32.lib",
@@ -1028,10 +1031,8 @@
configs += [ "//build/config/compiler:optimize_max" ]
}
- if (is_win) {
- # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
- cflags = [ "/wd4267" ]
- }
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
}
source_set("prefs") {
@@ -1335,6 +1336,7 @@
"timer/timer_unittest.cc",
"tools_sanity_unittest.cc",
"trace_event/memory_dump_manager_unittest.cc",
+ "trace_event/process_memory_totals_dump_provider_unittest.cc",
"trace_event/trace_event_argument_unittest.cc",
"trace_event/trace_event_memory_unittest.cc",
"trace_event/trace_event_synthetic_delay_unittest.cc",
@@ -1439,10 +1441,8 @@
set_sources_assignment_filter(sources_assignment_filter)
}
- if (is_win) {
- # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
- cflags = [ "/wd4267" ]
- }
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
}
if (is_android) {
diff --git a/allocator/BUILD.gn b/allocator/BUILD.gn
index e931b1c..a07a356 100644
--- a/allocator/BUILD.gn
+++ b/allocator/BUILD.gn
@@ -36,7 +36,7 @@
args = [
visual_studio_path + "/vc/lib",
rebase_path("$target_gen_dir/allocator"),
- cpu_arch,
+ current_cpu,
]
}
}
diff --git a/android/base_jni_onload.cc b/android/base_jni_onload.cc
index ae64120..c3a65d4 100644
--- a/android/base_jni_onload.cc
+++ b/android/base_jni_onload.cc
@@ -5,59 +5,51 @@
#include "base/android/base_jni_onload.h"
#include "base/android/jni_android.h"
-#include "base/android/jni_onload_delegate.h"
+#include "base/android/jni_utils.h"
#include "base/android/library_loader/library_loader_hooks.h"
+#include "base/bind.h"
namespace base {
namespace android {
namespace {
-// The JNIOnLoadDelegate implementation in base.
-class BaseJNIOnLoadDelegate : public JNIOnLoadDelegate {
- public:
- bool RegisterJNI(JNIEnv* env) override;
- bool Init() override;
-};
-
-bool BaseJNIOnLoadDelegate::RegisterJNI(JNIEnv* env) {
+bool RegisterJNI(JNIEnv* env) {
return RegisterLibraryLoaderEntryHook(env);
}
-bool BaseJNIOnLoadDelegate::Init() {
+bool Init() {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ base::android::InitReplacementClassLoader(env,
+ base::android::GetClassLoader(env));
return true;
}
} // namespace
-bool OnJNIOnLoad(JavaVM* vm,
- std::vector<JNIOnLoadDelegate*>* delegates) {
+bool OnJNIOnLoadRegisterJNI(JavaVM* vm,
+ std::vector<RegisterCallback> callbacks) {
base::android::InitVM(vm);
JNIEnv* env = base::android::AttachCurrentThread();
- BaseJNIOnLoadDelegate delegate;
- delegates->push_back(&delegate);
- bool ret = true;
- for (std::vector<JNIOnLoadDelegate*>::reverse_iterator i =
- delegates->rbegin(); i != delegates->rend(); ++i) {
- if (!(*i)->RegisterJNI(env)) {
- ret = false;
- break;
- }
+ callbacks.push_back(base::Bind(&RegisterJNI));
+ for (std::vector<RegisterCallback>::reverse_iterator i =
+ callbacks.rbegin(); i != callbacks.rend(); ++i) {
+ if (!i->Run(env))
+ return false;
}
+ return true;
+}
- if (ret) {
- for (std::vector<JNIOnLoadDelegate*>::reverse_iterator i =
- delegates->rbegin(); i != delegates->rend(); ++i) {
- if (!(*i)->Init()) {
- ret = false;
- break;
- }
- }
+bool OnJNIOnLoadInit(std::vector<InitCallback> callbacks) {
+ callbacks.push_back(base::Bind(&Init));
+ for (std::vector<InitCallback>::reverse_iterator i =
+ callbacks.rbegin(); i != callbacks.rend(); ++i) {
+ if (!i->Run())
+ return false;
}
- delegates->pop_back();
- return ret;
+ return true;
}
} // namespace android
diff --git a/android/base_jni_onload.h b/android/base_jni_onload.h
index f3f05fa..dcc7756 100644
--- a/android/base_jni_onload.h
+++ b/android/base_jni_onload.h
@@ -9,17 +9,22 @@
#include <vector>
#include "base/base_export.h"
+#include "base/callback.h"
namespace base {
namespace android {
-class JNIOnLoadDelegate;
+// Returns whether JNI registration succeeded. Caller shall put the
+// RegisterCallback into |callbacks| in reverse order.
+typedef base::Callback<bool(JNIEnv*)> RegisterCallback;
+BASE_EXPORT bool OnJNIOnLoadRegisterJNI(
+ JavaVM* vm,
+ std::vector<RegisterCallback> callbacks);
-// Returns whether JNI registration and initialization succeeded. Caller shall
-// put the JNIOnLoadDelegate into |delegates| in reverse order. Refer
-// JNIOnLoadDelegate for more information.
-BASE_EXPORT bool OnJNIOnLoad(JavaVM* vm,
- std::vector<JNIOnLoadDelegate*>* delegates);
+// Returns whether initialization succeeded. Caller shall put the
+// InitCallback into |callbacks| in reverse order.
+typedef base::Callback<bool(void)> InitCallback;
+BASE_EXPORT bool OnJNIOnLoadInit(std::vector<InitCallback> callbacks);
} // namespace android
} // namespace base
diff --git a/android/java/src/org/chromium/base/ResourceExtractor.java b/android/java/src/org/chromium/base/ResourceExtractor.java
index 9252b4d..d44f2fc 100644
--- a/android/java/src/org/chromium/base/ResourceExtractor.java
+++ b/android/java/src/org/chromium/base/ResourceExtractor.java
@@ -12,6 +12,8 @@
import android.content.res.AssetManager;
import android.os.AsyncTask;
import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
import android.os.Trace;
import android.preference.PreferenceManager;
import android.util.Log;
@@ -57,6 +59,8 @@
private class ExtractTask extends AsyncTask<Void, Void, Void> {
private static final int BUFFER_SIZE = 16 * 1024;
+ private final List<Runnable> mCompletionCallbacks = new ArrayList<Runnable>();
+
public ExtractTask() {
}
@@ -211,6 +215,23 @@
return null;
}
+ private void onPostExecuteImpl() {
+ for (int i = 0; i < mCompletionCallbacks.size(); i++) {
+ mCompletionCallbacks.get(i).run();
+ }
+ mCompletionCallbacks.clear();
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ beginTraceSection("ResourceExtractor.ExtractTask.onPostExecute");
+ try {
+ onPostExecuteImpl();
+ } finally {
+ endTraceSection();
+ }
+ }
+
// Looks for a timestamp file on disk that indicates the version of the APK that
// the resource paks were extracted from. Returns null if a timestamp was found
// and it indicates that the resources match the current APK. Otherwise returns
@@ -335,6 +356,13 @@
mContext = context.getApplicationContext();
}
+ /**
+ * Synchronously wait for the resource extraction to be completed.
+ * <p>
+ * This method is bad and you should feel bad for using it.
+ *
+ * @see #addCompletionCallback(Runnable)
+ */
public void waitForCompletion() {
if (shouldSkipPakExtraction()) {
return;
@@ -355,6 +383,35 @@
}
/**
+ * Adds a callback to be notified upon the completion of resource extraction.
+ * <p>
+ * If the resource task has already completed, the callback will be posted to the UI message
+ * queue. Otherwise, it will be executed after all the resources have been extracted.
+ * <p>
+ * This must be called on the UI thread. The callback will also always be executed on
+ * the UI thread.
+ *
+ * @param callback The callback to be enqueued.
+ */
+ public void addCompletionCallback(Runnable callback) {
+ ThreadUtils.assertOnUiThread();
+
+ Handler handler = new Handler(Looper.getMainLooper());
+ if (shouldSkipPakExtraction()) {
+ handler.post(callback);
+ return;
+ }
+
+ assert mExtractTask != null;
+ assert !mExtractTask.isCancelled();
+ if (mExtractTask.getStatus() == AsyncTask.Status.FINISHED) {
+ handler.post(callback);
+ } else {
+ mExtractTask.mCompletionCallbacks.add(callback);
+ }
+ }
+
+ /**
* This will extract the application pak resources in an
* AsyncTask. Call waitForCompletion() at the point resources
* are needed to block until the task completes.
diff --git a/android/java/src/org/chromium/base/library_loader/Linker.java b/android/java/src/org/chromium/base/library_loader/Linker.java
index 23f953c..bbf76cb 100644
--- a/android/java/src/org/chromium/base/library_loader/Linker.java
+++ b/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -1039,7 +1039,7 @@
mLoadSize = in.readLong();
mRelroStart = in.readLong();
mRelroSize = in.readLong();
- ParcelFileDescriptor fd = in.readFileDescriptor();
+ ParcelFileDescriptor fd = ParcelFileDescriptor.CREATOR.createFromParcel(in);
// If CreateSharedRelro fails, the OS file descriptor will be -1 and |fd| will be null.
mRelroFd = (fd == null) ? -1 : fd.detachFd();
}
diff --git a/android/jni_android.cc b/android/jni_android.cc
index e09c2d5..a2de00a 100644
--- a/android/jni_android.cc
+++ b/android/jni_android.cc
@@ -17,6 +17,8 @@
using base::android::MethodID;
using base::android::ScopedJavaLocalRef;
+bool g_disable_manual_jni_registration = false;
+
JavaVM* g_jvm = NULL;
// Leak the global app context, as it is used from a non-joinable worker thread
// that may still be running at shutdown. There is no harm in doing this.
@@ -77,6 +79,15 @@
namespace base {
namespace android {
+bool IsManualJniRegistrationDisabled() {
+ return g_disable_manual_jni_registration;
+}
+
+void DisableManualJniRegistration() {
+ DCHECK(!g_disable_manual_jni_registration);
+ g_disable_manual_jni_registration = true;
+}
+
JNIEnv* AttachCurrentThread() {
DCHECK(g_jvm);
JNIEnv* env = NULL;
diff --git a/android/jni_android.h b/android/jni_android.h
index b5e5526..504eb85 100644
--- a/android/jni_android.h
+++ b/android/jni_android.h
@@ -21,6 +21,13 @@
// Used to mark symbols to be exported in a shared library's symbol table.
#define JNI_EXPORT __attribute__ ((visibility("default")))
+// Used to disable manual JNI registration in binaries that prefer to use native
+// JNI exports for startup performance. This is not compatible with the crazy
+// linker and so defaults to off. Call DisableManualJniRegistration at the very
+// beginning of JNI_OnLoad to use this.
+BASE_EXPORT bool IsManualJniRegistrationDisabled();
+BASE_EXPORT void DisableManualJniRegistration();
+
// Contains the registration method information for initializing JNI bindings.
struct RegistrationMethod {
const char* name;
diff --git a/android/jni_generator/jni_generator.py b/android/jni_generator/jni_generator.py
index 6e39c13..54fea6b 100755
--- a/android/jni_generator/jni_generator.py
+++ b/android/jni_generator/jni_generator.py
@@ -889,7 +889,7 @@
def GetJNINativeMethodsString(self):
"""Returns the implementation of the array of native methods."""
- if self.options.native_exports:
+ if self.options.native_exports and not self.options.native_exports_optional:
return ''
template = Template("""\
static const JNINativeMethod kMethods${JAVA_CLASS}[] = {
@@ -922,7 +922,7 @@
"""Returns the code for RegisterNatives."""
template = Template("""\
${REGISTER_NATIVES_SIGNATURE} {
-${CLASSES}
+${EARLY_EXIT}${CLASSES}
${NATIVES}
${CALLED_BY_NATIVES}
return true;
@@ -934,9 +934,16 @@
else:
signature += ')'
+ early_exit = ''
+ if self.options.native_exports_optional:
+ early_exit = """\
+ if (base::android::IsManualJniRegistrationDisabled()) return true;
+"""
+
natives = self.GetRegisterNativesImplString()
called_by_natives = self.GetRegisterCalledByNativesImplString()
values = {'REGISTER_NATIVES_SIGNATURE': signature,
+ 'EARLY_EXIT': early_exit,
'CLASSES': self.GetFindClasses(),
'NATIVES': natives,
'CALLED_BY_NATIVES': called_by_natives,
@@ -945,7 +952,7 @@
def GetRegisterNativesImplString(self):
"""Returns the shared implementation for RegisterNatives."""
- if self.options.native_exports:
+ if self.options.native_exports and not self.options.native_exports_optional:
return ''
template = Template("""\
@@ -1035,6 +1042,31 @@
param.name
for param in called_by_native.params])
+ def GetStubName(self, native):
+ """Return the name of the stub function for this native method.
+
+ Args:
+ native: the native dictionary describing the method.
+
+ Returns:
+ A string with the stub function name. For native exports mode this is the
+ Java_* symbol name required by the JVM; otherwise it is just the name of
+ the native method itself.
+ """
+ if self.options.native_exports:
+ template = Template("Java_${JAVA_NAME}_native${NAME}")
+
+ java_name = JniParams.RemapClassName(self.fully_qualified_class)
+ java_name = java_name.replace('_', '_1').replace('/', '_')
+ if native.java_class_name:
+ java_name += '_00024' + native.java_class_name
+
+ values = {'NAME': native.name,
+ 'JAVA_NAME': java_name}
+ return template.substitute(values)
+ else:
+ return native.name
+
def GetForwardDeclaration(self, native):
template_str = """
static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS});
@@ -1042,7 +1074,7 @@
if self.options.native_exports:
template_str += """
__attribute__((visibility("default"), alias("${NAME}")))
-${RETURN} Java_${JAVA_NAME}_native${NAME}(JNIEnv* env, ${PARAMS});
+${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS});
"""
template = Template(template_str)
params_in_call = []
@@ -1050,16 +1082,11 @@
params_in_call = ['env', 'jcaller']
params_in_call = ', '.join(params_in_call + [p.name for p in native.params])
- java_name = JniParams.RemapClassName(self.fully_qualified_class)
- java_name = java_name.replace('_', '_1').replace('/', '_')
- if native.java_class_name:
- java_name += '_00024' + native.java_class_name
-
values = {'RETURN': JavaDataTypeToC(native.return_type),
'NAME': native.name,
- 'JAVA_NAME': java_name,
'PARAMS': self.GetParamsInDeclaration(native),
- 'PARAMS_IN_CALL': params_in_call}
+ 'PARAMS_IN_CALL': params_in_call,
+ 'STUB_NAME': self.GetStubName(native)}
return template.substitute(values)
def GetNativeMethodStubString(self, native):
@@ -1067,11 +1094,11 @@
if self.options.native_exports:
template_str = """\
__attribute__((visibility("default")))
-${RETURN} Java_${JAVA_NAME}_native${NAME}(JNIEnv* env,
+${RETURN} ${STUB_NAME}(JNIEnv* env,
${PARAMS_IN_DECLARATION}) {"""
else:
template_str = """\
-static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {"""
+static ${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {"""
template_str += """
${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME});
CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}"${OPTIONAL_ERROR_RETURN});
@@ -1093,24 +1120,16 @@
if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
post_call = '.Release()'
- if self.options.native_exports:
- java_name = JniParams.RemapClassName(self.fully_qualified_class)
- java_name = java_name.replace('_', '_1').replace('/', '_')
- if native.java_class_name:
- java_name += '_00024' + native.java_class_name
- else:
- java_name = ''
-
values = {
'RETURN': return_type,
'OPTIONAL_ERROR_RETURN': optional_error_return,
- 'JAVA_NAME': java_name,
'NAME': native.name,
'PARAMS_IN_DECLARATION': self.GetParamsInDeclaration(native),
'PARAM0_NAME': native.params[0].name,
'P0_TYPE': native.p0_type,
'PARAMS_IN_CALL': params_in_call,
- 'POST_CALL': post_call
+ 'POST_CALL': post_call,
+ 'STUB_NAME': self.GetStubName(native),
}
return template.substitute(values)
@@ -1225,12 +1244,13 @@
return template.substitute(values)
def GetKMethodArrayEntry(self, native):
- template = Template("""\
- { "native${NAME}", ${JNI_SIGNATURE}, reinterpret_cast<void*>(${NAME}) },""")
+ template = Template(' { "native${NAME}", ${JNI_SIGNATURE}, ' +
+ 'reinterpret_cast<void*>(${STUB_NAME}) },')
values = {'NAME': native.name,
'JNI_SIGNATURE': JniParams.Signature(native.params,
native.return_type,
- True)}
+ True),
+ 'STUB_NAME': self.GetStubName(native)}
return template.substitute(values)
def GetUniqueClasses(self, origin):
@@ -1500,7 +1520,12 @@
option_parser.add_option('--native_exports', action='store_true',
help='Native method registration through .so '
'exports.')
+ option_parser.add_option('--native_exports_optional', action='store_true',
+ help='Support both explicit and native method'
+ 'registration.')
options, args = option_parser.parse_args(argv)
+ if options.native_exports_optional:
+ options.native_exports = True
if options.jar_file:
input_file = ExtractJarInputFile(options.jar_file, options.input_file,
options.output_dir)
diff --git a/android/jni_generator/jni_generator_tests.py b/android/jni_generator/jni_generator_tests.py
index 7e39cda..e29bc0c 100755
--- a/android/jni_generator/jni_generator_tests.py
+++ b/android/jni_generator/jni_generator_tests.py
@@ -43,6 +43,7 @@
self.cpp = 'cpp'
self.javap = 'javap'
self.native_exports = False
+ self.native_exports_optional = False
class TestGenerator(unittest.TestCase):
def assertObjEquals(self, first, second):
@@ -1019,7 +1020,7 @@
test_data, 'org/chromium/example/jni_generator/Test', options)
self.assertGoldenTextEquals(jni_from_java.GetContent())
- def testNativeExportsOption(self):
+ def runNativeExportsOption(self, optional):
test_data = """
package org.chromium.example.jni_generator;
@@ -1054,9 +1055,18 @@
options = TestOptions()
options.jni_init_native_name = 'nativeInitNativeClass'
options.native_exports = True
+ options.native_exports_optional = optional
jni_from_java = jni_generator.JNIFromJavaSource(
test_data, 'org/chromium/example/jni_generator/SampleForTests', options)
- self.assertGoldenTextEquals(jni_from_java.GetContent())
+ return jni_from_java.GetContent()
+
+ def testNativeExportsOption(self):
+ content = self.runNativeExportsOption(False)
+ self.assertGoldenTextEquals(content)
+
+ def testNativeExportsOptionalOption(self):
+ content = self.runNativeExportsOption(True)
+ self.assertGoldenTextEquals(content)
def testOuterInnerRaises(self):
test_data = """
diff --git a/android/jni_generator/testNativeExportsOptionalOption.golden b/android/jni_generator/testNativeExportsOptionalOption.golden
new file mode 100644
index 0000000..2a3b172
--- /dev/null
+++ b/android/jni_generator/testNativeExportsOptionalOption.golden
@@ -0,0 +1,283 @@
+// 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.
+
+// This file is autogenerated by
+// base/android/jni_generator/jni_generator.py
+// For
+// org/chromium/example/jni_generator/SampleForTests
+
+#ifndef org_chromium_example_jni_generator_SampleForTests_JNI
+#define org_chromium_example_jni_generator_SampleForTests_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_generator/jni_generator_helper.h"
+
+#include "base/android/jni_int_wrapper.h"
+
+// Step 1: forward declarations.
+namespace {
+const char kSampleForTestsClassPath[] =
+ "org/chromium/example/jni_generator/SampleForTests";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+base::subtle::AtomicWord g_SampleForTests_clazz __attribute__((unused)) = 0;
+#define SampleForTests_clazz(env) base::android::LazyGetClass(env, kSampleForTestsClassPath, &g_SampleForTests_clazz)
+
+} // namespace
+
+extern "C" {
+
+static jint Init(JNIEnv* env, jobject jcaller);
+
+__attribute__((visibility("default"), alias("Init")))
+jint
+ Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit(JNIEnv*
+ env, jobject jcaller);
+
+static jint Init(JNIEnv* env, jobject jcaller);
+
+__attribute__((visibility("default"), alias("Init")))
+jint
+ Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit(JNIEnv*
+ env, jobject jcaller);
+
+}; // extern "C"
+
+// Step 2: method stubs.
+
+extern "C" {
+__attribute__((visibility("default")))
+jint
+ Java_org_chromium_example_jni_1generator_SampleForTests_nativeStaticMethod(JNIEnv*
+ env,
+ jobject jcaller,
+ jlong nativeTest,
+ jint arg1) {
+ Test* native = reinterpret_cast<Test*>(nativeTest);
+ CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0);
+ return native->StaticMethod(env, jcaller, arg1);
+}
+
+__attribute__((visibility("default")))
+jint
+ Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethod(JNIEnv*
+ env,
+ jobject jcaller,
+ jlong nativeTest,
+ jint arg1) {
+ Test* native = reinterpret_cast<Test*>(nativeTest);
+ CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0);
+ return native->Method(env, jcaller, arg1);
+}
+
+static base::subtle::AtomicWord g_SampleForTests_testMethodWithParam = 0;
+static void Java_SampleForTests_testMethodWithParam(JNIEnv* env, jobject obj,
+ JniIntWrapper iParam) {
+ /* Must call RegisterNativesImpl() */
+ CHECK_CLAZZ(env, obj,
+ SampleForTests_clazz(env));
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, SampleForTests_clazz(env),
+ "testMethodWithParam",
+
+"("
+"I"
+")"
+"V",
+ &g_SampleForTests_testMethodWithParam);
+
+ env->CallVoidMethod(obj,
+ method_id, as_jint(iParam));
+ jni_generator::CheckException(env);
+
+}
+
+static base::subtle::AtomicWord g_SampleForTests_testMethodWithParamAndReturn =
+ 0;
+static base::android::ScopedJavaLocalRef<jstring>
+ Java_SampleForTests_testMethodWithParamAndReturn(JNIEnv* env, jobject obj,
+ JniIntWrapper iParam) {
+ /* Must call RegisterNativesImpl() */
+ CHECK_CLAZZ(env, obj,
+ SampleForTests_clazz(env), NULL);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_INSTANCE>(
+ env, SampleForTests_clazz(env),
+ "testMethodWithParamAndReturn",
+
+"("
+"I"
+")"
+"Ljava/lang/String;",
+ &g_SampleForTests_testMethodWithParamAndReturn);
+
+ jstring ret =
+ static_cast<jstring>(env->CallObjectMethod(obj,
+ method_id, as_jint(iParam)));
+ jni_generator::CheckException(env);
+ return base::android::ScopedJavaLocalRef<jstring>(env, ret);
+}
+
+static base::subtle::AtomicWord g_SampleForTests_testStaticMethodWithParam = 0;
+static jint Java_SampleForTests_testStaticMethodWithParam(JNIEnv* env,
+ JniIntWrapper iParam) {
+ /* Must call RegisterNativesImpl() */
+ CHECK_CLAZZ(env, SampleForTests_clazz(env),
+ SampleForTests_clazz(env), 0);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_STATIC>(
+ env, SampleForTests_clazz(env),
+ "testStaticMethodWithParam",
+
+"("
+"I"
+")"
+"I",
+ &g_SampleForTests_testStaticMethodWithParam);
+
+ jint ret =
+ env->CallStaticIntMethod(SampleForTests_clazz(env),
+ method_id, as_jint(iParam));
+ jni_generator::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_SampleForTests_testMethodWithNoParam = 0;
+static jdouble Java_SampleForTests_testMethodWithNoParam(JNIEnv* env) {
+ /* Must call RegisterNativesImpl() */
+ CHECK_CLAZZ(env, SampleForTests_clazz(env),
+ SampleForTests_clazz(env), 0);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_STATIC>(
+ env, SampleForTests_clazz(env),
+ "testMethodWithNoParam",
+
+"("
+")"
+"D",
+ &g_SampleForTests_testMethodWithNoParam);
+
+ jdouble ret =
+ env->CallStaticDoubleMethod(SampleForTests_clazz(env),
+ method_id);
+ jni_generator::CheckException(env);
+ return ret;
+}
+
+static base::subtle::AtomicWord g_SampleForTests_testStaticMethodWithNoParam =
+ 0;
+static base::android::ScopedJavaLocalRef<jstring>
+ Java_SampleForTests_testStaticMethodWithNoParam(JNIEnv* env) {
+ /* Must call RegisterNativesImpl() */
+ CHECK_CLAZZ(env, SampleForTests_clazz(env),
+ SampleForTests_clazz(env), NULL);
+ jmethodID method_id =
+ base::android::MethodID::LazyGet<
+ base::android::MethodID::TYPE_STATIC>(
+ env, SampleForTests_clazz(env),
+ "testStaticMethodWithNoParam",
+
+"("
+")"
+"Ljava/lang/String;",
+ &g_SampleForTests_testStaticMethodWithNoParam);
+
+ jstring ret =
+static_cast<jstring>(env->CallStaticObjectMethod(SampleForTests_clazz(env),
+ method_id));
+ jni_generator::CheckException(env);
+ return base::android::ScopedJavaLocalRef<jstring>(env, ret);
+}
+}; // extern "C"
+
+// Step 3: RegisterNatives.
+
+static const JNINativeMethod kMethodsMyOtherInnerClass[] = {
+ { "nativeInit",
+"("
+")"
+"I",
+ reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit)
+ },
+};
+
+static const JNINativeMethod kMethodsMyInnerClass[] = {
+ { "nativeInit",
+"("
+")"
+"I",
+ reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit)
+ },
+};
+
+static const JNINativeMethod kMethodsSampleForTests[] = {
+ { "nativeStaticMethod",
+"("
+"J"
+"I"
+")"
+"I",
+ reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeStaticMethod)
+ },
+ { "nativeMethod",
+"("
+"J"
+"I"
+")"
+"I",
+ reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethod)
+ },
+};
+
+static bool RegisterNativesImpl(JNIEnv* env, jclass clazz) {
+ if (base::android::IsManualJniRegistrationDisabled()) return true;
+ base::subtle::Release_Store(&g_SampleForTests_clazz,
+ static_cast<base::subtle::AtomicWord>(env->NewWeakGlobalRef(clazz));
+
+ const int kMethodsMyOtherInnerClassSize =
+ arraysize(kMethodsMyOtherInnerClass);
+
+ if (env->RegisterNatives(MyOtherInnerClass_clazz(env),
+ kMethodsMyOtherInnerClass,
+ kMethodsMyOtherInnerClassSize) < 0) {
+ jni_generator::HandleRegistrationError(
+ env, MyOtherInnerClass_clazz(env), __FILE__);
+ return false;
+ }
+
+ const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass);
+
+ if (env->RegisterNatives(MyInnerClass_clazz(env),
+ kMethodsMyInnerClass,
+ kMethodsMyInnerClassSize) < 0) {
+ jni_generator::HandleRegistrationError(
+ env, MyInnerClass_clazz(env), __FILE__);
+ return false;
+ }
+
+ const int kMethodsSampleForTestsSize = arraysize(kMethodsSampleForTests);
+
+ if (env->RegisterNatives(SampleForTests_clazz(env),
+ kMethodsSampleForTests,
+ kMethodsSampleForTestsSize) < 0) {
+ jni_generator::HandleRegistrationError(
+ env, SampleForTests_clazz(env), __FILE__);
+ return false;
+ }
+
+ return true;
+}
+
+extern "C" JNIEXPORT bool JNICALL
+Java_org_chromium_example_jni_1generator_SampleForTests_nativeInitNativeClass(JNIEnv*
+ env, jclass clazz) {
+ return RegisterNativesImpl(env, clazz);
+}
+
+#endif // org_chromium_example_jni_generator_SampleForTests_JNI
diff --git a/android/jni_onload_delegate.h b/android/jni_onload_delegate.h
deleted file mode 100644
index ef1b137..0000000
--- a/android/jni_onload_delegate.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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 BASE_ANDROID_JNI_ONLOAD_DELEGATE_H_
-#define BASE_ANDROID_JNI_ONLOAD_DELEGATE_H_
-
-#include <jni.h>
-
-#include "base/base_export.h"
-
-namespace base {
-namespace android {
-
-// This delegate class is used to implement component specific JNI registration
-// and initialization. All methods are called in JNI_OnLoad().
-//
-// Both RegisterJNI() and Init() methods are called if the shared library
-// is loaded by crazy linker that can't find JNI methods without JNI
-// registration, otherwise, only Init() is invoked where dynamic lookup is
-// used to find the JNI methods.
-//
-// It is important to make sure the JNI registration code is only in
-// RegisterJNI(), so it could be stripped out when JNI registration isn't
-// needed.
-class BASE_EXPORT JNIOnLoadDelegate {
- public:
- virtual ~JNIOnLoadDelegate() {}
-
- // Returns whether the JNI registration succeeded.
- virtual bool RegisterJNI(JNIEnv* env) = 0;
-
- // Returns whether the initialization succeeded. This method is called after
- // RegisterJNI(), all JNI methods shall ready to be used.
- virtual bool Init() = 0;
-};
-
-} // namespace android
-} // namespace base
-
-#endif // BASE_ANDROID_JNI_ONLOAD_DELEGATE_H_
diff --git a/base.gyp b/base.gyp
index 8cd5d6a..213e62d 100644
--- a/base.gyp
+++ b/base.gyp
@@ -645,6 +645,7 @@
'timer/timer_unittest.cc',
'tools_sanity_unittest.cc',
'trace_event/memory_dump_manager_unittest.cc',
+ 'trace_event/process_memory_totals_dump_provider_unittest.cc',
'trace_event/trace_event_argument_unittest.cc',
'trace_event/trace_event_memory_unittest.cc',
'trace_event/trace_event_synthetic_delay_unittest.cc',
diff --git a/base.gypi b/base.gypi
index 37a8171..b7c33b8 100644
--- a/base.gypi
+++ b/base.gypi
@@ -44,7 +44,6 @@
'android/jni_android.h',
'android/jni_array.cc',
'android/jni_array.h',
- 'android/jni_onload_delegate.h',
'android/jni_registrar.cc',
'android/jni_registrar.h',
'android/jni_string.cc',
@@ -672,6 +671,10 @@
'trace_event/memory_dump_provider.h',
'trace_event/process_memory_dump.cc',
'trace_event/process_memory_dump.h',
+ 'trace_event/process_memory_totals.cc',
+ 'trace_event/process_memory_totals.h',
+ 'trace_event/process_memory_totals_dump_provider.cc',
+ 'trace_event/process_memory_totals_dump_provider.h',
'trace_event/trace_event.h',
'trace_event/trace_event_android.cc',
'trace_event/trace_event_argument.cc',
diff --git a/i18n/break_iterator.cc b/i18n/break_iterator.cc
index e3aaa2b..e2ed667 100644
--- a/i18n/break_iterator.cc
+++ b/i18n/break_iterator.cc
@@ -74,7 +74,8 @@
static_cast<int32_t>(string_.size()),
&status);
if (U_FAILURE(status)) {
- NOTREACHED() << "ubrk_open failed";
+ NOTREACHED() << "ubrk_open failed for type " << break_type
+ << " with error " << status;
}
}
diff --git a/i18n/time_formatting_unittest.cc b/i18n/time_formatting_unittest.cc
index 4739b62..df0c1ed 100644
--- a/i18n/time_formatting_unittest.cc
+++ b/i18n/time_formatting_unittest.cc
@@ -158,11 +158,11 @@
EXPECT_EQ(ASCIIToUTF16("30 Apr 2011"), TimeFormatShortDate(time));
EXPECT_EQ(ASCIIToUTF16("30/04/2011"), TimeFormatShortDateNumeric(time));
- EXPECT_EQ(ASCIIToUTF16("30/04/2011 15:42:07"),
+ EXPECT_EQ(ASCIIToUTF16("30/04/2011, 15:42:07"),
TimeFormatShortDateAndTime(time));
- EXPECT_EQ(ASCIIToUTF16("30/04/2011 15:42:07 ") + GetShortTimeZone(),
+ EXPECT_EQ(ASCIIToUTF16("30/04/2011, 15:42:07 ") + GetShortTimeZone(),
TimeFormatShortDateAndTimeWithTimeZone(time));
- EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011 15:42:07"),
+ EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011 at 15:42:07"),
TimeFormatFriendlyDateAndTime(time));
EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011"),
TimeFormatFriendlyDate(time));
diff --git a/ios/device_util.mm b/ios/device_util.mm
index ff7be36..1234562 100644
--- a/ios/device_util.mm
+++ b/ios/device_util.mm
@@ -13,7 +13,6 @@
#include <sys/socket.h>
#include <sys/sysctl.h>
-#include "base/ios/ios_util.h"
#include "base/logging.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/memory/scoped_ptr.h"
diff --git a/ios/device_util_unittest.mm b/ios/device_util_unittest.mm
index 3494e00..82d4217 100644
--- a/ios/device_util_unittest.mm
+++ b/ios/device_util_unittest.mm
@@ -5,7 +5,6 @@
#import <UIKit/UIKit.h>
#include "base/ios/device_util.h"
-#include "base/ios/ios_util.h"
#include "base/strings/sys_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
diff --git a/memory/discardable_memory_android.cc b/memory/discardable_memory_android.cc
index 27b390f..5dcdfdc 100644
--- a/memory/discardable_memory_android.cc
+++ b/memory/discardable_memory_android.cc
@@ -53,8 +53,8 @@
std::vector<DiscardableMemoryType>* types) {
const DiscardableMemoryType supported_types[] = {
DISCARDABLE_MEMORY_TYPE_ASHMEM,
- DISCARDABLE_MEMORY_TYPE_EMULATED,
- DISCARDABLE_MEMORY_TYPE_SHMEM
+ DISCARDABLE_MEMORY_TYPE_SHMEM,
+ DISCARDABLE_MEMORY_TYPE_EMULATED
};
types->assign(supported_types, supported_types + arraysize(supported_types));
}
diff --git a/memory/discardable_memory_linux.cc b/memory/discardable_memory_linux.cc
index 977b029..670ad7e 100644
--- a/memory/discardable_memory_linux.cc
+++ b/memory/discardable_memory_linux.cc
@@ -19,8 +19,8 @@
void DiscardableMemory::GetSupportedTypes(
std::vector<DiscardableMemoryType>* types) {
const DiscardableMemoryType supported_types[] = {
- DISCARDABLE_MEMORY_TYPE_EMULATED,
- DISCARDABLE_MEMORY_TYPE_SHMEM
+ DISCARDABLE_MEMORY_TYPE_SHMEM,
+ DISCARDABLE_MEMORY_TYPE_EMULATED
};
types->assign(supported_types, supported_types + arraysize(supported_types));
}
diff --git a/memory/discardable_memory_mac.cc b/memory/discardable_memory_mac.cc
index c8669a6..e0096e5 100644
--- a/memory/discardable_memory_mac.cc
+++ b/memory/discardable_memory_mac.cc
@@ -23,8 +23,8 @@
std::vector<DiscardableMemoryType>* types) {
const DiscardableMemoryType supported_types[] = {
DISCARDABLE_MEMORY_TYPE_MACH,
- DISCARDABLE_MEMORY_TYPE_EMULATED,
- DISCARDABLE_MEMORY_TYPE_SHMEM
+ DISCARDABLE_MEMORY_TYPE_SHMEM,
+ DISCARDABLE_MEMORY_TYPE_EMULATED
};
types->assign(supported_types, supported_types + arraysize(supported_types));
}
diff --git a/memory/discardable_memory_win.cc b/memory/discardable_memory_win.cc
index 977b029..670ad7e 100644
--- a/memory/discardable_memory_win.cc
+++ b/memory/discardable_memory_win.cc
@@ -19,8 +19,8 @@
void DiscardableMemory::GetSupportedTypes(
std::vector<DiscardableMemoryType>* types) {
const DiscardableMemoryType supported_types[] = {
- DISCARDABLE_MEMORY_TYPE_EMULATED,
- DISCARDABLE_MEMORY_TYPE_SHMEM
+ DISCARDABLE_MEMORY_TYPE_SHMEM,
+ DISCARDABLE_MEMORY_TYPE_EMULATED
};
types->assign(supported_types, supported_types + arraysize(supported_types));
}
diff --git a/memory/scoped_ptr_unittest.cc b/memory/scoped_ptr_unittest.cc
index 0887a99..766f444 100644
--- a/memory/scoped_ptr_unittest.cc
+++ b/memory/scoped_ptr_unittest.cc
@@ -9,7 +9,6 @@
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/callback.h"
-#include "base/strings/stringprintf.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -686,8 +685,11 @@
// value first.
TEST(ScopedPtrTest, LoggingDoesntConvertToBoolean) {
scoped_ptr<int> x(new int);
- std::stringstream s;
- s << x;
- std::string expected = base::StringPrintf("%p", x.get());
- EXPECT_EQ(expected, s.str());
+ std::stringstream s1;
+ s1 << x;
+
+ std::stringstream s2;
+ s2 << x.get();
+
+ EXPECT_EQ(s2.str(), s1.str());
}
diff --git a/memory/singleton.h b/memory/singleton.h
index e5e2e3e..e50bdc0 100644
--- a/memory/singleton.h
+++ b/memory/singleton.h
@@ -23,7 +23,6 @@
#include "base/atomicops.h"
#include "base/base_export.h"
#include "base/memory/aligned_memory.h"
-#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/threading/thread_restrictions.h"
namespace base {
@@ -237,8 +236,6 @@
// instance_ pointer must acquire visibility over the singleton data.
base::subtle::AtomicWord value = base::subtle::Acquire_Load(&instance_);
if (value != 0 && value != base::internal::kBeingCreatedMarker) {
- // See the corresponding HAPPENS_BEFORE below.
- ANNOTATE_HAPPENS_AFTER(&instance_);
return reinterpret_cast<Type*>(value);
}
@@ -250,10 +247,6 @@
// stop right after we do this store.
Type* newval = Traits::New();
- // This annotation helps race detectors recognize correct lock-less
- // synchronization between different threads calling get().
- // See the corresponding HAPPENS_AFTER below and above.
- ANNOTATE_HAPPENS_BEFORE(&instance_);
// Releases the visibility over instance_ to the readers.
base::subtle::Release_Store(
&instance_, reinterpret_cast<base::subtle::AtomicWord>(newval));
@@ -267,8 +260,6 @@
// We hit a race. Wait for the other thread to complete it.
value = base::internal::WaitForInstance(&instance_);
- // See the corresponding HAPPENS_BEFORE above.
- ANNOTATE_HAPPENS_AFTER(&instance_);
return reinterpret_cast<Type*>(value);
}
diff --git a/process/process_win.cc b/process/process_win.cc
index 4e600f9..8e5360b 100644
--- a/process/process_win.cc
+++ b/process/process_win.cc
@@ -6,6 +6,7 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/metrics/field_trial.h"
#include "base/process/kill.h"
#include "base/win/windows_version.h"
@@ -165,7 +166,21 @@
priority = value ? PROCESS_MODE_BACKGROUND_BEGIN :
PROCESS_MODE_BACKGROUND_END;
} else {
- priority = value ? BELOW_NORMAL_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS;
+ // Experiment (http://crbug.com/458594) with using IDLE_PRIORITY_CLASS as a
+ // background priority for background renderers (this code path is
+ // technically for more than just the renderers but they're the only use
+ // case in practice and experimenting here direclty is thus easier -- plus
+ // it doesn't really hurt as above we already state our intent of using
+ // PROCESS_MODE_BACKGROUND_BEGIN if available which is essentially
+ // IDLE_PRIORITY_CLASS plus lowered IO priority). Enabled by default in the
+ // asbence of field trials to get coverage on the perf waterfall.
+ DWORD background_priority = IDLE_PRIORITY_CLASS;
+ base::FieldTrial* trial =
+ base::FieldTrialList::Find("BackgroundRendererProcesses");
+ if (trial && trial->group_name() == "AllowBelowNormalFromBrowser")
+ background_priority = BELOW_NORMAL_PRIORITY_CLASS;
+
+ priority = value ? background_priority : NORMAL_PRIORITY_CLASS;
}
return (::SetPriorityClass(Handle(), priority) != 0);
diff --git a/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java b/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java
index b6c103d..a104831 100644
--- a/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java
+++ b/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java
@@ -4,19 +4,20 @@
package org.chromium.base.test;
-import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.test.AndroidTestRunner;
import android.test.InstrumentationTestRunner;
import android.util.Log;
-import junit.framework.Test;
import junit.framework.TestCase;
-import junit.framework.TestSuite;
+import junit.framework.TestResult;
import org.chromium.base.test.util.MinAndroidSdkLevel;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* An Instrumentation test runner that checks SDK level for tests with specific requirements.
*/
@@ -24,40 +25,94 @@
private static final String TAG = "BaseInstrumentationTestRunner";
- @Override
- protected AndroidTestRunner getAndroidTestRunner() {
- return new BaseAndroidTestRunner(getContext());
+ /**
+ * An interface for classes that check whether a test case should be skipped.
+ */
+ public interface SkipCheck {
+ /**
+ * Checks whether the given test case should be skipped.
+ *
+ * @param testCase The test case to check.
+ * @return Whether the test case should be skipped.
+ */
+ public boolean shouldSkip(TestCase testCase);
}
/**
- * Skips tests that don't meet the requirements of the current device.
+ * A test result that can skip tests.
*/
- public class BaseAndroidTestRunner extends AndroidTestRunner {
- private final Context mContext;
+ public class SkippingTestResult extends TestResult {
- public BaseAndroidTestRunner(Context context) {
- mContext = context;
+ private final List<SkipCheck> mSkipChecks;
+
+ /**
+ * Creates an instance of SkippingTestResult.
+ */
+ public SkippingTestResult() {
+ mSkipChecks = new ArrayList<SkipCheck>();
+ }
+
+ /**
+ * Adds a check for whether a test should run.
+ *
+ * @param skipCheck The check to add.
+ */
+ public void addSkipCheck(SkipCheck skipCheck) {
+ mSkipChecks.add(skipCheck);
+ }
+
+ private boolean shouldSkip(final TestCase test) {
+ for (SkipCheck s : mSkipChecks) {
+ if (s.shouldSkip(test)) return true;
+ }
+ return false;
}
@Override
- public void setTest(Test test) {
- super.setTest(test);
- TestSuite revisedTestSuite = new TestSuite();
- for (TestCase testCase : this.getTestCases()) {
- Class<?> testClass = testCase.getClass();
- if (shouldSkip(testClass, testCase)) {
- revisedTestSuite.addTest(new SkippedTest(testCase));
- Bundle skipResult = new Bundle();
- skipResult.putBoolean("test_skipped", true);
- sendStatus(0, skipResult);
- } else {
- revisedTestSuite.addTest(testCase);
- }
- }
- super.setTest(revisedTestSuite);
- }
+ protected void run(final TestCase test) {
+ if (shouldSkip(test)) {
+ startTest(test);
- protected boolean shouldSkip(Class<?> testClass, TestCase testCase) {
+ Bundle skipResult = new Bundle();
+ skipResult.putString("class", test.getClass().getName());
+ skipResult.putString("test", test.getName());
+ skipResult.putBoolean("test_skipped", true);
+ sendStatus(0, skipResult);
+
+ endTest(test);
+ } else {
+ super.run(test);
+ }
+ }
+ }
+
+ @Override
+ protected AndroidTestRunner getAndroidTestRunner() {
+ return new AndroidTestRunner() {
+ @Override
+ protected TestResult createTestResult() {
+ SkippingTestResult r = new SkippingTestResult();
+ r.addSkipCheck(new MinAndroidSdkLevelSkipCheck());
+ return r;
+ }
+ };
+ }
+
+ /**
+ * Checks the device's SDK level against any specified minimum requirement.
+ */
+ public static class MinAndroidSdkLevelSkipCheck implements SkipCheck {
+
+ /**
+ * If {@link org.chromium.base.test.util.MinAndroidSdkLevel} is present, checks its value
+ * against the device's SDK level.
+ *
+ * @param testCase The test to check.
+ * @return true if the device's SDK level is below the specified minimum.
+ */
+ @Override
+ public boolean shouldSkip(TestCase testCase) {
+ Class<?> testClass = testCase.getClass();
if (testClass.isAnnotationPresent(MinAndroidSdkLevel.class)) {
MinAndroidSdkLevel v = testClass.getAnnotation(MinAndroidSdkLevel.class);
if (Build.VERSION.SDK_INT < v.value()) {
@@ -69,28 +124,6 @@
}
return false;
}
-
- protected Context getContext() {
- return mContext;
- }
}
- /**
- * Replaces a TestCase that should be skipped.
- */
- public static class SkippedTest extends TestCase {
-
- public SkippedTest(TestCase skipped) {
- super(skipped.getClass().getName() + "#" + skipped.getName());
- }
-
- @Override
- protected void runTest() throws Throwable {
- }
-
- @Override
- public String toString() {
- return "SKIPPED " + super.toString();
- }
- }
}
diff --git a/test/test_suite.cc b/test/test_suite.cc
index 903e93e..d40dd98 100644
--- a/test/test_suite.cc
+++ b/test/test_suite.cc
@@ -35,6 +35,14 @@
#endif // OS_IOS
#endif // OS_MACOSX
+#if !defined(OS_WIN)
+#include "base/i18n/rtl.h"
+#if !defined(OS_IOS)
+#include "base/strings/string_util.h"
+#include "third_party/icu/source/common/unicode/uloc.h"
+#endif
+#endif
+
#if defined(OS_ANDROID)
#include "base/test/test_support_android.h"
#endif
@@ -321,6 +329,22 @@
}
base::i18n::InitializeICU();
+ // On the Mac OS X command line, the default locale is *_POSIX. In Chromium,
+ // the locale is set via an OS X locale API and is never *_POSIX.
+ // Some tests (such as those involving word break iterator) will behave
+ // differently and fail if we use *POSIX locale. Setting it to en_US here
+ // does not affect tests that explicitly overrides the locale for testing.
+ // This can be an issue on all platforms other than Windows.
+ // TODO(jshin): Should we set the locale via an OS X locale API here?
+#if !defined(OS_WIN)
+#if defined(OS_IOS)
+ base::i18n::SetICUDefaultLocale("en_US");
+#else
+ std::string default_locale(uloc_getDefault());
+ if (EndsWith(default_locale, "POSIX", false))
+ base::i18n::SetICUDefaultLocale("en_US");
+#endif
+#endif
CatchMaybeTests();
ResetCommandLine();
diff --git a/time/time.h b/time/time.h
index 18de085..6d61861 100644
--- a/time/time.h
+++ b/time/time.h
@@ -178,6 +178,14 @@
return delta_ / a.delta_;
}
+ // Multiplicative computations with floats.
+ TimeDelta multiply_by(double a) const {
+ return TimeDelta(delta_ * a);
+ }
+ TimeDelta divide_by(double a) const {
+ return TimeDelta(delta_ / a);
+ }
+
// Defined below because it depends on the definition of the other classes.
Time operator+(Time t) const;
TimeTicks operator+(TimeTicks t) const;
diff --git a/time/time_unittest.cc b/time/time_unittest.cc
index fdac59d..6387ec7 100644
--- a/time/time_unittest.cc
+++ b/time/time_unittest.cc
@@ -867,6 +867,15 @@
TimeDelta::FromMicroseconds(min_int64_plus_two).magnitude());
}
+
+TEST(TimeDelta, multiply_by) {
+ double d = 0.5;
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+ TimeDelta::FromMilliseconds(1000).multiply_by(d));
+ EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+ TimeDelta::FromMilliseconds(1000).divide_by(d));
+}
+
TEST(TimeDeltaLogging, DCheckEqCompiles) {
DCHECK_EQ(TimeDelta(), TimeDelta());
}
diff --git a/trace_event/memory_dump_manager.cc b/trace_event/memory_dump_manager.cc
index bf631b3..c8be8f8 100644
--- a/trace_event/memory_dump_manager.cc
+++ b/trace_event/memory_dump_manager.cc
@@ -9,22 +9,15 @@
#include "base/compiler_specific.h"
#include "base/trace_event/memory_dump_provider.h"
#include "base/trace_event/process_memory_dump.h"
-
-// TODO(primiano): in a separate CL rename DeleteTraceLogForTesting into
-// something like base::internal::TeardownSingletonForTesting so we don't have
-// to add a new friend to singleton each time.
-class DeleteTraceLogForTesting {
- public:
- static void Delete() {
- Singleton<
- base::trace_event::MemoryDumpManager,
- LeakySingletonTraits<base::trace_event::MemoryDumpManager>>::OnExit(0);
- }
-};
+#include "base/trace_event/trace_event_argument.h"
namespace base {
namespace trace_event {
+namespace {
+MemoryDumpManager* g_instance_for_testing = nullptr;
+}
+
// TODO(primiano): this should be smarter and should do something similar to
// trace event synthetic delays.
const char MemoryDumpManager::kTraceCategory[] =
@@ -32,13 +25,16 @@
// static
MemoryDumpManager* MemoryDumpManager::GetInstance() {
+ if (g_instance_for_testing)
+ return g_instance_for_testing;
+
return Singleton<MemoryDumpManager,
LeakySingletonTraits<MemoryDumpManager>>::get();
}
// static
-void MemoryDumpManager::DeleteForTesting() {
- DeleteTraceLogForTesting::Delete();
+void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) {
+ g_instance_for_testing = instance;
}
MemoryDumpManager::MemoryDumpManager() : memory_tracing_enabled_(0) {
@@ -96,15 +92,14 @@
// Creates a dump point for the current process and appends it to the trace.
void MemoryDumpManager::CreateLocalDumpPoint() {
AutoLock lock(lock_);
- // TRACE_EVENT_* macros don't induce scoped_refptr type inference, hence we
- // need the base ConvertableToTraceFormat and the upcast below. The
- // alternative would be unnecessarily expensive (double Acquire/Release).
- scoped_refptr<ConvertableToTraceFormat> pmd(new ProcessMemoryDump());
+ scoped_ptr<ProcessMemoryDump> pmd(new ProcessMemoryDump());
for (MemoryDumpProvider* dump_provider : dump_providers_enabled_) {
- dump_provider->DumpInto(static_cast<ProcessMemoryDump*>(pmd.get()));
+ dump_provider->DumpInto(pmd.get());
}
+ scoped_refptr<TracedValue> value(new TracedValue());
+ pmd->AsValueInto(value.get());
// TODO(primiano): add the dump point to the trace at this point.
}
diff --git a/trace_event/memory_dump_manager.h b/trace_event/memory_dump_manager.h
index fbc71d5..1a22e61 100644
--- a/trace_event/memory_dump_manager.h
+++ b/trace_event/memory_dump_manager.h
@@ -50,17 +50,17 @@
void OnTraceLogDisabled() override;
private:
+ friend struct DefaultDeleter<MemoryDumpManager>; // For the testing instance.
friend struct DefaultSingletonTraits<MemoryDumpManager>;
friend class MemoryDumpManagerTest;
static const char kTraceCategory[];
+ static void SetInstanceForTesting(MemoryDumpManager* instance);
+
MemoryDumpManager();
virtual ~MemoryDumpManager();
- // Tears down the singleton instance.
- static void DeleteForTesting();
-
// Broadcasts the dump requests to the other processes.
void BroadcastDumpRequest();
diff --git a/trace_event/memory_dump_manager_unittest.cc b/trace_event/memory_dump_manager_unittest.cc
index b5337e9..1ba73e6 100644
--- a/trace_event/memory_dump_manager_unittest.cc
+++ b/trace_event/memory_dump_manager_unittest.cc
@@ -17,14 +17,16 @@
class MemoryDumpManagerTest : public testing::Test {
public:
void SetUp() override {
+ mdm_.reset(new MemoryDumpManager());
+ MemoryDumpManager::SetInstanceForTesting(mdm_.get());
+ ASSERT_EQ(mdm_, MemoryDumpManager::GetInstance());
MemoryDumpManager::GetInstance()->Initialize();
- mdm_ = MemoryDumpManager::GetInstance();
}
void TearDown() override {
- MemoryDumpManager::DeleteForTesting();
+ MemoryDumpManager::SetInstanceForTesting(nullptr);
+ mdm_.reset();
TraceLog::DeleteForTesting();
- mdm_ = NULL;
}
protected:
@@ -37,7 +39,7 @@
void DisableTracing() { TraceLog::GetInstance()->SetDisabled(); }
- MemoryDumpManager* mdm_;
+ scoped_ptr<MemoryDumpManager> mdm_;
private:
// We want our singleton torn down after each test.
diff --git a/trace_event/process_memory_dump.cc b/trace_event/process_memory_dump.cc
index 0a3e096..6da9132 100644
--- a/trace_event/process_memory_dump.cc
+++ b/trace_event/process_memory_dump.cc
@@ -4,25 +4,25 @@
#include "base/trace_event/process_memory_dump.h"
-#include "base/json/json_writer.h"
-#include "base/values.h"
+#include "base/trace_event/process_memory_totals.h"
+#include "base/trace_event/trace_event_argument.h"
namespace base {
namespace trace_event {
-ProcessMemoryDump::ProcessMemoryDump() {
+ProcessMemoryDump::ProcessMemoryDump() : has_process_totals_(false) {
}
ProcessMemoryDump::~ProcessMemoryDump() {
}
-void ProcessMemoryDump::AppendAsTraceFormat(std::string* out) const {
- // Build up the [dumper name] -> [serialized snapshot] JSON dictionary.
- DictionaryValue dict;
- std::string json_dict;
- // TODO(primiano): this will append here the actual dumps from the dumpers.
- base::JSONWriter::Write(&dict, &json_dict);
- *out += json_dict;
+void ProcessMemoryDump::AsValueInto(TracedValue* value) const {
+ // Build up the [dumper name] -> [value] dictionary.
+ if (has_process_totals_) {
+ value->BeginDictionary("process_totals");
+ process_totals_.AsValueInto(value);
+ value->EndDictionary();
+ }
}
} // namespace trace_event
diff --git a/trace_event/process_memory_dump.h b/trace_event/process_memory_dump.h
index ae42987..f70537b 100644
--- a/trace_event/process_memory_dump.h
+++ b/trace_event/process_memory_dump.h
@@ -6,27 +6,34 @@
#define BASE_TRACE_EVENT_PROCESS_MEMORY_DUMP_H_
#include "base/base_export.h"
-#include "base/basictypes.h"
-#include "base/trace_event/trace_event_impl.h"
+#include "base/trace_event/process_memory_totals.h"
namespace base {
namespace trace_event {
-// A container which holds the dumps produced by the MemoryDumpProvider(s)
-// for a specific process. ProcessMemoryDump is as a strongly typed container
-// which enforces the data model for each memory dump point.
-// At trace generation time (i.e. when AppendAsTraceFormat is called) the
-// ProcessMemoryDump will compose a key-value dictionary of the various dumps
-// obtained during at trace dump point time.
-class BASE_EXPORT ProcessMemoryDump : public ConvertableToTraceFormat {
+class ConvertableToTraceFormat;
+
+// ProcessMemoryDump is as a strongly typed container which enforces the data
+// model for each memory dump point and holds the dumps produced by the
+// MemoryDumpProvider(s) for a specific process.
+// At trace generation time (i.e. when AsValue() is called), ProcessMemoryDump
+// will compose a key-value dictionary of the various dumps obtained at trace
+// dump point time.
+class BASE_EXPORT ProcessMemoryDump {
public:
ProcessMemoryDump();
+ ~ProcessMemoryDump();
- // ConvertableToTraceFormat implementation.
- void AppendAsTraceFormat(std::string* out) const override;
+ // Called at trace generation time to populate the TracedValue.
+ void AsValueInto(TracedValue* value) const;
+
+ ProcessMemoryTotals* process_totals() { return &process_totals_; }
+ bool has_process_totals() const { return has_process_totals_; }
+ void set_has_process_totals() { has_process_totals_ = true; }
private:
- ~ProcessMemoryDump() override;
+ ProcessMemoryTotals process_totals_;
+ bool has_process_totals_;
DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDump);
};
diff --git a/trace_event/process_memory_totals.cc b/trace_event/process_memory_totals.cc
new file mode 100644
index 0000000..41ad788
--- /dev/null
+++ b/trace_event/process_memory_totals.cc
@@ -0,0 +1,17 @@
+// 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 "base/trace_event/process_memory_totals.h"
+
+#include "base/trace_event/trace_event_argument.h"
+
+namespace base {
+namespace trace_event {
+
+void ProcessMemoryTotals::AsValueInto(TracedValue* value) const {
+ value->SetDouble("resident_set_bytes", resident_set_bytes_);
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/trace_event/process_memory_totals.h b/trace_event/process_memory_totals.h
new file mode 100644
index 0000000..1c99152
--- /dev/null
+++ b/trace_event/process_memory_totals.h
@@ -0,0 +1,36 @@
+// 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 BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_H_
+#define BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace trace_event {
+
+class TracedValue;
+
+// Dump provider which collects process-wide memory stats.
+class BASE_EXPORT ProcessMemoryTotals {
+ public:
+ ProcessMemoryTotals() {}
+
+ // Called at trace generation time to populate the TracedValue.
+ void AsValueInto(TracedValue* value) const;
+
+ uint64 resident_set_bytes() const { return resident_set_bytes_; }
+ void set_resident_set_bytes(uint64 value) { resident_set_bytes_ = value; }
+
+ private:
+ uint64 resident_set_bytes_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessMemoryTotals);
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_H_
diff --git a/trace_event/process_memory_totals_dump_provider.cc b/trace_event/process_memory_totals_dump_provider.cc
new file mode 100644
index 0000000..cda0ff1
--- /dev/null
+++ b/trace_event/process_memory_totals_dump_provider.cc
@@ -0,0 +1,53 @@
+// 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 "base/trace_event/process_memory_totals_dump_provider.h"
+
+#include "base/process/process_metrics.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/process_memory_totals.h"
+
+namespace base {
+namespace trace_event {
+
+// static
+uint64 ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 0;
+
+namespace {
+ProcessMetrics* CreateProcessMetricsForCurrentProcess() {
+#if !defined(OS_MACOSX) || defined(OS_IOS)
+ return ProcessMetrics::CreateProcessMetrics(GetCurrentProcessHandle());
+#else
+ return ProcessMetrics::CreateProcessMetrics(GetCurrentProcessHandle(), NULL);
+#endif
+}
+} // namespace
+
+// static
+ProcessMemoryTotalsDumpProvider*
+ProcessMemoryTotalsDumpProvider::GetInstance() {
+ return Singleton<
+ ProcessMemoryTotalsDumpProvider,
+ LeakySingletonTraits<ProcessMemoryTotalsDumpProvider>>::get();
+}
+
+ProcessMemoryTotalsDumpProvider::ProcessMemoryTotalsDumpProvider()
+ : process_metrics_(CreateProcessMetricsForCurrentProcess()) {
+}
+
+ProcessMemoryTotalsDumpProvider::~ProcessMemoryTotalsDumpProvider() {
+}
+
+// Called at trace dump point time. Creates a snapshot the memory counters for
+// the current process.
+void ProcessMemoryTotalsDumpProvider::DumpInto(ProcessMemoryDump* pmd) {
+ const uint64 rss_bytes = rss_bytes_for_testing
+ ? rss_bytes_for_testing
+ : process_metrics_->GetWorkingSetSize();
+ pmd->process_totals()->set_resident_set_bytes(rss_bytes);
+ pmd->set_has_process_totals();
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/trace_event/process_memory_totals_dump_provider.h b/trace_event/process_memory_totals_dump_provider.h
new file mode 100644
index 0000000..45917a8
--- /dev/null
+++ b/trace_event/process_memory_totals_dump_provider.h
@@ -0,0 +1,44 @@
+// 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 BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_
+#define BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/trace_event/memory_dump_provider.h"
+
+namespace base {
+
+class ProcessMetrics;
+
+namespace trace_event {
+
+// Dump provider which collects process-wide memory stats.
+class BASE_EXPORT ProcessMemoryTotalsDumpProvider : public MemoryDumpProvider {
+ public:
+ static ProcessMemoryTotalsDumpProvider* GetInstance();
+
+ // MemoryDumpProvider implementation.
+ void DumpInto(ProcessMemoryDump* pmd) override;
+
+ private:
+ friend struct DefaultSingletonTraits<ProcessMemoryTotalsDumpProvider>;
+ FRIEND_TEST_ALL_PREFIXES(ProcessMemoryTotalsDumpProviderTest, DumpRSS);
+
+ static uint64 rss_bytes_for_testing;
+
+ ProcessMemoryTotalsDumpProvider();
+ ~ProcessMemoryTotalsDumpProvider() override;
+
+ scoped_ptr<ProcessMetrics> process_metrics_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessMemoryTotalsDumpProvider);
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_
diff --git a/trace_event/process_memory_totals_dump_provider_unittest.cc b/trace_event/process_memory_totals_dump_provider_unittest.cc
new file mode 100644
index 0000000..4a60036
--- /dev/null
+++ b/trace_event/process_memory_totals_dump_provider_unittest.cc
@@ -0,0 +1,43 @@
+// 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 "base/trace_event/process_memory_totals_dump_provider.h"
+
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/process_memory_totals.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+TEST(ProcessMemoryTotalsDumpProviderTest, DumpRSS) {
+ auto mdptp = ProcessMemoryTotalsDumpProvider::GetInstance();
+ scoped_ptr<ProcessMemoryDump> pmd_before(new ProcessMemoryDump());
+ scoped_ptr<ProcessMemoryDump> pmd_after(new ProcessMemoryDump());
+
+ ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 1024;
+ mdptp->DumpInto(pmd_before.get());
+
+ // Pretend that the RSS of the process increased of +1M.
+ const size_t kAllocSize = 1048576;
+ ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing += kAllocSize;
+
+ mdptp->DumpInto(pmd_after.get());
+
+ ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 0;
+
+ ASSERT_TRUE(pmd_before->has_process_totals());
+ ASSERT_TRUE(pmd_after->has_process_totals());
+
+ const uint64 rss_before = pmd_before->process_totals()->resident_set_bytes();
+ const uint64 rss_after = pmd_after->process_totals()->resident_set_bytes();
+
+ EXPECT_NE(0U, rss_before);
+ EXPECT_NE(0U, rss_after);
+
+ EXPECT_EQ(rss_after - rss_before, kAllocSize);
+}
+
+} // namespace trace_Event
+} // namespace base
diff --git a/trace_event/trace_event_synthetic_delay.cc b/trace_event/trace_event_synthetic_delay.cc
index 4b957c3..bad79cc 100644
--- a/trace_event/trace_event_synthetic_delay.cc
+++ b/trace_event/trace_event_synthetic_delay.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/memory/singleton.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/trace_event/trace_event_synthetic_delay.h"
namespace {
diff --git a/version.cc b/version.cc
index 6318b35..933356e 100644
--- a/version.cc
+++ b/version.cc
@@ -31,6 +31,8 @@
for (std::vector<std::string>::const_iterator it = numbers.begin();
it != numbers.end(); ++it) {
+ if (StartsWithASCII(*it, "+", false))
+ return false;
int num;
if (!StringToInt(*it, &num))
return false;
@@ -42,8 +44,8 @@
if (num > max)
return false;
- // This throws out things like +3, or 032.
- if (IntToString(num) != *it)
+ // This throws out leading zeros for the first item only.
+ if (it == numbers.begin() && IntToString(num) != *it)
return false;
parsed->push_back(static_cast<uint16>(num));
diff --git a/version_unittest.cc b/version_unittest.cc
index 3119c39..46d8255 100644
--- a/version_unittest.cc
+++ b/version_unittest.cc
@@ -41,16 +41,22 @@
{".", 0, false},
{" . ", 0, false},
{"0", 1, true},
+ {"0.", 0, false},
{"0.0", 2, true},
{"65537.0", 0, false},
{"-1.0", 0, false},
{"1.-1.0", 0, false},
+ {"1,--1.0", 0, false},
{"+1.0", 0, false},
{"1.+1.0", 0, false},
+ {"1+1.0", 0, false},
+ {"++1.0", 0, false},
{"1.0a", 0, false},
{"1.2.3.4.5.6.7.8.9.0", 10, true},
{"02.1", 0, false},
+ {"0.01", 2, true},
{"f.1", 0, false},
+ {"15.007.20011", 3, true},
};
for (size_t i = 0; i < arraysize(cases); ++i) {
@@ -77,6 +83,7 @@
{"1.1", "1.0.1", 1},
{"1.0.0", "1.0", 0},
{"1.0.3", "1.0.20", -1},
+ {"11.0.10", "15.007.20011", -1},
};
for (size_t i = 0; i < arraysize(cases); ++i) {
Version lhs(cases[i].lhs);
diff --git a/win/pe_image.cc b/win/pe_image.cc
index 572b4d9..e226b6a 100644
--- a/win/pe_image.cc
+++ b/win/pe_image.cc
@@ -20,6 +20,9 @@
namespace {
+ // PdbInfo Signature
+ const DWORD kPdbInfoSignature = 'SDSR';
+
// Compare two strings byte by byte on an unsigned basis.
// if s1 == s2, return 0
// if s1 < s2, return negative
@@ -35,6 +38,12 @@
*reinterpret_cast<const unsigned char*>(s2));
}
+ struct PdbInfo {
+ DWORD Signature;
+ GUID Guid;
+ DWORD Age;
+ char PdbFileName[1];
+ };
} // namespace
// Callback used to enumerate imports. See EnumImportChunksFunction.
@@ -142,6 +151,36 @@
return ret;
}
+bool PEImage::GetDebugId(LPGUID guid, LPDWORD age) const {
+ if (NULL == guid || NULL == age) {
+ return false;
+ }
+
+ DWORD debug_directory_size =
+ GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DEBUG);
+ PIMAGE_DEBUG_DIRECTORY debug_directory =
+ reinterpret_cast<PIMAGE_DEBUG_DIRECTORY>(
+ GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_DEBUG));
+
+ size_t directory_count =
+ debug_directory_size / sizeof(IMAGE_DEBUG_DIRECTORY);
+
+ for (size_t index = 0; index < directory_count; ++index) {
+ if (debug_directory[index].Type == IMAGE_DEBUG_TYPE_CODEVIEW) {
+ PdbInfo* pdb_info = reinterpret_cast<PdbInfo*>(
+ RVAToAddr(debug_directory[index].AddressOfRawData));
+ if (pdb_info->Signature != kPdbInfoSignature) {
+ // Unsupported PdbInfo signature
+ return false;
+ }
+ *guid = pdb_info->Guid;
+ *age = pdb_info->Age;
+ return true;
+ }
+ }
+ return false;
+}
+
PDWORD PEImage::GetExportEntry(LPCSTR name) const {
PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
diff --git a/win/pe_image.h b/win/pe_image.h
index dde1b48..5cef537 100644
--- a/win/pe_image.h
+++ b/win/pe_image.h
@@ -132,6 +132,9 @@
// Returns the exports directory.
PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const;
+ // Returns the debug id (guid+age).
+ bool GetDebugId(LPGUID guid, LPDWORD age) const;
+
// Returns a given export entry.
// Use: e = image.GetExportEntry(f);
// Pre: 'f' is either a zero terminated string or ordinal
diff --git a/win/pe_image_unittest.cc b/win/pe_image_unittest.cc
index 238c924..af4209b 100644
--- a/win/pe_image_unittest.cc
+++ b/win/pe_image_unittest.cc
@@ -267,5 +267,21 @@
FreeLibrary(module);
}
+// Test that we can get debug id out of a module.
+TEST(PEImageTest, GetDebugId) {
+ HMODULE module = LoadLibrary(L"advapi32.dll");
+ ASSERT_TRUE(NULL != module);
+
+ PEImage pe(module);
+ GUID guid = {0};
+ DWORD age = 0;
+ EXPECT_TRUE(pe.GetDebugId(&guid, &age));
+
+ GUID empty_guid = {0};
+ EXPECT_TRUE(!IsEqualGUID(empty_guid, guid));
+ EXPECT_NE(0U, age);
+ FreeLibrary(module);
+}
+
} // namespace win
} // namespace base