Update from https://crrev.com/318214
TBR=qsr@chromium.org
Review URL: https://codereview.chromium.org/960873002
diff --git a/base/BUILD.gn b/base/BUILD.gn
index c63d377..9dbc41c 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -310,6 +310,7 @@
"mac/scoped_sending_event.h",
"mac/scoped_sending_event.mm",
"mac/sdk_forward_declarations.h",
+ "mac/sdk_forward_declarations.mm",
"macros.h",
"md5.cc",
"md5.h",
@@ -675,6 +676,10 @@
"trace_event/memory_dump_provider.h",
"trace_event/process_memory_dump.cc",
"trace_event/process_memory_dump.h",
+ "trace_event/process_memory_maps.cc",
+ "trace_event/process_memory_maps.h",
+ "trace_event/process_memory_maps_dump_provider.cc",
+ "trace_event/process_memory_maps_dump_provider.h",
"trace_event/process_memory_totals.cc",
"trace_event/process_memory_totals.h",
"trace_event/process_memory_totals_dump_provider.cc",
@@ -759,7 +764,29 @@
]
if (is_nacl) {
- sources += [ "files/file_path_watcher_stub.cc" ]
+ # We reset sources_assignment_filter in order to explicitly include
+ # the linux file (which would otherwise be filtered out).
+ set_sources_assignment_filter([])
+ sources += [
+ "files/file_path_watcher_stub.cc",
+ "sync_socket_nacl.cc",
+ "threading/platform_thread_linux.cc",
+ ]
+ set_sources_assignment_filter(sources_assignment_filter)
+
+ sources -= [
+ "allocator/type_profiler_control.cc",
+ "allocator/type_profiler_control.h",
+ "async_socket_io_handler_posix.cc",
+ "base_paths.cc",
+ "cpu.cc",
+ "files/file_proxy.cc",
+ "files/file_util.cc",
+ "files/file_util_proxy.cc",
+ "path_service.cc",
+ "scoped_native_library.cc",
+ "files/scoped_temp_dir.cc",
+ ]
}
sources -= [
@@ -847,11 +874,12 @@
"process/launch_posix.cc",
"process/process_metrics_posix.cc",
"process/process_posix.cc",
+ "rand_util_posix.cc",
"sync_socket_posix.cc",
"sys_info_posix.cc",
]
} else {
- # Remove nacl stuff.
+ # Remove NaCl stuff.
sources -= [
"memory/shared_memory_nacl.cc",
"os_compat_nacl.cc",
@@ -1134,6 +1162,23 @@
]
}
+if (is_win) {
+ shared_library("pe_image_test") {
+ sources = [
+ "win/pe_image_test.cc",
+ ]
+ ldflags = [
+ "/DELAYLOAD:cfgmgr32.dll",
+ "/DELAYLOAD:shell32.dll",
+ "/SUBSYSTEM:WINDOWS",
+ ]
+ libs = [
+ "cfgmgr32.lib",
+ "shell32.lib",
+ ]
+ }
+}
+
test("base_unittests") {
sources = [
"android/application_status_listener_unittest.cc",
@@ -1336,6 +1381,7 @@
"timer/timer_unittest.cc",
"tools_sanity_unittest.cc",
"trace_event/memory_dump_manager_unittest.cc",
+ "trace_event/process_memory_maps_dump_provider_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",
@@ -1441,6 +1487,10 @@
set_sources_assignment_filter(sources_assignment_filter)
}
+ if (is_win) {
+ deps += [ ":pe_image_test" ]
+ }
+
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
}
diff --git a/base/android/java/src/org/chromium/base/ObserverList.java b/base/android/java/src/org/chromium/base/ObserverList.java
index e812b0d..7a2ab98 100644
--- a/base/android/java/src/org/chromium/base/ObserverList.java
+++ b/base/android/java/src/org/chromium/base/ObserverList.java
@@ -46,6 +46,7 @@
public final List<E> mObservers = new ArrayList<E>();
private int mIterationDepth = 0;
private int mCount = 0;
+ private boolean mNeedsCompact = false;
public ObserverList() {}
@@ -91,6 +92,7 @@
// No one is iterating over the list.
mObservers.remove(index);
} else {
+ mNeedsCompact = true;
mObservers.set(index, null);
}
--mCount;
@@ -112,6 +114,7 @@
}
int size = mObservers.size();
+ mNeedsCompact |= size != 0;
for (int i = 0; i < size; i++) {
mObservers.set(i, null);
}
@@ -167,7 +170,10 @@
private void decrementIterationDepthAndCompactIfNeeded() {
mIterationDepth--;
assert mIterationDepth >= 0;
- if (mIterationDepth == 0) compact();
+ if (mIterationDepth > 0) return;
+ if (!mNeedsCompact) return;
+ mNeedsCompact = false;
+ compact();
}
/**
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index d68fb4a..c7d8527 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -42,7 +42,7 @@
private static final Object sLock = new Object();
// The singleton instance of LibraryLoader.
- private static LibraryLoader sInstance;
+ private static volatile LibraryLoader sInstance;
// One-way switch becomes true when the libraries are loaded.
private boolean mLoaded;
@@ -54,7 +54,9 @@
// One-way switch becomes true when the libraries are initialized (
// by calling nativeLibraryLoaded, which forwards to LibraryLoaded(...) in
// library_loader_hooks.cc).
- private boolean mInitialized;
+ // Note that this member should remain a one-way switch, since it accessed from multiple
+ // threads without a lock.
+ private volatile boolean mInitialized;
// One-way switches recording attempts to use Relro sharing in the browser.
// The flags are used to report UMA stats later.
@@ -81,7 +83,9 @@
private boolean mLibraryIsMappableInApk = true;
// The type of process the shared library is loaded in.
- private int mLibraryProcessType;
+ // This member can be accessed from multiple threads simultaneously, so it have to be
+ // final (like now) or be protected in some way (volatile of synchronized).
+ private final int mLibraryProcessType;
/**
* @param libraryProcessType the process the shared library is loaded in. refer to
@@ -141,9 +145,7 @@
* Checks if library is fully loaded and initialized.
*/
public static boolean isInitialized() {
- synchronized (sLock) {
- return sInstance != null && sInstance.mInitialized;
- }
+ return sInstance != null && sInstance.mInitialized;
}
/**
@@ -377,10 +379,6 @@
Log.e(TAG, "error calling nativeLibraryLoaded");
throw new ProcessInitException(LoaderErrors.LOADER_ERROR_FAILED_TO_REGISTER_JNI);
}
- // From this point on, native code is ready to use and checkIsReady()
- // shouldn't complain from now on (and in fact, it's used by the
- // following calls).
- mInitialized = true;
// The Chrome JNI is registered by now so we can switch the Java
// command line over to delegating to native if it's necessary.
@@ -391,6 +389,13 @@
// From now on, keep tracing in sync with native.
TraceEvent.registerNativeEnabledObserver();
+
+ // From this point on, native code is ready to use and checkIsReady()
+ // shouldn't complain from now on (and in fact, it's used by the
+ // following calls).
+ // Note that this flag can be accessed asynchronously, so any initialization
+ // must be performed before.
+ mInitialized = true;
}
// Called after all native initializations are complete.
@@ -455,10 +460,8 @@
*/
@CalledByNative
public static int getLibraryProcessType() {
- synchronized (sLock) {
- if (sInstance == null) return LibraryProcessType.PROCESS_UNINITIALIZED;
- return sInstance.mLibraryProcessType;
- }
+ if (sInstance == null) return LibraryProcessType.PROCESS_UNINITIALIZED;
+ return sInstance.mLibraryProcessType;
}
private native void nativeInitCommandLine(String[] initCommandLine);
diff --git a/base/android/jni_generator/golden_sample_for_tests_jni.h b/base/android/jni_generator/golden_sample_for_tests_jni.h
index 0fcdc69..ba7494e 100644
--- a/base/android/jni_generator/golden_sample_for_tests_jni.h
+++ b/base/android/jni_generator/golden_sample_for_tests_jni.h
@@ -373,6 +373,7 @@
};
static bool RegisterNativesImpl(JNIEnv* env) {
+
g_InnerStructA_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
base::android::GetClass(env, kInnerStructAClassPath).obj()));
g_SampleForTests_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py
index 54fea6b..fd03f0e 100755
--- a/base/android/jni_generator/jni_generator.py
+++ b/base/android/jni_generator/jni_generator.py
@@ -922,7 +922,8 @@
"""Returns the code for RegisterNatives."""
template = Template("""\
${REGISTER_NATIVES_SIGNATURE} {
-${EARLY_EXIT}${CLASSES}
+${EARLY_EXIT}
+${CLASSES}
${NATIVES}
${CALLED_BY_NATIVES}
return true;
@@ -1073,8 +1074,10 @@
"""
if self.options.native_exports:
template_str += """
-__attribute__((visibility("default"), alias("${NAME}")))
-${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS});
+__attribute__((visibility("default")))
+${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS}) {
+ return ${NAME}(${PARAMS_IN_CALL});
+}
"""
template = Template(template_str)
params_in_call = []
@@ -1312,7 +1315,7 @@
if self.init_native:
if self.options.native_exports:
template = Template("""\
- base::subtle::Release_Store(&g_${JAVA_CLASS}_clazz,
+ base::subtle::Release_Store(&g_${JAVA_CLASS}_clazz,
static_cast<base::subtle::AtomicWord>(env->NewWeakGlobalRef(clazz));""")
else:
template = Template("""\
diff --git a/base/android/jni_generator/testCalledByNatives.golden b/base/android/jni_generator/testCalledByNatives.golden
index 22aa45d..e33356a 100644
--- a/base/android/jni_generator/testCalledByNatives.golden
+++ b/base/android/jni_generator/testCalledByNatives.golden
@@ -498,6 +498,7 @@
// Step 3: RegisterNatives.
static bool RegisterNativesImpl(JNIEnv* env) {
+
g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
base::android::GetClass(env, kTestJniClassPath).obj()));
g_InfoBar_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
diff --git a/base/android/jni_generator/testConstantsFromJavaP.golden b/base/android/jni_generator/testConstantsFromJavaP.golden
index f122529..1c816bc 100644
--- a/base/android/jni_generator/testConstantsFromJavaP.golden
+++ b/base/android/jni_generator/testConstantsFromJavaP.golden
@@ -2221,6 +2221,7 @@
// Step 3: RegisterNatives.
static bool RegisterNativesImpl(JNIEnv* env) {
+
g_MotionEvent_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
base::android::GetClass(env, kMotionEventClassPath).obj()));
diff --git a/base/android/jni_generator/testEagerCalledByNativesOption.golden b/base/android/jni_generator/testEagerCalledByNativesOption.golden
index 6c1323e..19108bf 100644
--- a/base/android/jni_generator/testEagerCalledByNativesOption.golden
+++ b/base/android/jni_generator/testEagerCalledByNativesOption.golden
@@ -79,6 +79,7 @@
};
static bool RegisterNativesImpl(JNIEnv* env, jclass clazz) {
+
g_Test_clazz = static_cast<jclass>(env->NewWeakGlobalRef(clazz));
const int kMethodsTestSize = arraysize(kMethodsTest);
diff --git a/base/android/jni_generator/testFromJavaP.golden b/base/android/jni_generator/testFromJavaP.golden
index 5827410..b7276bc 100644
--- a/base/android/jni_generator/testFromJavaP.golden
+++ b/base/android/jni_generator/testFromJavaP.golden
@@ -255,6 +255,7 @@
// Step 3: RegisterNatives.
static bool RegisterNativesImpl(JNIEnv* env) {
+
g_InputStream_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
base::android::GetClass(env, kInputStreamClassPath).obj()));
diff --git a/base/android/jni_generator/testFromJavaPGenerics.golden b/base/android/jni_generator/testFromJavaPGenerics.golden
index 5d78390..489872c 100644
--- a/base/android/jni_generator/testFromJavaPGenerics.golden
+++ b/base/android/jni_generator/testFromJavaPGenerics.golden
@@ -53,6 +53,7 @@
// Step 3: RegisterNatives.
static bool RegisterNativesImpl(JNIEnv* env) {
+
g_HashSet_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
base::android::GetClass(env, kHashSetClassPath).obj()));
diff --git a/base/android/jni_generator/testInnerClassNatives.golden b/base/android/jni_generator/testInnerClassNatives.golden
index 2dee84e..5a525ef 100644
--- a/base/android/jni_generator/testInnerClassNatives.golden
+++ b/base/android/jni_generator/testInnerClassNatives.golden
@@ -40,6 +40,7 @@
};
static bool RegisterNativesImpl(JNIEnv* env) {
+
g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
base::android::GetClass(env, kTestJniClassPath).obj()));
diff --git a/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden b/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden
index 6ffbaac..c8d4b3c 100644
--- a/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden
+++ b/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden
@@ -50,6 +50,7 @@
};
static bool RegisterNativesImpl(JNIEnv* env) {
+
g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
base::android::GetClass(env, kTestJniClassPath).obj()));
diff --git a/base/android/jni_generator/testInnerClassNativesMultiple.golden b/base/android/jni_generator/testInnerClassNativesMultiple.golden
index b74e65f..42643ae 100644
--- a/base/android/jni_generator/testInnerClassNativesMultiple.golden
+++ b/base/android/jni_generator/testInnerClassNativesMultiple.golden
@@ -51,6 +51,7 @@
};
static bool RegisterNativesImpl(JNIEnv* env) {
+
g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
base::android::GetClass(env, kTestJniClassPath).obj()));
diff --git a/base/android/jni_generator/testJNIInitNativeNameOption.golden b/base/android/jni_generator/testJNIInitNativeNameOption.golden
index 53b5f17..a0998da 100644
--- a/base/android/jni_generator/testJNIInitNativeNameOption.golden
+++ b/base/android/jni_generator/testJNIInitNativeNameOption.golden
@@ -46,6 +46,7 @@
};
static bool RegisterNativesImpl(JNIEnv* env, jclass clazz) {
+
g_Test_clazz = static_cast<jclass>(env->NewWeakGlobalRef(clazz));
const int kMethodsTestSize = arraysize(kMethodsTest);
diff --git a/base/android/jni_generator/testJarJarRemapping.golden b/base/android/jni_generator/testJarJarRemapping.golden
index 75a35c5..2f85122 100644
--- a/base/android/jni_generator/testJarJarRemapping.golden
+++ b/base/android/jni_generator/testJarJarRemapping.golden
@@ -65,6 +65,7 @@
};
static bool RegisterNativesImpl(JNIEnv* env) {
+
g_Example_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
base::android::GetClass(env, kExampleClassPath).obj()));
diff --git a/base/android/jni_generator/testMultipleJNIAdditionalImport.golden b/base/android/jni_generator/testMultipleJNIAdditionalImport.golden
index df5b1c8..b0db9dd 100644
--- a/base/android/jni_generator/testMultipleJNIAdditionalImport.golden
+++ b/base/android/jni_generator/testMultipleJNIAdditionalImport.golden
@@ -68,6 +68,7 @@
};
static bool RegisterNativesImpl(JNIEnv* env) {
+
g_Foo_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
base::android::GetClass(env, kFooClassPath).obj()));
diff --git a/base/android/jni_generator/testNativeExportsOption.golden b/base/android/jni_generator/testNativeExportsOption.golden
index a4953cc..395fc39 100644
--- a/base/android/jni_generator/testNativeExportsOption.golden
+++ b/base/android/jni_generator/testNativeExportsOption.golden
@@ -30,17 +30,21 @@
static jint Init(JNIEnv* env, jobject jcaller);
-__attribute__((visibility("default"), alias("Init")))
+__attribute__((visibility("default")))
jint
Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit(JNIEnv*
- env, jobject jcaller);
+ env, jobject jcaller) {
+ return Init(env, jcaller);
+}
static jint Init(JNIEnv* env, jobject jcaller);
-__attribute__((visibility("default"), alias("Init")))
+__attribute__((visibility("default")))
jint
Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit(JNIEnv*
- env, jobject jcaller);
+ env, jobject jcaller) {
+ return Init(env, jcaller);
+}
}; // extern "C"
@@ -199,7 +203,8 @@
// Step 3: RegisterNatives.
static bool RegisterNativesImpl(JNIEnv* env, jclass clazz) {
- base::subtle::Release_Store(&g_SampleForTests_clazz,
+
+ base::subtle::Release_Store(&g_SampleForTests_clazz,
static_cast<base::subtle::AtomicWord>(env->NewWeakGlobalRef(clazz));
return true;
diff --git a/base/android/jni_generator/testNativeExportsOptionalOption.golden b/base/android/jni_generator/testNativeExportsOptionalOption.golden
index 2a3b172..f47cb98 100644
--- a/base/android/jni_generator/testNativeExportsOptionalOption.golden
+++ b/base/android/jni_generator/testNativeExportsOptionalOption.golden
@@ -30,17 +30,21 @@
static jint Init(JNIEnv* env, jobject jcaller);
-__attribute__((visibility("default"), alias("Init")))
+__attribute__((visibility("default")))
jint
Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit(JNIEnv*
- env, jobject jcaller);
+ env, jobject jcaller) {
+ return Init(env, jcaller);
+}
static jint Init(JNIEnv* env, jobject jcaller);
-__attribute__((visibility("default"), alias("Init")))
+__attribute__((visibility("default")))
jint
Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit(JNIEnv*
- env, jobject jcaller);
+ env, jobject jcaller) {
+ return Init(env, jcaller);
+}
}; // extern "C"
@@ -237,7 +241,8 @@
static bool RegisterNativesImpl(JNIEnv* env, jclass clazz) {
if (base::android::IsManualJniRegistrationDisabled()) return true;
- base::subtle::Release_Store(&g_SampleForTests_clazz,
+
+ base::subtle::Release_Store(&g_SampleForTests_clazz,
static_cast<base::subtle::AtomicWord>(env->NewWeakGlobalRef(clazz));
const int kMethodsMyOtherInnerClassSize =
diff --git a/base/android/jni_generator/testNatives.golden b/base/android/jni_generator/testNatives.golden
index 8708fa2..e5a4fab 100644
--- a/base/android/jni_generator/testNatives.golden
+++ b/base/android/jni_generator/testNatives.golden
@@ -198,6 +198,7 @@
};
static bool RegisterNativesImpl(JNIEnv* env) {
+
g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
base::android::GetClass(env, kTestJniClassPath).obj()));
diff --git a/base/android/jni_generator/testNativesLong.golden b/base/android/jni_generator/testNativesLong.golden
index 11e7c49..5fa901c 100644
--- a/base/android/jni_generator/testNativesLong.golden
+++ b/base/android/jni_generator/testNativesLong.golden
@@ -45,6 +45,7 @@
};
static bool RegisterNativesImpl(JNIEnv* env) {
+
g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
base::android::GetClass(env, kTestJniClassPath).obj()));
diff --git a/base/android/jni_generator/testPureNativeMethodsOption.golden b/base/android/jni_generator/testPureNativeMethodsOption.golden
index a45a386..ad63cca 100644
--- a/base/android/jni_generator/testPureNativeMethodsOption.golden
+++ b/base/android/jni_generator/testPureNativeMethodsOption.golden
@@ -46,6 +46,7 @@
};
static bool RegisterNativesImpl(JNIEnv* env) {
+
g_Test_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
base::android::GetClass(env, kTestClassPath).obj()));
diff --git a/base/android/jni_generator/testSingleJNIAdditionalImport.golden b/base/android/jni_generator/testSingleJNIAdditionalImport.golden
index 787f7f5..1cf6554 100644
--- a/base/android/jni_generator/testSingleJNIAdditionalImport.golden
+++ b/base/android/jni_generator/testSingleJNIAdditionalImport.golden
@@ -64,6 +64,7 @@
};
static bool RegisterNativesImpl(JNIEnv* env) {
+
g_Foo_clazz = reinterpret_cast<jclass>(env->NewGlobalRef(
base::android::GetClass(env, kFooClassPath).obj()));
diff --git a/base/android/library_loader/library_loader_hooks.h b/base/android/library_loader/library_loader_hooks.h
index 6203eb0..7e8d527 100644
--- a/base/android/library_loader/library_loader_hooks.h
+++ b/base/android/library_loader/library_loader_hooks.h
@@ -62,7 +62,7 @@
BASE_EXPORT void LibraryLoaderExitHook();
// Return the process type the shared library is loaded in.
-BASE_EXPORT LibraryProcessType GetLibraryProcesssType();
+BASE_EXPORT LibraryProcessType GetLibraryProcessType(JNIEnv* env);
} // namespace android
} // namespace base
diff --git a/base/base.gyp b/base/base.gyp
index 213e62d..30de275 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -188,6 +188,14 @@
},
},
},
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)/',
+ 'files': [
+ '../build/win/dbghelp_xp/dbghelp.dll',
+ ],
+ },
+ ],
}],
['OS == "mac" or (OS == "ios" and _toolset == "host")', {
'link_settings': {
@@ -645,6 +653,7 @@
'timer/timer_unittest.cc',
'tools_sanity_unittest.cc',
'trace_event/memory_dump_manager_unittest.cc',
+ 'trace_event/process_memory_maps_dump_provider_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',
@@ -769,6 +778,9 @@
'message_loop/message_pump_libevent_unittest.cc',
'threading/worker_pool_posix_unittest.cc',
],
+ 'dependencies': [
+ 'pe_image_test',
+ ],
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
'msvs_disabled_warnings': [
4267,
@@ -876,6 +888,7 @@
'base_i18n',
'../testing/gmock.gyp:gmock',
'../testing/gtest.gyp:gtest',
+ '../third_party/icu/icu.gyp:icuuc',
'../third_party/libxml/libxml.gyp:libxml',
'third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
],
@@ -1537,6 +1550,26 @@
},
},
},
+ {
+ 'target_name': 'pe_image_test',
+ 'type': 'shared_library',
+ 'sources': [
+ 'win/pe_image_test.cc',
+ ],
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS
+ 'DelayLoadDLLs': [
+ 'cfgmgr32.dll',
+ 'shell32.dll',
+ ],
+ 'AdditionalDependencies': [
+ 'cfgmgr32.lib',
+ 'shell32.lib',
+ ],
+ },
+ },
+ },
],
}],
['test_isolation_mode != "noop"', {
diff --git a/base/base.gypi b/base/base.gypi
index b7c33b8..148246f 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -671,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_maps.cc',
+ 'trace_event/process_memory_maps.h',
+ 'trace_event/process_memory_maps_dump_provider.cc',
+ 'trace_event/process_memory_maps_dump_provider.h',
'trace_event/process_memory_totals.cc',
'trace_event/process_memory_totals.h',
'trace_event/process_memory_totals_dump_provider.cc',
diff --git a/base/base.isolate b/base/base.isolate
index cb85965..a245819 100644
--- a/base/base.isolate
+++ b/base/base.isolate
@@ -30,6 +30,14 @@
],
},
}],
+ ['OS=="win"', {
+ # Required for base/stack_trace_win.cc to symbolize correctly.
+ 'variables': {
+ 'files': [
+ '<(PRODUCT_DIR)/dbghelp.dll',
+ ],
+ },
+ }],
['OS=="win" and asan==1 and component=="shared_library"', {
'variables': {
'files': [
@@ -53,13 +61,6 @@
],
},
}],
- ['lsan==1', {
- 'variables': {
- 'files': [
- '../tools/lsan/suppressions.txt',
- ],
- },
- }],
# Copy the VS runtime DLLs into the isolate so that they
# don't have to be preinstalled on the target machine.
['OS=="win" and component=="shared_library" and CONFIGURATION_NAME=="Debug"', {
diff --git a/base/base_unittests.isolate b/base/base_unittests.isolate
index 2e33cc0..e5495e3 100644
--- a/base/base_unittests.isolate
+++ b/base/base_unittests.isolate
@@ -72,6 +72,13 @@
],
},
}],
+ ['OS=="win"', {
+ 'variables': {
+ 'files': [
+ '<(PRODUCT_DIR)/pe_image_test.dll',
+ ],
+ },
+ }],
],
'includes': [
'base.isolate',
diff --git a/base/debug/stack_trace_unittest.cc b/base/debug/stack_trace_unittest.cc
index b07fcdb..15c9093 100644
--- a/base/debug/stack_trace_unittest.cc
+++ b/base/debug/stack_trace_unittest.cc
@@ -148,8 +148,9 @@
TEST_F(StackTraceTest, AsyncSignalUnsafeSignalHandlerHang) {
Process child = SpawnChild("MismatchedMallocChildProcess");
ASSERT_TRUE(child.IsValid());
- ASSERT_TRUE(WaitForSingleProcess(child.Handle(),
- TestTimeouts::action_timeout()));
+ int exit_code;
+ ASSERT_TRUE(child.WaitForExitWithTimeout(TestTimeouts::action_timeout(),
+ &exit_code));
}
#endif // !defined(OS_IOS)
diff --git a/base/debug/stack_trace_win.cc b/base/debug/stack_trace_win.cc
index 27661ed..55d5562 100644
--- a/base/debug/stack_trace_win.cc
+++ b/base/debug/stack_trace_win.cc
@@ -152,10 +152,6 @@
init_error_ = ERROR_SUCCESS;
- // Work around a mysterious hang on Windows XP.
- if (base::win::GetVersion() < base::win::VERSION_VISTA)
- return;
-
// When transferring the binaries e.g. between bots, path put
// into the executable will get off. To still retrieve symbols correctly,
// add the directory of the executable to symbol search path.
diff --git a/base/files/file_enumerator_win.cc b/base/files/file_enumerator_win.cc
index 6da1667..931d154 100644
--- a/base/files/file_enumerator_win.cc
+++ b/base/files/file_enumerator_win.cc
@@ -147,7 +147,8 @@
// add it to pending_paths_ so we scan it after we finish scanning this
// directory. However, don't do recursion through reparse points or we
// may end up with an infinite cycle.
- if (!(find_data_.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+ DWORD attributes = GetFileAttributes(cur_file.value().c_str());
+ if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT))
pending_paths_.push(cur_file);
}
if (file_type_ & FileEnumerator::DIRECTORIES)
diff --git a/base/memory/shared_memory_win.cc b/base/memory/shared_memory_win.cc
index 20659ab..7e0cf0b 100644
--- a/base/memory/shared_memory_win.cc
+++ b/base/memory/shared_memory_win.cc
@@ -144,7 +144,8 @@
rand_values[2], rand_values[3]);
}
mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, &sa,
- PAGE_READWRITE, 0, static_cast<DWORD>(rounded_size), name_.c_str());
+ PAGE_READWRITE, 0, static_cast<DWORD>(rounded_size),
+ name_.empty() ? nullptr : name_.c_str());
if (!mapped_file_)
return false;
diff --git a/base/process/kill.h b/base/process/kill.h
index e8ce334..8c0a213 100644
--- a/base/process/kill.h
+++ b/base/process/kill.h
@@ -119,12 +119,6 @@
base::TimeDelta wait,
const ProcessFilter* filter);
-// Wait for a single process to exit. Return true if it exited cleanly within
-// the given time limit. On Linux |handle| must be a child process, however
-// on Mac and Windows it can be any process.
-BASE_EXPORT bool WaitForSingleProcess(ProcessHandle handle,
- base::TimeDelta wait);
-
// Waits a certain amount of time (can be 0) for all the processes with a given
// executable name to exit, then kills off any of them that are still around.
// If filter is non-null, then only processes selected by the filter are waited
diff --git a/base/process/kill_posix.cc b/base/process/kill_posix.cc
index 5e8b61f..77705ee 100644
--- a/base/process/kill_posix.cc
+++ b/base/process/kill_posix.cc
@@ -84,6 +84,97 @@
return ret_pid > 0;
}
+
+#if defined(OS_MACOSX)
+// Using kqueue on Mac so that we can wait on non-child processes.
+// We can't use kqueues on child processes because we need to reap
+// our own children using wait.
+static bool WaitForSingleNonChildProcess(ProcessHandle handle,
+ TimeDelta wait) {
+ DCHECK_GT(handle, 0);
+ DCHECK(wait.InMilliseconds() == kNoTimeout || wait > TimeDelta());
+
+ ScopedFD kq(kqueue());
+ if (!kq.is_valid()) {
+ DPLOG(ERROR) << "kqueue";
+ return false;
+ }
+
+ struct kevent change = {0};
+ EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
+ int result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL));
+ if (result == -1) {
+ if (errno == ESRCH) {
+ // If the process wasn't found, it must be dead.
+ return true;
+ }
+
+ DPLOG(ERROR) << "kevent (setup " << handle << ")";
+ return false;
+ }
+
+ // Keep track of the elapsed time to be able to restart kevent if it's
+ // interrupted.
+ bool wait_forever = wait.InMilliseconds() == kNoTimeout;
+ TimeDelta remaining_delta;
+ TimeTicks deadline;
+ if (!wait_forever) {
+ remaining_delta = wait;
+ deadline = TimeTicks::Now() + remaining_delta;
+ }
+
+ result = -1;
+ struct kevent event = {0};
+
+ while (wait_forever || remaining_delta > TimeDelta()) {
+ struct timespec remaining_timespec;
+ struct timespec* remaining_timespec_ptr;
+ if (wait_forever) {
+ remaining_timespec_ptr = NULL;
+ } else {
+ remaining_timespec = remaining_delta.ToTimeSpec();
+ remaining_timespec_ptr = &remaining_timespec;
+ }
+
+ result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr);
+
+ if (result == -1 && errno == EINTR) {
+ if (!wait_forever) {
+ remaining_delta = deadline - TimeTicks::Now();
+ }
+ result = 0;
+ } else {
+ break;
+ }
+ }
+
+ if (result < 0) {
+ DPLOG(ERROR) << "kevent (wait " << handle << ")";
+ return false;
+ } else if (result > 1) {
+ DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result "
+ << result;
+ return false;
+ } else if (result == 0) {
+ // Timed out.
+ return false;
+ }
+
+ DCHECK_EQ(result, 1);
+
+ if (event.filter != EVFILT_PROC ||
+ (event.fflags & NOTE_EXIT) == 0 ||
+ event.ident != static_cast<uintptr_t>(handle)) {
+ DLOG(ERROR) << "kevent (wait " << handle
+ << "): unexpected event: filter=" << event.filter
+ << ", fflags=" << event.fflags
+ << ", ident=" << event.ident;
+ return false;
+ }
+
+ return true;
+}
+#endif // OS_MACOSX
#endif // !defined(OS_NACL_NONSFI)
TerminationStatus GetTerminationStatusImpl(ProcessHandle handle,
@@ -230,7 +321,19 @@
bool WaitForExitCodeWithTimeout(ProcessHandle handle,
int* exit_code,
- base::TimeDelta timeout) {
+ TimeDelta timeout) {
+ ProcessHandle parent_pid = GetParentProcessId(handle);
+ ProcessHandle our_pid = GetCurrentProcessHandle();
+ if (parent_pid != our_pid) {
+#if defined(OS_MACOSX)
+ // On Mac we can wait on non child processes.
+ return WaitForSingleNonChildProcess(handle, timeout);
+#else
+ // Currently on Linux we can't handle non child processes.
+ NOTIMPLEMENTED();
+#endif // OS_MACOSX
+ }
+
int status;
if (!WaitpidWithTimeout(handle, &status, timeout))
return false;
@@ -246,138 +349,28 @@
}
bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
- base::TimeDelta wait,
+ TimeDelta wait,
const ProcessFilter* filter) {
bool result = false;
// TODO(port): This is inefficient, but works if there are multiple procs.
// TODO(port): use waitpid to avoid leaving zombies around
- base::TimeTicks end_time = base::TimeTicks::Now() + wait;
+ TimeTicks end_time = TimeTicks::Now() + wait;
do {
NamedProcessIterator iter(executable_name, filter);
if (!iter.NextProcessEntry()) {
result = true;
break;
}
- base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
- } while ((end_time - base::TimeTicks::Now()) > base::TimeDelta());
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+ } while ((end_time - TimeTicks::Now()) > TimeDelta());
return result;
}
-#if defined(OS_MACOSX)
-// Using kqueue on Mac so that we can wait on non-child processes.
-// We can't use kqueues on child processes because we need to reap
-// our own children using wait.
-static bool WaitForSingleNonChildProcess(ProcessHandle handle,
- base::TimeDelta wait) {
- DCHECK_GT(handle, 0);
- DCHECK(wait.InMilliseconds() == base::kNoTimeout || wait > base::TimeDelta());
-
- ScopedFD kq(kqueue());
- if (!kq.is_valid()) {
- DPLOG(ERROR) << "kqueue";
- return false;
- }
-
- struct kevent change = {0};
- EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
- int result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL));
- if (result == -1) {
- if (errno == ESRCH) {
- // If the process wasn't found, it must be dead.
- return true;
- }
-
- DPLOG(ERROR) << "kevent (setup " << handle << ")";
- return false;
- }
-
- // Keep track of the elapsed time to be able to restart kevent if it's
- // interrupted.
- bool wait_forever = wait.InMilliseconds() == base::kNoTimeout;
- base::TimeDelta remaining_delta;
- base::TimeTicks deadline;
- if (!wait_forever) {
- remaining_delta = wait;
- deadline = base::TimeTicks::Now() + remaining_delta;
- }
-
- result = -1;
- struct kevent event = {0};
-
- while (wait_forever || remaining_delta > base::TimeDelta()) {
- struct timespec remaining_timespec;
- struct timespec* remaining_timespec_ptr;
- if (wait_forever) {
- remaining_timespec_ptr = NULL;
- } else {
- remaining_timespec = remaining_delta.ToTimeSpec();
- remaining_timespec_ptr = &remaining_timespec;
- }
-
- result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr);
-
- if (result == -1 && errno == EINTR) {
- if (!wait_forever) {
- remaining_delta = deadline - base::TimeTicks::Now();
- }
- result = 0;
- } else {
- break;
- }
- }
-
- if (result < 0) {
- DPLOG(ERROR) << "kevent (wait " << handle << ")";
- return false;
- } else if (result > 1) {
- DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result "
- << result;
- return false;
- } else if (result == 0) {
- // Timed out.
- return false;
- }
-
- DCHECK_EQ(result, 1);
-
- if (event.filter != EVFILT_PROC ||
- (event.fflags & NOTE_EXIT) == 0 ||
- event.ident != static_cast<uintptr_t>(handle)) {
- DLOG(ERROR) << "kevent (wait " << handle
- << "): unexpected event: filter=" << event.filter
- << ", fflags=" << event.fflags
- << ", ident=" << event.ident;
- return false;
- }
-
- return true;
-}
-#endif // OS_MACOSX
-
-bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
- ProcessHandle parent_pid = GetParentProcessId(handle);
- ProcessHandle our_pid = GetCurrentProcessHandle();
- if (parent_pid != our_pid) {
-#if defined(OS_MACOSX)
- // On Mac we can wait on non child processes.
- return WaitForSingleNonChildProcess(handle, wait);
-#else
- // Currently on Linux we can't handle non child processes.
- NOTIMPLEMENTED();
-#endif // OS_MACOSX
- }
-
- int status;
- if (!WaitpidWithTimeout(handle, &status, wait))
- return false;
- return WIFEXITED(status);
-}
-
bool CleanupProcesses(const FilePath::StringType& executable_name,
- base::TimeDelta wait,
+ TimeDelta wait,
int exit_code,
const ProcessFilter* filter) {
bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter);
diff --git a/base/process/kill_win.cc b/base/process/kill_win.cc
index f280c6f..7daf5f8 100644
--- a/base/process/kill_win.cc
+++ b/base/process/kill_win.cc
@@ -189,7 +189,7 @@
bool WaitForExitCodeWithTimeout(ProcessHandle handle,
int* exit_code,
- base::TimeDelta timeout) {
+ TimeDelta timeout) {
if (::WaitForSingleObject(
handle, static_cast<DWORD>(timeout.InMilliseconds())) != WAIT_OBJECT_0)
return false;
@@ -202,7 +202,7 @@
}
bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
- base::TimeDelta wait,
+ TimeDelta wait,
const ProcessFilter* filter) {
bool result = true;
DWORD start_time = GetTickCount();
@@ -224,13 +224,8 @@
return result;
}
-bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
- int exit_code;
- return WaitForExitCodeWithTimeout(handle, &exit_code, wait) && exit_code == 0;
-}
-
bool CleanupProcesses(const FilePath::StringType& executable_name,
- base::TimeDelta wait,
+ TimeDelta wait,
int exit_code,
const ProcessFilter* filter) {
if (WaitForProcessesToExit(executable_name, wait, filter))
@@ -249,9 +244,9 @@
MessageLoop::current()->PostDelayedTask(
FROM_HERE,
- base::Bind(&TimerExpiredTask::TimedOut,
- base::Owned(new TimerExpiredTask(process.Pass()))),
- base::TimeDelta::FromMilliseconds(kWaitInterval));
+ Bind(&TimerExpiredTask::TimedOut,
+ Owned(new TimerExpiredTask(process.Pass()))),
+ TimeDelta::FromMilliseconds(kWaitInterval));
}
} // namespace base
diff --git a/base/process/process.h b/base/process/process.h
index 77d2bce..a834a29 100644
--- a/base/process/process.h
+++ b/base/process/process.h
@@ -98,6 +98,8 @@
// Waits for the process to exit. Returns true on success.
// On POSIX, if the process has been signaled then |exit_code| is set to -1.
+ // On Linux this must be a child process, however on Mac and Windows it can be
+ // any process.
bool WaitForExit(int* exit_code);
// Same as WaitForExit() but only waits for up to |timeout|.
diff --git a/base/process/process_unittest.cc b/base/process/process_unittest.cc
index 4ea7a5e..535a36f 100644
--- a/base/process/process_unittest.cc
+++ b/base/process/process_unittest.cc
@@ -124,7 +124,8 @@
exit_code = kDummyExitCode;
int kExpectedExitCode = 250;
process.Terminate(kExpectedExitCode);
- WaitForSingleProcess(process.Handle(), TestTimeouts::action_max_timeout());
+ process.WaitForExitWithTimeout(TestTimeouts::action_max_timeout(),
+ &exit_code);
EXPECT_NE(TERMINATION_STATUS_STILL_RUNNING,
GetTerminationStatus(process.Handle(), &exit_code));
diff --git a/base/process/process_util_unittest.cc b/base/process/process_util_unittest.cc
index 5ed15d2..11d8874 100644
--- a/base/process/process_util_unittest.cc
+++ b/base/process/process_util_unittest.cc
@@ -151,8 +151,9 @@
TEST_F(ProcessUtilTest, SpawnChild) {
base::Process process = SpawnChild("SimpleChildProcess");
ASSERT_TRUE(process.IsValid());
- EXPECT_TRUE(base::WaitForSingleProcess(process.Handle(),
- TestTimeouts::action_max_timeout()));
+ int exit_code;
+ EXPECT_TRUE(process.WaitForExitWithTimeout(
+ TestTimeouts::action_max_timeout(), &exit_code));
}
MULTIPROCESS_TEST_MAIN(SlowChildProcess) {
@@ -167,8 +168,9 @@
base::Process process = SpawnChild("SlowChildProcess");
ASSERT_TRUE(process.IsValid());
SignalChildren(signal_file.c_str());
- EXPECT_TRUE(base::WaitForSingleProcess(process.Handle(),
- TestTimeouts::action_max_timeout()));
+ int exit_code;
+ EXPECT_TRUE(process.WaitForExitWithTimeout(
+ TestTimeouts::action_max_timeout(), &exit_code));
remove(signal_file.c_str());
}
@@ -550,12 +552,12 @@
#if defined(THREAD_SANITIZER)
// Compiler-based ThreadSanitizer makes this test slow.
- CHECK(base::WaitForSingleProcess(process.Handle(),
- base::TimeDelta::FromSeconds(3)));
+ base::TimeDelta timeout = base::TimeDelta::FromSeconds(3);
#else
- CHECK(base::WaitForSingleProcess(process.Handle(),
- base::TimeDelta::FromSeconds(1)));
+ base::TimeDelta timeout = base::TimeDelta::FromSeconds(1);
#endif
+ int exit_code;
+ CHECK(process.WaitForExitWithTimeout(timeout, &exit_code));
ret = IGNORE_EINTR(close(fds[0]));
DPCHECK(ret == 0);
@@ -891,8 +893,9 @@
base::Process child_process = SpawnChild("process_util_test_never_die");
ASSERT_TRUE(child_process.IsValid());
base::EnsureProcessTerminated(child_process.Duplicate());
- base::WaitForSingleProcess(child_process.Handle(),
- base::TimeDelta::FromSeconds(5));
+ int exit_code;
+ child_process.WaitForExitWithTimeout(base::TimeDelta::FromSeconds(5),
+ &exit_code);
// Check that process was really killed.
EXPECT_TRUE(IsProcessDead(child_process.Handle()));
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index cca72dd..05a3dc3 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -116,6 +116,7 @@
"//testing/gmock",
"//testing/gtest",
"//third_party/libxml",
+ "//third_party/icu:icuuc",
]
if (!is_posix) {
diff --git a/base/third_party/dynamic_annotations/BUILD.gn b/base/third_party/dynamic_annotations/BUILD.gn
index e52938c..d6a5123 100644
--- a/base/third_party/dynamic_annotations/BUILD.gn
+++ b/base/third_party/dynamic_annotations/BUILD.gn
@@ -2,14 +2,22 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-source_set("dynamic_annotations") {
- sources = [
- "../valgrind/valgrind.h",
- "dynamic_annotations.c",
- "dynamic_annotations.h",
- ]
- if (is_android && !is_debug) {
- configs -= [ "//build/config/compiler:optimize" ]
- configs += [ "//build/config/compiler:optimize_max" ]
+if (is_nacl) {
+ # Native client doesn't need dynamic annotations, so we provide a
+ # dummy target in order for clients to not have to special-case the
+ # dependency.
+ group("dynamic_annotations") {
+ }
+} else {
+ source_set("dynamic_annotations") {
+ sources = [
+ "../valgrind/valgrind.h",
+ "dynamic_annotations.c",
+ "dynamic_annotations.h",
+ ]
+ if (is_android && !is_debug) {
+ configs -= [ "//build/config/compiler:optimize" ]
+ configs += [ "//build/config/compiler:optimize_max" ]
+ }
}
}
diff --git a/base/time/time.h b/base/time/time.h
index 6d61861..b18c0b2 100644
--- a/base/time/time.h
+++ b/base/time/time.h
@@ -158,34 +158,30 @@
return TimeDelta(-delta_);
}
- // Computations with ints, note that we only allow multiplicative operations
- // with ints, and additive operations with other deltas.
- TimeDelta operator*(int64 a) const {
+ // Computations with numeric types.
+ template<typename T>
+ TimeDelta operator*(T a) const {
return TimeDelta(delta_ * a);
}
- TimeDelta operator/(int64 a) const {
+ template<typename T>
+ TimeDelta operator/(T a) const {
return TimeDelta(delta_ / a);
}
- TimeDelta& operator*=(int64 a) {
+ template<typename T>
+ TimeDelta& operator*=(T a) {
delta_ *= a;
return *this;
}
- TimeDelta& operator/=(int64 a) {
+ template<typename T>
+ TimeDelta& operator/=(T a) {
delta_ /= a;
return *this;
}
+
int64 operator/(TimeDelta a) const {
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;
@@ -213,7 +209,6 @@
private:
friend class Time;
friend class TimeTicks;
- friend TimeDelta operator*(int64 a, TimeDelta td);
// Constructs a delta given the duration in microseconds. This is private
// to avoid confusion by callers with an integer constructor. Use
@@ -225,8 +220,9 @@
int64 delta_;
};
-inline TimeDelta operator*(int64 a, TimeDelta td) {
- return TimeDelta(a * td.delta_);
+template<typename T>
+inline TimeDelta operator*(T a, TimeDelta td) {
+ return td * a;
}
// For logging use only.
diff --git a/base/time/time_unittest.cc b/base/time/time_unittest.cc
index 6387ec7..a96787c 100644
--- a/base/time/time_unittest.cc
+++ b/base/time/time_unittest.cc
@@ -868,12 +868,78 @@
}
-TEST(TimeDelta, multiply_by) {
+TEST(TimeDelta, NumericOperators) {
double d = 0.5;
EXPECT_EQ(TimeDelta::FromMilliseconds(500),
- TimeDelta::FromMilliseconds(1000).multiply_by(d));
+ TimeDelta::FromMilliseconds(1000) * d);
EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
- TimeDelta::FromMilliseconds(1000).divide_by(d));
+ TimeDelta::FromMilliseconds(1000) / d);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+ TimeDelta::FromMilliseconds(1000) *= d);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+ TimeDelta::FromMilliseconds(1000) /= d);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+ d * TimeDelta::FromMilliseconds(1000));
+
+ float f = 0.5;
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+ TimeDelta::FromMilliseconds(1000) * f);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+ TimeDelta::FromMilliseconds(1000) / f);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+ TimeDelta::FromMilliseconds(1000) *= f);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+ TimeDelta::FromMilliseconds(1000) /= f);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+ f * TimeDelta::FromMilliseconds(1000));
+
+
+ int i = 2;
+ EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+ TimeDelta::FromMilliseconds(1000) * i);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+ TimeDelta::FromMilliseconds(1000) / i);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+ TimeDelta::FromMilliseconds(1000) *= i);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+ TimeDelta::FromMilliseconds(1000) /= i);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+ i * TimeDelta::FromMilliseconds(1000));
+
+ int64_t i64 = 2;
+ EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+ TimeDelta::FromMilliseconds(1000) * i64);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+ TimeDelta::FromMilliseconds(1000) / i64);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+ TimeDelta::FromMilliseconds(1000) *= i64);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+ TimeDelta::FromMilliseconds(1000) /= i64);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+ i64 * TimeDelta::FromMilliseconds(1000));
+
+
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+ TimeDelta::FromMilliseconds(1000) * 0.5);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+ TimeDelta::FromMilliseconds(1000) / 0.5);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+ TimeDelta::FromMilliseconds(1000) *= 0.5);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+ TimeDelta::FromMilliseconds(1000) /= 0.5);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+ 0.5 * TimeDelta::FromMilliseconds(1000));
+
+ EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+ TimeDelta::FromMilliseconds(1000) * 2);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+ TimeDelta::FromMilliseconds(1000) / 2);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+ TimeDelta::FromMilliseconds(1000) *= 2);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(500),
+ TimeDelta::FromMilliseconds(1000) /= 2);
+ EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
+ 2 * TimeDelta::FromMilliseconds(1000));
}
TEST(TimeDeltaLogging, DCheckEqCompiles) {
diff --git a/base/trace_event/process_memory_dump.cc b/base/trace_event/process_memory_dump.cc
index 6da9132..5363db5 100644
--- a/base/trace_event/process_memory_dump.cc
+++ b/base/trace_event/process_memory_dump.cc
@@ -10,7 +10,8 @@
namespace base {
namespace trace_event {
-ProcessMemoryDump::ProcessMemoryDump() : has_process_totals_(false) {
+ProcessMemoryDump::ProcessMemoryDump()
+ : has_process_totals_(false), has_process_mmaps_(false) {
}
ProcessMemoryDump::~ProcessMemoryDump() {
@@ -23,6 +24,11 @@
process_totals_.AsValueInto(value);
value->EndDictionary();
}
+ if (has_process_mmaps_) {
+ value->BeginDictionary("process_mmaps");
+ process_mmaps_.AsValueInto(value);
+ value->EndDictionary();
+ }
}
} // namespace trace_event
diff --git a/base/trace_event/process_memory_dump.h b/base/trace_event/process_memory_dump.h
index f70537b..4256c4c 100644
--- a/base/trace_event/process_memory_dump.h
+++ b/base/trace_event/process_memory_dump.h
@@ -6,6 +6,7 @@
#define BASE_TRACE_EVENT_PROCESS_MEMORY_DUMP_H_
#include "base/base_export.h"
+#include "base/trace_event/process_memory_maps.h"
#include "base/trace_event/process_memory_totals.h"
namespace base {
@@ -31,10 +32,17 @@
bool has_process_totals() const { return has_process_totals_; }
void set_has_process_totals() { has_process_totals_ = true; }
+ ProcessMemoryMaps* process_mmaps() { return &process_mmaps_; }
+ bool has_process_mmaps() const { return has_process_mmaps_; }
+ void set_has_process_mmaps() { has_process_mmaps_ = true; }
+
private:
ProcessMemoryTotals process_totals_;
bool has_process_totals_;
+ ProcessMemoryMaps process_mmaps_;
+ bool has_process_mmaps_;
+
DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDump);
};
diff --git a/base/trace_event/process_memory_maps.cc b/base/trace_event/process_memory_maps.cc
new file mode 100644
index 0000000..bf3c5a0
--- /dev/null
+++ b/base/trace_event/process_memory_maps.cc
@@ -0,0 +1,45 @@
+// 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_maps.h"
+
+#include "base/trace_event/trace_event_argument.h"
+
+namespace base {
+namespace trace_event {
+
+// static
+const uint32 ProcessMemoryMaps::VMRegion::kProtectionFlagsRead = 4;
+const uint32 ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite = 2;
+const uint32 ProcessMemoryMaps::VMRegion::kProtectionFlagsExec = 1;
+
+ProcessMemoryMaps::ProcessMemoryMaps() {
+}
+
+ProcessMemoryMaps::~ProcessMemoryMaps() {
+}
+
+void ProcessMemoryMaps::AsValueInto(TracedValue* value) const {
+ value->BeginArray("vm_regions");
+ for (const auto& region : vm_regions_) {
+ value->BeginDictionary();
+
+ value->SetDouble("start_address", region.start_address);
+ value->SetDouble("size_in_bytes", region.size_in_bytes);
+ value->SetInteger("protection_flags", region.protection_flags);
+ value->SetString("mapped_file", region.mapped_file);
+ value->SetDouble("mapped_file_offset", region.mapped_file_offset);
+
+ value->BeginDictionary("byte_stats");
+ value->SetDouble("resident", region.byte_stats_resident);
+ value->SetDouble("anonymous", region.byte_stats_anonymous);
+ value->EndDictionary();
+
+ value->EndDictionary();
+ }
+ value->EndArray();
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/process_memory_maps.h b/base/trace_event/process_memory_maps.h
new file mode 100644
index 0000000..70f6610
--- /dev/null
+++ b/base/trace_event/process_memory_maps.h
@@ -0,0 +1,54 @@
+// 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_MAPS_H_
+#define BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+namespace trace_event {
+
+class TracedValue;
+
+// Data model for process-wide memory stats.
+class BASE_EXPORT ProcessMemoryMaps {
+ public:
+ struct VMRegion {
+ static const uint32 kProtectionFlagsRead;
+ static const uint32 kProtectionFlagsWrite;
+ static const uint32 kProtectionFlagsExec;
+
+ uint64 start_address;
+ uint64 size_in_bytes;
+ uint32 protection_flags;
+ std::string mapped_file;
+ uint64 mapped_file_offset;
+ uint64 byte_stats_resident;
+ uint64 byte_stats_anonymous;
+ };
+
+ ProcessMemoryMaps();
+ ~ProcessMemoryMaps();
+
+ void AddVMRegion(const VMRegion& region) { vm_regions_.push_back(region); }
+ const std::vector<VMRegion>& vm_regions() const { return vm_regions_; }
+
+ // Called at trace generation time to populate the TracedValue.
+ void AsValueInto(TracedValue* value) const;
+
+ private:
+ std::vector<VMRegion> vm_regions_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessMemoryMaps);
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_H_
diff --git a/base/trace_event/process_memory_maps_dump_provider.cc b/base/trace_event/process_memory_maps_dump_provider.cc
new file mode 100644
index 0000000..e1cefc3
--- /dev/null
+++ b/base/trace_event/process_memory_maps_dump_provider.cc
@@ -0,0 +1,176 @@
+// 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_maps_dump_provider.h"
+
+#include <cctype>
+#include <fstream>
+
+#include "base/logging.h"
+#include "base/process/process_metrics.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/process_memory_maps.h"
+
+namespace base {
+namespace trace_event {
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+// static
+std::istream* ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = nullptr;
+
+namespace {
+
+const uint32 kMaxLineSize = 4096;
+
+bool ParseSmapsHeader(std::istream* smaps,
+ ProcessMemoryMaps::VMRegion* region) {
+ // e.g., "00400000-00421000 r-xp 00000000 fc:01 1234 /foo.so\n"
+ bool res = true; // Whether this region should be appended or skipped.
+ uint64 end_addr;
+ std::string protection_flags;
+ std::string ignored;
+ *smaps >> std::hex >> region->start_address;
+ smaps->ignore(1);
+ *smaps >> std::hex >> end_addr;
+ if (end_addr > region->start_address) {
+ region->size_in_bytes = end_addr - region->start_address;
+ } else {
+ // This is not just paranoia, it can actually happen (See crbug.com/461237).
+ region->size_in_bytes = 0;
+ res = false;
+ }
+
+ region->protection_flags = 0;
+ *smaps >> protection_flags;
+ CHECK(4UL == protection_flags.size());
+ if (protection_flags[0] == 'r') {
+ region->protection_flags |=
+ ProcessMemoryMaps::VMRegion::kProtectionFlagsRead;
+ }
+ if (protection_flags[1] == 'w') {
+ region->protection_flags |=
+ ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite;
+ }
+ if (protection_flags[2] == 'x') {
+ region->protection_flags |=
+ ProcessMemoryMaps::VMRegion::kProtectionFlagsExec;
+ }
+ *smaps >> std::hex >> region->mapped_file_offset;
+ *smaps >> ignored; // Ignore device maj-min (fc:01 in the example above).
+ *smaps >> ignored; // Ignore inode number (1234 in the example above).
+
+ while (smaps->peek() == ' ')
+ smaps->ignore(1);
+ char mapped_file[kMaxLineSize];
+ smaps->getline(mapped_file, sizeof(mapped_file));
+ region->mapped_file = mapped_file;
+
+ return res;
+}
+
+uint32 ParseSmapsCounter(std::istream* smaps,
+ ProcessMemoryMaps::VMRegion* region) {
+ // e.g., "RSS: 0 Kb\n"
+ uint32 res = 0;
+ std::string counter_name;
+ *smaps >> counter_name;
+
+ // TODO(primiano): "Swap" should also be accounted as resident. Check
+ // whether Rss isn't already counting swapped and fix below if that is
+ // the case.
+ if (counter_name == "Rss:") {
+ *smaps >> std::dec >> region->byte_stats_resident;
+ region->byte_stats_resident *= 1024;
+ res = 1;
+ } else if (counter_name == "Anonymous:") {
+ *smaps >> std::dec >> region->byte_stats_anonymous;
+ region->byte_stats_anonymous *= 1024;
+ res = 1;
+ }
+
+#ifndef NDEBUG
+ // Paranoid check against changes of the Kernel /proc interface.
+ if (res) {
+ std::string unit;
+ *smaps >> unit;
+ DCHECK_EQ("kB", unit);
+ }
+#endif
+
+ smaps->ignore(kMaxLineSize, '\n');
+
+ return res;
+}
+
+uint32 ReadLinuxProcSmapsFile(std::istream* smaps, ProcessMemoryMaps* pmm) {
+ if (!smaps->good()) {
+ LOG(ERROR) << "Could not read smaps file.";
+ return 0;
+ }
+ const uint32 kNumExpectedCountersPerRegion = 2;
+ uint32 counters_parsed_for_current_region = 0;
+ uint32 num_valid_regions = 0;
+ ProcessMemoryMaps::VMRegion region;
+ bool should_add_current_region = false;
+ for (;;) {
+ int next = smaps->peek();
+ if (next == std::ifstream::traits_type::eof() || next == '\n')
+ break;
+ if (isxdigit(next) && !isupper(next)) {
+ region = {0};
+ counters_parsed_for_current_region = 0;
+ should_add_current_region = ParseSmapsHeader(smaps, ®ion);
+ } else {
+ counters_parsed_for_current_region += ParseSmapsCounter(smaps, ®ion);
+ DCHECK_LE(counters_parsed_for_current_region,
+ kNumExpectedCountersPerRegion);
+ if (counters_parsed_for_current_region == kNumExpectedCountersPerRegion) {
+ if (should_add_current_region) {
+ pmm->AddVMRegion(region);
+ ++num_valid_regions;
+ should_add_current_region = false;
+ }
+ }
+ }
+ }
+ return num_valid_regions;
+}
+
+} // namespace
+#endif // defined(OS_LINUX) || defined(OS_ANDROID)
+
+// static
+ProcessMemoryMapsDumpProvider* ProcessMemoryMapsDumpProvider::GetInstance() {
+ return Singleton<ProcessMemoryMapsDumpProvider,
+ LeakySingletonTraits<ProcessMemoryMapsDumpProvider>>::get();
+}
+
+ProcessMemoryMapsDumpProvider::ProcessMemoryMapsDumpProvider() {
+}
+
+ProcessMemoryMapsDumpProvider::~ProcessMemoryMapsDumpProvider() {
+}
+
+// Called at trace dump point time. Creates a snapshot the memory maps for the
+// current process.
+void ProcessMemoryMapsDumpProvider::DumpInto(ProcessMemoryDump* pmd) {
+ uint32 res = 0;
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+ if (UNLIKELY(proc_smaps_for_testing)) {
+ res = ReadLinuxProcSmapsFile(proc_smaps_for_testing, pmd->process_mmaps());
+ } else {
+ std::ifstream proc_self_smaps("/proc/self/smaps");
+ res = ReadLinuxProcSmapsFile(&proc_self_smaps, pmd->process_mmaps());
+ }
+#else
+ LOG(ERROR) << "ProcessMemoryMaps dump provider is supported only on Linux";
+#endif
+
+ if (res > 0)
+ pmd->set_has_process_mmaps();
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/process_memory_maps_dump_provider.h b/base/trace_event/process_memory_maps_dump_provider.h
new file mode 100644
index 0000000..543f7fd
--- /dev/null
+++ b/base/trace_event/process_memory_maps_dump_provider.h
@@ -0,0 +1,42 @@
+// 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_MAPS_DUMP_PROVIDER_H_
+#define BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_DUMP_PROVIDER_H_
+
+#include <istream>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/singleton.h"
+#include "base/trace_event/memory_dump_provider.h"
+
+namespace base {
+namespace trace_event {
+
+// Dump provider which collects process-wide memory stats.
+class BASE_EXPORT ProcessMemoryMapsDumpProvider : public MemoryDumpProvider {
+ public:
+ static ProcessMemoryMapsDumpProvider* GetInstance();
+
+ // MemoryDumpProvider implementation.
+ void DumpInto(ProcessMemoryDump* pmd) override;
+
+ private:
+ friend struct DefaultSingletonTraits<ProcessMemoryMapsDumpProvider>;
+ FRIEND_TEST_ALL_PREFIXES(ProcessMemoryMapsDumpProviderTest, ParseProcSmaps);
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+ static std::istream* proc_smaps_for_testing;
+#endif
+
+ ProcessMemoryMapsDumpProvider();
+ ~ProcessMemoryMapsDumpProvider() override;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessMemoryMapsDumpProvider);
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_DUMP_PROVIDER_H_
diff --git a/base/trace_event/process_memory_maps_dump_provider_unittest.cc b/base/trace_event/process_memory_maps_dump_provider_unittest.cc
new file mode 100644
index 0000000..02fd136
--- /dev/null
+++ b/base/trace_event/process_memory_maps_dump_provider_unittest.cc
@@ -0,0 +1,175 @@
+// 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_maps_dump_provider.h"
+
+#include <fstream>
+#include <sstream>
+
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/process_memory_maps.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+namespace {
+const char kTestSmaps1[] =
+ "00400000-004be000 r-xp 00000000 fc:01 1234 /file/1\n"
+ "Size: 760 kB\n"
+ "Rss: 296 kB\n"
+ "Pss: 162 kB\n"
+ "Shared_Clean: 228 kB\n"
+ "Shared_Dirty: 0 kB\n"
+ "Private_Clean: 0 kB\n"
+ "Private_Dirty: 68 kB\n"
+ "Referenced: 296 kB\n"
+ "Anonymous: 68 kB\n"
+ "AnonHugePages: 0 kB\n"
+ "Swap: 0 kB\n"
+ "KernelPageSize: 4 kB\n"
+ "MMUPageSize: 4 kB\n"
+ "Locked: 0 kB\n"
+ "VmFlags: rd ex mr mw me dw sd\n"
+ "ff000000-ff800000 -w-p 00001080 fc:01 0 /file/name with space\n"
+ "Size: 0 kB\n"
+ "Rss: 128 kB\n"
+ "Pss: 128 kB\n"
+ "Shared_Clean: 124 kB\n"
+ "Shared_Dirty: 0 kB\n"
+ "Private_Clean: 68 kB\n"
+ "Private_Dirty: 0 kB\n"
+ "Referenced: 296 kB\n"
+ "Anonymous: 0 kB\n"
+ "AnonHugePages: 0 kB\n"
+ "Swap: 0 kB\n"
+ "KernelPageSize: 4 kB\n"
+ "MMUPageSize: 4 kB\n"
+ "Locked: 0 kB\n"
+ "VmFlags: rd ex mr mw me dw sd";
+
+const char kTestSmaps2[] =
+ // An invalid region, with zero size and overlapping with the last one
+ // (See crbug.com/461237).
+ "7fe7ce79c000-7fe7ce79c000 ---p 00000000 00:00 0 \n"
+ "Size: 4 kB\n"
+ "Rss: 0 kB\n"
+ "Pss: 0 kB\n"
+ "Shared_Clean: 0 kB\n"
+ "Shared_Dirty: 0 kB\n"
+ "Private_Clean: 0 kB\n"
+ "Private_Dirty: 0 kB\n"
+ "Referenced: 0 kB\n"
+ "Anonymous: 0 kB\n"
+ "AnonHugePages: 0 kB\n"
+ "Swap: 0 kB\n"
+ "KernelPageSize: 4 kB\n"
+ "MMUPageSize: 4 kB\n"
+ "Locked: 0 kB\n"
+ "VmFlags: rd ex mr mw me dw sd\n"
+ // A invalid region with its range going backwards.
+ "00400000-00200000 ---p 00000000 00:00 0 \n"
+ "Size: 4 kB\n"
+ "Rss: 0 kB\n"
+ "Pss: 0 kB\n"
+ "Shared_Clean: 0 kB\n"
+ "Shared_Dirty: 0 kB\n"
+ "Private_Clean: 0 kB\n"
+ "Private_Dirty: 0 kB\n"
+ "Referenced: 0 kB\n"
+ "Anonymous: 0 kB\n"
+ "AnonHugePages: 0 kB\n"
+ "Swap: 0 kB\n"
+ "KernelPageSize: 4 kB\n"
+ "MMUPageSize: 4 kB\n"
+ "Locked: 0 kB\n"
+ "VmFlags: rd ex mr mw me dw sd\n"
+ // A good anonymous region at the end.
+ "7fe7ce79c000-7fe7ce7a8000 ---p 00000000 00:00 0 \n"
+ "Size: 48 kB\n"
+ "Rss: 40 kB\n"
+ "Pss: 0 kB\n"
+ "Shared_Clean: 16 kB\n"
+ "Shared_Dirty: 12 kB\n"
+ "Private_Clean: 8 kB\n"
+ "Private_Dirty: 4 kB\n"
+ "Referenced: 40 kB\n"
+ "Anonymous: 16 kB\n"
+ "AnonHugePages: 0 kB\n"
+ "Swap: 0 kB\n"
+ "KernelPageSize: 4 kB\n"
+ "MMUPageSize: 4 kB\n"
+ "Locked: 0 kB\n"
+ "VmFlags: rd wr mr mw me ac sd\n";
+} // namespace
+
+TEST(ProcessMemoryMapsDumpProviderTest, ParseProcSmaps) {
+ const uint32 kProtR = ProcessMemoryMaps::VMRegion::kProtectionFlagsRead;
+ const uint32 kProtW = ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite;
+ const uint32 kProtX = ProcessMemoryMaps::VMRegion::kProtectionFlagsExec;
+
+ auto pmmdp = ProcessMemoryMapsDumpProvider::GetInstance();
+
+ // Emulate a non-existent /proc/self/smaps.
+ ProcessMemoryDump pmd_invalid;
+ std::ifstream non_existent_file("/tmp/does-not-exist");
+ ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = &non_existent_file;
+ CHECK_EQ(false, non_existent_file.good());
+ pmmdp->DumpInto(&pmd_invalid);
+ ASSERT_FALSE(pmd_invalid.has_process_mmaps());
+
+ // Emulate an empty /proc/self/smaps.
+ std::ifstream empty_file("/dev/null");
+ ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = &empty_file;
+ CHECK_EQ(true, empty_file.good());
+ pmmdp->DumpInto(&pmd_invalid);
+ ASSERT_FALSE(pmd_invalid.has_process_mmaps());
+
+ // Parse the 1st smaps file.
+ ProcessMemoryDump pmd_1;
+ std::istringstream test_smaps_1(kTestSmaps1);
+ ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = &test_smaps_1;
+ pmmdp->DumpInto(&pmd_1);
+ ASSERT_TRUE(pmd_1.has_process_mmaps());
+ const auto& regions_1 = pmd_1.process_mmaps()->vm_regions();
+ ASSERT_EQ(2UL, regions_1.size());
+
+ EXPECT_EQ(0x00400000UL, regions_1[0].start_address);
+ EXPECT_EQ(0x004be000UL - 0x00400000UL, regions_1[0].size_in_bytes);
+ EXPECT_EQ(kProtR | kProtX, regions_1[0].protection_flags);
+ EXPECT_EQ("/file/1", regions_1[0].mapped_file);
+ EXPECT_EQ(0UL, regions_1[0].mapped_file_offset);
+ EXPECT_EQ(296 * 1024UL, regions_1[0].byte_stats_resident);
+ EXPECT_EQ(68 * 1024UL, regions_1[0].byte_stats_anonymous);
+
+ EXPECT_EQ(0xff000000UL, regions_1[1].start_address);
+ EXPECT_EQ(0xff800000UL - 0xff000000UL, regions_1[1].size_in_bytes);
+ EXPECT_EQ(kProtW, regions_1[1].protection_flags);
+ EXPECT_EQ("/file/name with space", regions_1[1].mapped_file);
+ EXPECT_EQ(0x00001080UL, regions_1[1].mapped_file_offset);
+ EXPECT_EQ(128 * 1024UL, regions_1[1].byte_stats_resident);
+ EXPECT_EQ(0UL, regions_1[1].byte_stats_anonymous);
+
+ // Parse the 2nd smaps file.
+ ProcessMemoryDump pmd_2;
+ std::istringstream test_smaps_2(kTestSmaps2);
+ ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = &test_smaps_2;
+ pmmdp->DumpInto(&pmd_2);
+ ASSERT_TRUE(pmd_2.has_process_mmaps());
+ const auto& regions_2 = pmd_2.process_mmaps()->vm_regions();
+ ASSERT_EQ(1UL, regions_2.size());
+ EXPECT_EQ(0x7fe7ce79c000UL, regions_2[0].start_address);
+ EXPECT_EQ(0x7fe7ce7a8000UL - 0x7fe7ce79c000UL, regions_2[0].size_in_bytes);
+ EXPECT_EQ(0U, regions_2[0].protection_flags);
+ EXPECT_EQ("", regions_2[0].mapped_file);
+ EXPECT_EQ(0UL, regions_2[0].mapped_file_offset);
+ EXPECT_EQ(40 * 1024UL, regions_2[0].byte_stats_resident);
+ EXPECT_EQ(16 * 1024UL, regions_2[0].byte_stats_anonymous);
+}
+#endif // defined(OS_LINUX) || defined(OS_ANDROID)
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/trace_event.h b/base/trace_event/trace_event.h
index 07a2afa..e12d8f4 100644
--- a/base/trace_event/trace_event.h
+++ b/base/trace_event/trace_event.h
@@ -1008,7 +1008,8 @@
phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), \
name, trace_event_trace_id.data(), \
thread_id, base::TimeTicks::FromInternalValue(timestamp), \
- trace_event_flags, ##__VA_ARGS__); \
+ trace_event_flags | TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP, \
+ ##__VA_ARGS__); \
} \
} while (0)
@@ -1045,9 +1046,11 @@
#define TRACE_EVENT_FLAG_HAS_ID (static_cast<unsigned char>(1 << 1))
#define TRACE_EVENT_FLAG_MANGLE_ID (static_cast<unsigned char>(1 << 2))
#define TRACE_EVENT_FLAG_SCOPE_OFFSET (static_cast<unsigned char>(1 << 3))
+#define TRACE_EVENT_FLAG_SCOPE_EXTRA (static_cast<unsigned char>(1 << 4))
+#define TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP (static_cast<unsigned char>(1 << 5))
#define TRACE_EVENT_FLAG_SCOPE_MASK (static_cast<unsigned char>( \
- TRACE_EVENT_FLAG_SCOPE_OFFSET | (TRACE_EVENT_FLAG_SCOPE_OFFSET << 1)))
+ TRACE_EVENT_FLAG_SCOPE_OFFSET | TRACE_EVENT_FLAG_SCOPE_EXTRA))
// Type values for identifying types in the TraceValue union.
#define TRACE_VALUE_TYPE_BOOL (static_cast<unsigned char>(1))
diff --git a/base/trace_event/trace_event_impl.cc b/base/trace_event/trace_event_impl.cc
index 9ca0ddc..445cb6d 100644
--- a/base/trace_event/trace_event_impl.cc
+++ b/base/trace_event/trace_event_impl.cc
@@ -1914,7 +1914,8 @@
id ^= process_id_hash_;
TimeTicks offset_event_timestamp = OffsetTimestamp(timestamp);
- TimeTicks now = OffsetNow();
+ TimeTicks now = flags & TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP ?
+ OffsetNow() : offset_event_timestamp;
TimeTicks thread_now = ThreadNow();
ThreadLocalEventBuffer* thread_local_event_buffer = NULL;
@@ -2035,9 +2036,6 @@
}
}
- // Use |now| instead of |offset_event_timestamp| to compute overhead, because
- // event timestamp may be not the real time that we started to add the event
- // (e.g. event with zero timestamp or that was generated some time ago).
if (thread_local_event_buffer)
thread_local_event_buffer->ReportOverhead(now, thread_now);
diff --git a/base/win/pe_image_test.cc b/base/win/pe_image_test.cc
new file mode 100644
index 0000000..e374598
--- /dev/null
+++ b/base/win/pe_image_test.cc
@@ -0,0 +1,31 @@
+// 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 <windows.h>
+
+#include <cfgmgr32.h>
+#include <shellapi.h>
+
+extern "C" {
+
+__declspec(dllexport) void ExportFunc1() {
+ // Call into user32.dll.
+ HWND dummy = GetDesktopWindow();
+ SetWindowTextA(dummy, "dummy");
+}
+
+__declspec(dllexport) void ExportFunc2() {
+ // Call into cfgmgr32.dll.
+ CM_MapCrToWin32Err(CR_SUCCESS, ERROR_SUCCESS);
+
+ // Call into shell32.dll.
+ SHFILEOPSTRUCT file_operation = {0};
+ SHFileOperation(&file_operation);
+
+ // Call into kernel32.dll.
+ HANDLE h = CreateEvent(NULL, FALSE, FALSE, NULL);
+ CloseHandle(h);
+}
+
+} // extern "C"
diff --git a/base/win/pe_image_unittest.cc b/base/win/pe_image_unittest.cc
index af4209b..4134741 100644
--- a/base/win/pe_image_unittest.cc
+++ b/base/win/pe_image_unittest.cc
@@ -3,29 +3,140 @@
// found in the LICENSE file.
// This file contains unit tests for PEImage.
+#include <algorithm>
+#include <vector>
#include "testing/gtest/include/gtest/gtest.h"
#include "base/win/pe_image.h"
#include "base/win/windows_version.h"
+namespace {
+
+class Expectations {
+ public:
+ enum Value {
+ SECTIONS = 0,
+ IMPORTS_DLLS,
+ DELAY_DLLS,
+ EXPORTS,
+ IMPORTS,
+ DELAY_IMPORTS,
+ RELOCS
+ };
+
+ enum Arch {
+ ARCH_X86 = 0,
+ ARCH_X64,
+ ARCH_ALL
+ };
+
+ Expectations();
+
+ void SetDefault(Value value, int count);
+ void SetOverride(Value value, base::win::Version version,
+ Arch arch, int count);
+ void SetOverride(Value value, base::win::Version version, int count);
+ void SetOverride(Value value, Arch arch, int count);
+
+ // returns -1 on failure.
+ int GetExpectation(Value value);
+
+ private:
+ class Override {
+ public:
+ enum MatchType { MATCH_VERSION, MATCH_ARCH, MATCH_BOTH, MATCH_NONE };
+
+ Override(Value value, base::win::Version version, Arch arch, int count)
+ : value_(value), version_(version), arch_(arch), count_(count) {
+ };
+
+ bool Matches(Value value, base::win::Version version,
+ Arch arch, MatchType type) {
+ if (value_ != value)
+ return false;
+
+ switch (type) {
+ case MATCH_BOTH:
+ return (arch == arch_ && version == version_);
+ case MATCH_ARCH:
+ return (arch == arch_ && version_ == base::win::VERSION_WIN_LAST);
+ case MATCH_VERSION:
+ return (arch_ == ARCH_ALL && version == version_);
+ case MATCH_NONE:
+ return (arch_ == ARCH_ALL && version_ == base::win::VERSION_WIN_LAST);
+ }
+ return false;
+ }
+
+ int GetCount() { return count_; }
+
+ private:
+ Value value_;
+ base::win::Version version_;
+ Arch arch_;
+ int count_;
+ };
+
+ bool MatchesMyArch(Arch arch);
+
+ std::vector<Override> overrides_;
+ Arch my_arch_;
+ base::win::Version my_version_;
+};
+
+Expectations::Expectations() {
+ my_version_ = base::win::GetVersion();
+#if defined(ARCH_CPU_64_BITS)
+ my_arch_ = ARCH_X64;
+#else
+ my_arch_ = ARCH_X86;
+#endif
+}
+
+int Expectations::GetExpectation(Value value) {
+ // Prefer OS version specificity over Arch specificity.
+ for (auto type : { Override::MATCH_BOTH,
+ Override::MATCH_VERSION,
+ Override::MATCH_ARCH,
+ Override::MATCH_NONE }) {
+ for (auto override : overrides_) {
+ if (override.Matches(value, my_version_, my_arch_, type))
+ return override.GetCount();
+ }
+ }
+ return -1;
+}
+
+void Expectations::SetDefault(Value value, int count) {
+ SetOverride(value, base::win::VERSION_WIN_LAST, ARCH_ALL, count);
+}
+
+void Expectations::SetOverride(Value value,
+ base::win::Version version,
+ Arch arch,
+ int count) {
+ overrides_.push_back(Override(value, version, arch, count));
+}
+
+void Expectations::SetOverride(Value value,
+ base::win::Version version,
+ int count) {
+ SetOverride(value, version, ARCH_ALL, count);
+}
+
+void Expectations::SetOverride(Value value, Arch arch, int count) {
+ SetOverride(value, base::win::VERSION_WIN_LAST, arch, count);
+}
+
+} // namespace
+
namespace base {
namespace win {
-// Just counts the number of invocations.
-bool ExportsCallback(const PEImage &image,
- DWORD ordinal,
- DWORD hint,
- LPCSTR name,
- PVOID function,
- LPCSTR forward,
- PVOID cookie) {
- int* count = reinterpret_cast<int*>(cookie);
- (*count)++;
- return true;
-}
+namespace {
// Just counts the number of invocations.
-bool ImportsCallback(const PEImage &image,
+bool ImportsCallback(const PEImage& image,
LPCSTR module,
DWORD ordinal,
LPCSTR name,
@@ -38,18 +149,18 @@
}
// Just counts the number of invocations.
-bool SectionsCallback(const PEImage &image,
- PIMAGE_SECTION_HEADER header,
- PVOID section_start,
- DWORD section_size,
- PVOID cookie) {
+bool SectionsCallback(const PEImage& image,
+ PIMAGE_SECTION_HEADER header,
+ PVOID section_start,
+ DWORD section_size,
+ PVOID cookie) {
int* count = reinterpret_cast<int*>(cookie);
(*count)++;
return true;
}
// Just counts the number of invocations.
-bool RelocsCallback(const PEImage &image,
+bool RelocsCallback(const PEImage& image,
WORD type,
PVOID address,
PVOID cookie) {
@@ -59,7 +170,7 @@
}
// Just counts the number of invocations.
-bool ImportChunksCallback(const PEImage &image,
+bool ImportChunksCallback(const PEImage& image,
LPCSTR module,
PIMAGE_THUNK_DATA name_table,
PIMAGE_THUNK_DATA iat,
@@ -70,7 +181,7 @@
}
// Just counts the number of invocations.
-bool DelayImportChunksCallback(const PEImage &image,
+bool DelayImportChunksCallback(const PEImage& image,
PImgDelayDescr delay_descriptor,
LPCSTR module,
PIMAGE_THUNK_DATA name_table,
@@ -83,167 +194,89 @@
return true;
}
-// Identifiers for the set of supported expectations.
-enum ExpectationSet {
- WIN_2K_SET,
- WIN_XP_SET,
- WIN_VISTA_SET,
- WIN_7_SET,
- WIN_8_SET,
- UNSUPPORTED_SET,
-};
-
-// We'll be using some known values for the tests.
-enum Value {
- sections = 0,
- imports_dlls,
- delay_dlls,
- exports,
- imports,
- delay_imports,
- relocs
-};
-
-ExpectationSet GetExpectationSet(DWORD os) {
- if (os == 50)
- return WIN_2K_SET;
- if (os == 51)
- return WIN_XP_SET;
- if (os == 60)
- return WIN_VISTA_SET;
- if (os == 61)
- return WIN_7_SET;
- if (os >= 62)
- return WIN_8_SET;
- return UNSUPPORTED_SET;
+// Just counts the number of invocations.
+bool ExportsCallback(const PEImage& image,
+ DWORD ordinal,
+ DWORD hint,
+ LPCSTR name,
+ PVOID function,
+ LPCSTR forward,
+ PVOID cookie) {
+ int* count = reinterpret_cast<int*>(cookie);
+ (*count)++;
+ return true;
}
-// Retrieves the expected value from advapi32.dll based on the OS.
-int GetExpectedValue(Value value, DWORD os) {
- const int xp_delay_dlls = 2;
- const int xp_exports = 675;
- const int xp_imports = 422;
- const int xp_delay_imports = 8;
- const int xp_relocs = 9180;
- const int vista_delay_dlls = 4;
- const int vista_exports = 799;
- const int vista_imports = 476;
- const int vista_delay_imports = 24;
- const int vista_relocs = 10188;
- const int w2k_delay_dlls = 0;
- const int w2k_exports = 566;
- const int w2k_imports = 357;
- const int w2k_delay_imports = 0;
- const int w2k_relocs = 7388;
- const int win7_delay_dlls = 7;
- const int win7_exports = 806;
- const int win7_imports = 568;
- const int win7_delay_imports = 71;
- int win7_relocs = 7812;
- int win7_sections = 4;
- const int win8_delay_dlls = 9;
- const int win8_exports = 806;
- const int win8_imports = 568;
- const int win8_delay_imports = 113;
- const int win8_relocs = 9478;
- int win8_sections = 4;
- int win8_import_dlls = 17;
-
- base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
- // 32-bit process on a 32-bit system.
- if (os_info->architecture() == base::win::OSInfo::X86_ARCHITECTURE) {
- win8_sections = 5;
- win8_import_dlls = 19;
-
- // 64-bit process on a 64-bit system.
- } else if (os_info->wow64_status() == base::win::OSInfo::WOW64_DISABLED) {
- win7_sections = 6;
- win7_relocs = 2712;
- }
-
- // Contains the expected value, for each enumerated property (Value), and the
- // OS version: [Value][os_version]
- const int expected[][5] = {
- {4, 4, 4, win7_sections, win8_sections},
- {3, 3, 3, 13, win8_import_dlls},
- {w2k_delay_dlls, xp_delay_dlls, vista_delay_dlls, win7_delay_dlls,
- win8_delay_dlls},
- {w2k_exports, xp_exports, vista_exports, win7_exports, win8_exports},
- {w2k_imports, xp_imports, vista_imports, win7_imports, win8_imports},
- {w2k_delay_imports, xp_delay_imports,
- vista_delay_imports, win7_delay_imports, win8_delay_imports},
- {w2k_relocs, xp_relocs, vista_relocs, win7_relocs, win8_relocs}
- };
- COMPILE_ASSERT(arraysize(expected[0]) == UNSUPPORTED_SET,
- expected_value_set_mismatch);
-
- if (value > relocs)
- return 0;
- ExpectationSet expected_set = GetExpectationSet(os);
- if (expected_set >= arraysize(expected)) {
- // This should never happen. Log a failure if it does.
- EXPECT_NE(UNSUPPORTED_SET, expected_set);
- expected_set = WIN_2K_SET;
- }
-
- return expected[value][expected_set];
-}
-
-
-// TODO(jschuh): crbug.com/167707 Need to fix test on Win64 bots
-#if defined(OS_WIN) && defined(ARCH_CPU_X86_64)
-#define MAYBE_EnumeratesPE DISABLED_EnumeratesPE
-#else
-#define MAYBE_EnumeratesPE EnumeratesPE
-#endif
+} // namespace
// Tests that we are able to enumerate stuff from a PE file, and that
// the actual number of items found is within the expected range.
-TEST(PEImageTest, MAYBE_EnumeratesPE) {
- HMODULE module = LoadLibrary(L"advapi32.dll");
+TEST(PEImageTest, EnumeratesPE) {
+ Expectations expectations;
+
+#ifndef NDEBUG
+ // Default Debug expectations.
+ expectations.SetDefault(Expectations::SECTIONS, 7);
+ expectations.SetDefault(Expectations::IMPORTS_DLLS, 3);
+ expectations.SetDefault(Expectations::DELAY_DLLS, 2);
+ expectations.SetDefault(Expectations::EXPORTS, 2);
+ expectations.SetDefault(Expectations::IMPORTS, 49);
+ expectations.SetDefault(Expectations::DELAY_IMPORTS, 2);
+ expectations.SetDefault(Expectations::RELOCS, 438);
+
+ // 64-bit Debug expectations.
+ expectations.SetOverride(Expectations::SECTIONS, Expectations::ARCH_X64, 8);
+ expectations.SetOverride(Expectations::IMPORTS, Expectations::ARCH_X64, 69);
+ expectations.SetOverride(Expectations::RELOCS, Expectations::ARCH_X64, 632);
+#else
+ // Default Release expectations.
+ expectations.SetDefault(Expectations::SECTIONS, 5);
+ expectations.SetDefault(Expectations::IMPORTS_DLLS, 2);
+ expectations.SetDefault(Expectations::DELAY_DLLS, 2);
+ expectations.SetDefault(Expectations::EXPORTS, 2);
+ expectations.SetDefault(Expectations::IMPORTS, 66);
+ expectations.SetDefault(Expectations::DELAY_IMPORTS, 2);
+ expectations.SetDefault(Expectations::RELOCS, 1586);
+
+ // 64-bit Release expectations.
+ expectations.SetOverride(Expectations::SECTIONS, Expectations::ARCH_X64, 6);
+ expectations.SetOverride(Expectations::IMPORTS, Expectations::ARCH_X64, 69);
+ expectations.SetOverride(Expectations::RELOCS, Expectations::ARCH_X64, 632);
+#endif
+
+ HMODULE module = LoadLibrary(L"pe_image_test.dll");
ASSERT_TRUE(NULL != module);
PEImage pe(module);
int count = 0;
EXPECT_TRUE(pe.VerifyMagic());
- DWORD os = pe.GetNTHeaders()->OptionalHeader.MajorOperatingSystemVersion;
- os = os * 10 + pe.GetNTHeaders()->OptionalHeader.MinorOperatingSystemVersion;
-
- // Skip this test for unsupported OS versions.
- if (GetExpectationSet(os) == UNSUPPORTED_SET)
- return;
-
pe.EnumSections(SectionsCallback, &count);
- EXPECT_EQ(GetExpectedValue(sections, os), count);
+ EXPECT_EQ(expectations.GetExpectation(Expectations::SECTIONS), count);
count = 0;
pe.EnumImportChunks(ImportChunksCallback, &count);
- EXPECT_EQ(GetExpectedValue(imports_dlls, os), count);
+ EXPECT_EQ(expectations.GetExpectation(Expectations::IMPORTS_DLLS), count);
count = 0;
pe.EnumDelayImportChunks(DelayImportChunksCallback, &count);
- EXPECT_EQ(GetExpectedValue(delay_dlls, os), count);
+ EXPECT_EQ(expectations.GetExpectation(Expectations::DELAY_DLLS), count);
count = 0;
pe.EnumExports(ExportsCallback, &count);
- EXPECT_GT(count, GetExpectedValue(exports, os) - 20);
- EXPECT_LT(count, GetExpectedValue(exports, os) + 100);
+ EXPECT_EQ(expectations.GetExpectation(Expectations::EXPORTS), count);
count = 0;
pe.EnumAllImports(ImportsCallback, &count);
- EXPECT_GT(count, GetExpectedValue(imports, os) - 20);
- EXPECT_LT(count, GetExpectedValue(imports, os) + 100);
+ EXPECT_EQ(expectations.GetExpectation(Expectations::IMPORTS), count);
count = 0;
pe.EnumAllDelayImports(ImportsCallback, &count);
- EXPECT_GT(count, GetExpectedValue(delay_imports, os) - 2);
- EXPECT_LT(count, GetExpectedValue(delay_imports, os) + 8);
+ EXPECT_EQ(expectations.GetExpectation(Expectations::DELAY_IMPORTS), count);
count = 0;
pe.EnumRelocs(RelocsCallback, &count);
- EXPECT_GT(count, GetExpectedValue(relocs, os) - 150);
- EXPECT_LT(count, GetExpectedValue(relocs, os) + 1500);
+ EXPECT_EQ(expectations.GetExpectation(Expectations::RELOCS), count);
FreeLibrary(module);
}