diff options
Diffstat (limited to 'base')
149 files changed, 3316 insertions, 1663 deletions
diff --git a/base/base.gyp b/base/base.gyp index 8e0b1e9..6ae1a95 100644 --- a/base/base.gyp +++ b/base/base.gyp @@ -75,12 +75,11 @@ 'crypto/signature_verifier_unittest.cc', 'crypto/symmetric_key_unittest.cc', 'data_pack_unittest.cc', - 'debug_util_unittest.cc', + 'debug/leak_tracker_unittest.cc', + 'debug/stack_trace_unittest.cc', + 'debug/trace_event_win_unittest.cc', 'dir_reader_posix_unittest.cc', 'environment_unittest.cc', - 'event_trace_consumer_win_unittest.cc', - 'event_trace_controller_win_unittest.cc', - 'event_trace_provider_win_unittest.cc', 'file_descriptor_shuffle_unittest.cc', 'file_path_unittest.cc', 'file_util_unittest.cc', @@ -97,7 +96,6 @@ 'json/json_writer_unittest.cc', 'json/string_escape_unittest.cc', 'lazy_instance_unittest.cc', - 'leak_tracker_unittest.cc', 'linked_list_unittest.cc', 'linked_ptr_unittest.cc', 'lock_unittest.cc', @@ -150,7 +148,6 @@ 'time_win_unittest.cc', 'timer_unittest.cc', 'tools_sanity_unittest.cc', - 'trace_event_win_unittest.cc', 'tracked_objects_unittest.cc', 'tuple_unittest.cc', 'utf_offset_string_conversions_unittest.cc', @@ -163,6 +160,9 @@ 'watchdog_unittest.cc', 'weak_ptr_unittest.cc', 'win_util_unittest.cc', + 'win/event_trace_consumer_unittest.cc', + 'win/event_trace_controller_unittest.cc', + 'win/event_trace_provider_unittest.cc', 'win/pe_image_unittest.cc', 'win/registry_unittest.cc', 'win/scoped_bstr_unittest.cc', @@ -225,9 +225,6 @@ ['exclude', '^win/'], ], 'sources!': [ - 'event_trace_consumer_win_unittest.cc', - 'event_trace_controller_win_unittest.cc', - 'event_trace_provider_win_unittest.cc', 'object_watcher_unittest.cc', 'system_monitor_unittest.cc', 'time_win_unittest.cc', @@ -235,6 +232,11 @@ 'win_util_unittest.cc', ], }], + [ 'use_openssl==1', { + 'sources!': [ + 'crypto/rsa_private_key_nss_unittest.cc', + ], + }], ], }, { diff --git a/base/base.gypi b/base/base.gypi index 7280eb8..78c97aa 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -58,18 +58,24 @@ 'debug_util.cc', 'debug_util.h', 'debug_util_mac.cc', - 'debug_util_posix.cc', - 'debug_util_win.cc', + 'debug/debugger.cc', + 'debug/debugger.h', + 'debug/debugger_posix.cc', + 'debug/debugger_win.cc', + 'debug/leak_annotations.h', + 'debug/leak_tracker.h', + 'debug/stack_trace.cc', + 'debug/stack_trace.h', + 'debug/stack_trace_posix.cc', + 'debug/stack_trace_win.cc', + 'debug/trace_event_win.cc', + 'debug/trace_event.cc', + 'debug/trace_event.h', 'dir_reader_fallback.h', 'dir_reader_linux.h', 'dir_reader_posix.h', 'environment.cc', 'environment.h', - 'event_trace_consumer_win.h', - 'event_trace_controller_win.cc', - 'event_trace_controller_win.h', - 'event_trace_provider_win.cc', - 'event_trace_provider_win.h', 'file_path.cc', 'file_path.h', 'file_util.cc', @@ -92,8 +98,6 @@ 'global_descriptors_posix.cc', 'global_descriptors_posix.h', 'gtest_prod_util.h', - 'gtk_util.cc', - 'gtk_util.h', 'hash_tables.h', 'id_map.h', 'json/json_reader.cc', @@ -104,8 +108,6 @@ 'json/string_escape.h', 'lazy_instance.cc', 'lazy_instance.h', - 'leak_annotations.h', - 'leak_tracker.h', 'linked_list.h', 'linked_ptr.h', 'lock.cc', @@ -139,13 +141,14 @@ 'message_pump_win.h', 'metrics/histogram.cc', 'metrics/histogram.h', + 'metrics/nacl_histogram.cc', + 'metrics/nacl_histogram.h', 'metrics/stats_counters.cc', 'metrics/stats_counters.h', 'metrics/stats_table.cc', 'metrics/stats_table.h', 'mime_util.h', 'mime_util_xdg.cc', - 'move.h', 'native_library.h', 'native_library_linux.cc', 'native_library_mac.mm', @@ -193,7 +196,7 @@ 'safe_strerror_posix.cc', 'safe_strerror_posix.h', 'scoped_callback_factory.h', - 'scoped_cftyperef.h', + 'mac/scoped_cftyperef.h', 'scoped_handle.h', 'scoped_nsobject.h', 'scoped_open_process.h', @@ -254,14 +257,13 @@ 'thread_local_storage_posix.cc', 'thread_local_storage_win.cc', 'thread_local_win.cc', + 'thread_restrictions.h', + 'thread_restrictions.cc', 'time.cc', 'time.h', 'time_win.cc', 'timer.cc', 'timer.h', - 'trace_event_win.cc', - 'trace_event.cc', - 'trace_event.h', 'tracked.cc', 'tracked.h', 'tracked_objects.cc', @@ -289,6 +291,11 @@ 'weak_ptr.cc', 'weak_ptr.h', 'win/pe_image.cc', + 'win/event_trace_consumer.h', + 'win/event_trace_controller.cc', + 'win/event_trace_controller.h', + 'win/event_trace_provider.cc', + 'win/event_trace_provider.h', 'win/pe_image.h', 'win/registry.cc', 'win/registry.h', @@ -305,7 +312,6 @@ 'win/windows_version.h', 'win_util.cc', 'win_util.h', - 'windows_message_list.h', 'worker_pool.h', 'worker_pool_linux.cc', 'worker_pool_linux.h', @@ -333,6 +339,7 @@ 'sources!': [ 'atomicops_internals_x86_gcc.cc', 'message_pump_glib.cc', + 'message_pump_glib_x.cc', ], }], [ 'OS != "linux"', { @@ -346,6 +353,13 @@ ], }, ], + # Temporarily include linux implementation while debugging a + # workerpool issue. See http://crbug.com/20471 and + # http://crbug.com/60426 + [ 'OS == "mac"', { + 'sources/': [ ['include', '(^|/)worker_pool_linux\.cc$'] ], + }, + ], [ 'OS != "mac"', { 'sources!': [ 'scoped_aedesc.h' @@ -377,7 +391,7 @@ # regression to page cycler moz. 'sha1_win.cc', 'string16.cc', - 'trace_event.cc', + 'debug/trace_event.cc', ], },], ], @@ -391,23 +405,28 @@ 'crypto/encryptor.h', 'crypto/encryptor_mac.cc', 'crypto/encryptor_nss.cc', + 'crypto/encryptor_openssl.cc', 'crypto/encryptor_win.cc', 'crypto/rsa_private_key.h', 'crypto/rsa_private_key.cc', 'crypto/rsa_private_key_mac.cc', 'crypto/rsa_private_key_nss.cc', + 'crypto/rsa_private_key_openssl.cc', 'crypto/rsa_private_key_win.cc', 'crypto/signature_creator.h', 'crypto/signature_creator_mac.cc', 'crypto/signature_creator_nss.cc', + 'crypto/signature_creator_openssl.cc', 'crypto/signature_creator_win.cc', 'crypto/signature_verifier.h', 'crypto/signature_verifier_mac.cc', 'crypto/signature_verifier_nss.cc', + 'crypto/signature_verifier_openssl.cc', 'crypto/signature_verifier_win.cc', 'crypto/symmetric_key.h', 'crypto/symmetric_key_mac.cc', 'crypto/symmetric_key_nss.cc', + 'crypto/symmetric_key_openssl.cc', 'crypto/symmetric_key_win.cc', 'third_party/nspr/prcpucfg.h', 'third_party/nspr/prcpucfg_win.h', @@ -432,6 +451,7 @@ 'hmac.h', 'hmac_mac.cc', 'hmac_nss.cc', + 'hmac_openssl.cc', 'hmac_win.cc', 'image_util.cc', 'image_util.h', @@ -441,6 +461,9 @@ 'md5.h', 'message_pump_glib.cc', 'message_pump_glib.h', + 'message_pump_glib_x.cc', + 'message_pump_glib_x.h', + 'message_pump_glib_x_dispatch.h', 'message_pump_libevent.cc', 'message_pump_libevent.h', 'message_pump_mac.h', @@ -455,13 +478,12 @@ 'setproctitle_linux.h', 'sha2.cc', 'sha2.h', + 'sha2_openssl.cc', 'string16.cc', 'string16.h', 'sync_socket.h', 'sync_socket_win.cc', 'sync_socket_posix.cc', - 'thread_restrictions.h', - 'thread_restrictions.cc', 'time_mac.cc', 'time_posix.cc', 'version.cc', @@ -512,6 +534,38 @@ 'win_util.cc', ], },], + [ 'use_openssl==1', { + # TODO(joth): Use a glob to match exclude patterns once the + # OpenSSL file set is complete. + 'sources!': [ + 'crypto/encryptor_nss.cc', + 'crypto/rsa_private_key_nss.cc', + 'crypto/signature_creator_nss.cc', + 'crypto/signature_verifier_nss.cc', + 'crypto/symmetric_key_nss.cc', + 'hmac_nss.cc', + 'nss_util.cc', + 'nss_util.h', + # Note that sha2.cc depends on the NSS files bundled into + # chromium; it does not have the _nss postfix as it is required + # on platforms besides linux and *bsd. + 'sha2.cc', + 'third_party/nss/blapi.h', + 'third_party/nss/blapit.h', + 'third_party/nss/sha256.h', + 'third_party/nss/sha512.cc', + ], + }, { + 'sources!': [ + 'crypto/encryptor_openssl.cc', + 'crypto/rsa_private_key_openssl.cc', + 'crypto/signature_creator_openssl.cc', + 'crypto/signature_verifier_openssl.cc', + 'crypto/symmetric_key_openssl.cc', + 'hmac_openssl.cc', + 'sha2_openssl.cc', + ], + },], ], }], ], @@ -550,16 +604,27 @@ }, }, ], + [ 'use_openssl==1', { + 'dependencies': [ + '../build/linux/system.gyp:openssl', + ], + }, { # use_openssl==0 + 'dependencies': [ + '../build/linux/system.gyp:nss', + ], + } + ], ], 'dependencies': [ 'symbolize', '../build/util/build_util.gyp:lastchange', '../build/linux/system.gyp:gtk', - '../build/linux/system.gyp:nss', + '../build/linux/system.gyp:x11', 'xdg_mime', ], 'export_dependent_settings': [ '../build/linux/system.gyp:gtk', + '../build/linux/system.gyp:x11', ], },], [ 'OS == "freebsd" or OS == "openbsd"', { @@ -660,10 +725,12 @@ '../build/util/build_util.gyp:lastchange', '../build/linux/system.gyp:gtk', '../build/linux/system.gyp:nss', + '../build/linux/system.gyp:x11', 'xdg_mime', ], 'export_dependent_settings': [ '../build/linux/system.gyp:gtk', + '../build/linux/system.gyp:x11', ], },], ['OS == "linux"', { diff --git a/base/base_switches.cc b/base/base_switches.cc index 49c6487..d907a3a 100644 --- a/base/base_switches.cc +++ b/base/base_switches.cc @@ -39,6 +39,11 @@ const char kV[] = "v"; // given by --v. E.g. "my_module=2,foo*=3" would change the logging // level for all code in source files "my_module.*" and "foo*.*" // ("-inl" suffixes are also disregarded for this matching). +// +// Any pattern containing a forward or backward slash will be tested +// against the whole pathname and not just the module. E.g., +// "*/foo/bar/*=2" would change the logging level for all code in +// source files under a "foo/bar" directory. const char kVModule[] = "vmodule"; // Will wait for 60 seconds for a debugger to come to attach to the process. diff --git a/base/cocoa_protocols_mac.h b/base/cocoa_protocols_mac.h index c7808de..9482d51 100644 --- a/base/cocoa_protocols_mac.h +++ b/base/cocoa_protocols_mac.h @@ -27,6 +27,7 @@ @end DEFINE_EMPTY_PROTOCOL(NSAlertDelegate) +DEFINE_EMPTY_PROTOCOL(NSApplicationDelegate) DEFINE_EMPTY_PROTOCOL(NSControlTextEditingDelegate) DEFINE_EMPTY_PROTOCOL(NSMatrixDelegate) DEFINE_EMPTY_PROTOCOL(NSMenuDelegate) diff --git a/base/crypto/encryptor_openssl.cc b/base/crypto/encryptor_openssl.cc new file mode 100644 index 0000000..71a84be --- /dev/null +++ b/base/crypto/encryptor_openssl.cc @@ -0,0 +1,32 @@ +// Copyright (c) 2010 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/crypto/encryptor.h" + +#include "base/logging.h" + +namespace base { + +Encryptor::Encryptor() { +} + +Encryptor::~Encryptor() { +} + +bool Encryptor::Init(SymmetricKey* key, Mode mode, const std::string& iv) { + NOTIMPLEMENTED(); + return false; +} + +bool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) { + NOTIMPLEMENTED(); + return false; +} + +bool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) { + NOTIMPLEMENTED(); + return false; +} + +} // namespace base diff --git a/base/crypto/rsa_private_key_nss.cc b/base/crypto/rsa_private_key_nss.cc index 13e4f1f..7786521 100644 --- a/base/crypto/rsa_private_key_nss.cc +++ b/base/crypto/rsa_private_key_nss.cc @@ -10,7 +10,7 @@ #include <list> -#include "base/leak_annotations.h" +#include "base/debug/leak_annotations.h" #include "base/logging.h" #include "base/nss_util.h" #include "base/nss_util_internal.h" diff --git a/base/crypto/rsa_private_key_openssl.cc b/base/crypto/rsa_private_key_openssl.cc new file mode 100644 index 0000000..ec1d8b5 --- /dev/null +++ b/base/crypto/rsa_private_key_openssl.cc @@ -0,0 +1,79 @@ +// Copyright (c) 2010 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/crypto/rsa_private_key.h" + +#include "base/logging.h" + +namespace base { + +// static +RSAPrivateKey* RSAPrivateKey::CreateWithParams(uint16 num_bits, + bool permanent, + bool sensitive) { + NOTIMPLEMENTED(); + return NULL; +} + +// static +RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) { + return CreateWithParams(num_bits, + false /* not permanent */, + false /* not sensitive */); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateSensitive(uint16 num_bits) { + return CreateWithParams(num_bits, + true /* permanent */, + true /* sensitive */); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfoWithParams( + const std::vector<uint8>& input, bool permanent, bool sensitive) { + NOTIMPLEMENTED(); + return NULL; +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo( + const std::vector<uint8>& input) { + return CreateFromPrivateKeyInfoWithParams(input, + false /* not permanent */, + false /* not sensitive */); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateSensitiveFromPrivateKeyInfo( + const std::vector<uint8>& input) { + return CreateFromPrivateKeyInfoWithParams(input, + true /* permanent */, + true /* seneitive */); +} + +// static +RSAPrivateKey* RSAPrivateKey::FindFromPublicKeyInfo( + const std::vector<uint8>& input) { + NOTIMPLEMENTED(); + return NULL; +} + +RSAPrivateKey::RSAPrivateKey() { +} + +RSAPrivateKey::~RSAPrivateKey() { +} + +bool RSAPrivateKey::ExportPrivateKey(std::vector<uint8>* output) { + NOTIMPLEMENTED(); + return false; +} + +bool RSAPrivateKey::ExportPublicKey(std::vector<uint8>* output) { + NOTIMPLEMENTED(); + return false; +} + +} // namespace base diff --git a/base/crypto/signature_creator_openssl.cc b/base/crypto/signature_creator_openssl.cc new file mode 100644 index 0000000..5d70f01 --- /dev/null +++ b/base/crypto/signature_creator_openssl.cc @@ -0,0 +1,32 @@ +// Copyright (c) 2009 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/crypto/signature_creator.h" + +#include "base/logging.h" + +namespace base { + +// static +SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key) { + return NULL; +} + +SignatureCreator::SignatureCreator() { +} + +SignatureCreator::~SignatureCreator() { +} + +bool SignatureCreator::Update(const uint8* data_part, int data_part_len) { + NOTIMPLEMENTED(); + return false; +} + +bool SignatureCreator::Final(std::vector<uint8>* signature) { + NOTIMPLEMENTED(); + return false; +} + +} // namespace base diff --git a/base/crypto/signature_verifier_openssl.cc b/base/crypto/signature_verifier_openssl.cc new file mode 100644 index 0000000..49b5e07 --- /dev/null +++ b/base/crypto/signature_verifier_openssl.cc @@ -0,0 +1,41 @@ +// Copyright (c) 2010 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/crypto/signature_verifier.h" + +#include "base/logging.h" + +namespace base { + +SignatureVerifier::SignatureVerifier() { +} + +SignatureVerifier::~SignatureVerifier() { +} + +bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len) { + NOTIMPLEMENTED(); + return false; +} + +void SignatureVerifier::VerifyUpdate(const uint8* data_part, + int data_part_len) { + NOTIMPLEMENTED(); +} + +bool SignatureVerifier::VerifyFinal() { + NOTIMPLEMENTED(); + return false; +} + +void SignatureVerifier::Reset() { + NOTIMPLEMENTED(); +} + +} // namespace base diff --git a/base/crypto/symmetric_key.h b/base/crypto/symmetric_key.h index d7259be..3f2be76 100644 --- a/base/crypto/symmetric_key.h +++ b/base/crypto/symmetric_key.h @@ -65,7 +65,10 @@ class SymmetricKey { bool GetRawKey(std::string* raw_key); private: -#if defined(USE_NSS) +#if defined(USE_OPENSSL) + // TODO(joth): Add a constructor that accepts OpenSSL symmetric key data, and + // the appropriate data members to store it in. +#elif defined(USE_NSS) explicit SymmetricKey(PK11SymKey* key); ScopedPK11SymKey key_; #elif defined(OS_MACOSX) diff --git a/base/crypto/symmetric_key_openssl.cc b/base/crypto/symmetric_key_openssl.cc new file mode 100644 index 0000000..591252d --- /dev/null +++ b/base/crypto/symmetric_key_openssl.cc @@ -0,0 +1,43 @@ +// Copyright (c) 2010 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/crypto/symmetric_key.h" + +#include "base/logging.h" + +namespace base { + +SymmetricKey::~SymmetricKey() { +} + +// static +SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm, + size_t key_size_in_bits) { + NOTIMPLEMENTED(); + return NULL; +} + +// static +SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm, + const std::string& password, + const std::string& salt, + size_t iterations, + size_t key_size_in_bits) { + NOTIMPLEMENTED(); + return NULL; +} + +// static +SymmetricKey* SymmetricKey::Import(Algorithm algorithm, + const std::string& raw_key) { + NOTIMPLEMENTED(); + return NULL; +} + +bool SymmetricKey::GetRawKey(std::string* raw_key) { + NOTIMPLEMENTED(); + return false; +} + +} // namespace base diff --git a/base/data/valgrind/DIRECTORY_MOVED b/base/data/valgrind/DIRECTORY_MOVED new file mode 100644 index 0000000..96449fe --- /dev/null +++ b/base/data/valgrind/DIRECTORY_MOVED @@ -0,0 +1,4 @@ +For those who got a merge conflict in this directory on update: +The files from this dir has been moved to tools/valgrind/gtest_exclude + +TODO(timurrrr): remove this in a couple of weeks diff --git a/base/data/valgrind/base_unittests.gtest-drmemory_win32.txt b/base/data/valgrind/base_unittests.gtest-drmemory_win32.txt deleted file mode 100644 index b73fa61..0000000 --- a/base/data/valgrind/base_unittests.gtest-drmemory_win32.txt +++ /dev/null @@ -1,11 +0,0 @@ -# Dr. Memory crashes on some COM calls: -# http://code.google.com/p/drmemory/issues/detail?id=21 -WMI* - -# TODO(timurrrr) investigate the failures and enable these tests one-by-one. -RSA* -GmockTest.* -EtwTrace* -StatsTableTest.* -ProcessUtilTest.EnableLFH -ScopedNativeLibrary.Basic diff --git a/base/data/valgrind/base_unittests.gtest-tsan.txt b/base/data/valgrind/base_unittests.gtest-tsan.txt deleted file mode 100644 index 1f81bea..0000000 --- a/base/data/valgrind/base_unittests.gtest-tsan.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Don't run this test under TSan, it takes ~1-2 minutes to pass. -ProcessUtilTest.GetAppOutputRestrictedNoZombies - -# Don't run Memcheck sanity tests under ThreadSanitizer since they can -# corrupt memory. -ToolsSanityTest.*Memory* -ToolsSanityTest.*Delete* - -# TSan doesn't understand SharedMemory locks, see http://crbug.com/45083 -StatsTableTest.*MultipleThreads diff --git a/base/data/valgrind/base_unittests.gtest-tsan_mac.txt b/base/data/valgrind/base_unittests.gtest-tsan_mac.txt deleted file mode 100644 index 7ee06a1..0000000 --- a/base/data/valgrind/base_unittests.gtest-tsan_mac.txt +++ /dev/null @@ -1,2 +0,0 @@ -# http://crbug.com/29855. -StackTrace.OutputToStream diff --git a/base/data/valgrind/base_unittests.gtest-tsan_win32.txt b/base/data/valgrind/base_unittests.gtest-tsan_win32.txt deleted file mode 100644 index 0207c09..0000000 --- a/base/data/valgrind/base_unittests.gtest-tsan_win32.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Occasionally fails under TSan, see http://crbug.com/54229 -ProcessUtilTest.CalcFreeMemory - -# This file is copied from Valgrind-on-Wine filter -# TODO(timurrrr): include/investigate the listed tests one-by-one -EtwTraceControllerTest.EnableDisable -EtwTraceConsumer*Test.* -EtwTraceProvider*Test.* -JSONReaderTest.Reading -TimeTicks.* -WMIUtilTest.* - -# Too slow under TSan -RSAPrivateKeyUnitTest.* -ConditionVariableTest.LargeFastTaskTest diff --git a/base/data/valgrind/base_unittests.gtest.txt b/base/data/valgrind/base_unittests.gtest.txt deleted file mode 100644 index 173defa..0000000 --- a/base/data/valgrind/base_unittests.gtest.txt +++ /dev/null @@ -1,22 +0,0 @@ -# This test currently times out in valgrind, see http://crbug.com/9194 -WatchdogTest.AlarmTest - -# These tests occassionally hangs under Valgrind on Mac. valgrind-darwin r9573 -# Revisit with better valgrind. -# Valgrind bug: https://bugs.kde.org/show_bug.cgi?id=189661 -TimerTest.RepeatingTimer -TimerTest.RepeatingTimer_Cancel - -# Crashes occasionally, see http://crbug.com/7477 -ObserverListThreadSafeTest.CrossThreadObserver -ObserverListThreadSafeTest.CrossThreadNotifications - -# Hangs sometimes on linux, see http://crbug.com/22138 -ClipboardTest.* - -# These tests trigger a CHECK so they will leak memory. They don't test -# anything else, so just disable them on valgrind. Bug 28179. -OutOfMemoryDeathTest.* - -# Flaky under Valgrind, see http://crbug.com/55517 -PlatformFile.TouchGetInfoPlatformFile diff --git a/base/data/valgrind/base_unittests.gtest_mac.txt b/base/data/valgrind/base_unittests.gtest_mac.txt deleted file mode 100644 index 46fbf47..0000000 --- a/base/data/valgrind/base_unittests.gtest_mac.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Fails on Valgrind/Mac, see http://crbug.com/43972 -ConditionVariableTest.LargeFastTaskTest - -# Fails on Valgrind/Mac due to missing syscall wrapper -# for the symlink() syscall. See http://crbug.com/44001 -FileUtilTest.NormalizeFilePathSymlinks - -# Fails on Valgrind/Mac, see http://crbug.com/53196 -CancellationFlagTest.SetOnDifferentThreadDeathTest diff --git a/base/data/valgrind/base_unittests.gtest_win32.txt b/base/data/valgrind/base_unittests.gtest_win32.txt deleted file mode 100644 index 525648c..0000000 --- a/base/data/valgrind/base_unittests.gtest_win32.txt +++ /dev/null @@ -1,7 +0,0 @@ -# Too slow under Valgrind/Wine and TSan/Windows -TimeTicks.WinRollover - -# Very sensitive to slowdown -TimeTicks.Deltas -TimeTicks.HighResNow -TimerTest.RepeatingTimer* diff --git a/base/data/valgrind/base_unittests.gtest_wine.txt b/base/data/valgrind/base_unittests.gtest_wine.txt deleted file mode 100644 index 17d3085..0000000 --- a/base/data/valgrind/base_unittests.gtest_wine.txt +++ /dev/null @@ -1,96 +0,0 @@ -# crash Crashes in Wine -# crash-valgrind Crashes in Wine + Valgrind -# dontcare Safe to ignore -# dontcare-hangwin Ignore, hangs on Windows too -# dontcare-winfail Ignore, fails on Windows too -# dontcare-flaky Ignore, flaky test -# dontcare-hang Ignore, hangs we don't care about -# fail Fails, needs triaging or needs to be fixed -# fail-valgrind Fails only under Valgrind -# fail_wine_vmware Fails in Wine under VMware? TODO(dank) clarify -# flaky-valgrind Flaky under Valgrind, needs investigation -# hang Test that hangs for some reason -# hang-valgrind Test that hangs under valgrind, or just takes too long - -# dontcare -BaseWinUtilTest.FormatMessageW - -# fail-valgrind -# fails under wine + valgrind TODO(thestig): investigate -ConditionVariableTest.LargeFastTaskTest - -# hang -# http://bugs.winehq.org/show_bug.cgi?id=20946, advapi32.ControlTrace() not yet implemented -EtwTraceControllerTest.EnableDisable - -# crash -# http://bugs.winehq.org/show_bug.cgi?id=20946, advapi32.OpenTrace() unimplemented -EtwTraceConsumer*Test.* - -# crash -# http://bugs.winehq.org/show_bug.cgi?id=20946, advapi32.RegisterTraceGuids() unimplemented -EtwTraceProvider*Test.* - -# dontcare -FileUtilTest.CountFilesCreatedAfter - -# dontcare -FileUtilTest.GetFileCreationLocalTime - -# fail -# http://bugs.winehq.org/show_bug.cgi?id=20340 -HMACTest.HMACObjectReuse - -# fail -# http://bugs.winehq.org/show_bug.cgi?id=20340 -HMACTest.HmacSafeBrowsingResponseTest - -# fail -# http://bugs.winehq.org/show_bug.cgi?id=20340 -HMACTest.RFC2202TestCases - -# hang-valgrind -# not really a hang, takes 400 seconds -JSONReaderTest.Reading - -# dontcare -# Alexandre Julliard triaged -PEImageTest.EnumeratesPE - -# fail-valgrind -# fails under wine + valgrind TODO(thestig): investigate -ProcessUtilTest.CalcFreeMemory - -# fail-valgrind -# fails under wine + valgrind TODO(thestig): investigate -ProcessUtilTest.KillSlowChild - -# fail-valgrind -# fails under wine + valgrind TODO(thestig): investigate -ProcessUtilTest.SpawnChild - -# hang-valgrind -# not really a hang, takes 300 seconds -RSAPrivateKeyUnitTest.InitRandomTest - -# fail_wine_vmware -RSAPrivateKeyUnitTest.ShortIntegers - -# dontcare-flaky -# http://bugs.winehq.org/show_bug.cgi?id=20606 -StatsTableTest.MultipleProcesses - -# flaky-valgrind -# flaky, timing issues? TODO(thestig): investigate -StatsTableTest.StatsCounterTimer - -# fail-valgrind -# fails under wine + valgrind TODO(thestig): investigate -StatsTableTest.StatsRate - -# fail-valgrind -# fails under wine + valgrind TODO(thestig): investigate -StatsTableTest.StatsScope - -# dontcare -WMIUtilTest.* diff --git a/base/debug/debugger.cc b/base/debug/debugger.cc new file mode 100644 index 0000000..9ca7e8d --- /dev/null +++ b/base/debug/debugger.cc @@ -0,0 +1,25 @@ +// Copyright (c) 2010 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/debug/debugger.h" + +#include "base/platform_thread.h" + +namespace base { +namespace debug { + +bool WaitForDebugger(int wait_seconds, bool silent) { + for (int i = 0; i < wait_seconds * 10; ++i) { + if (BeingDebugged()) { + if (!silent) + BreakDebugger(); + return true; + } + PlatformThread::Sleep(100); + } + return false; +} + +} // namespace debug +} // namespace base diff --git a/base/debug/debugger.h b/base/debug/debugger.h new file mode 100644 index 0000000..008d77d --- /dev/null +++ b/base/debug/debugger.h @@ -0,0 +1,39 @@ +// Copyright (c) 2010 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 is a cross platform interface for helper functions related to +// debuggers. You should use this to test if you're running under a debugger, +// and if you would like to yield (breakpoint) into the debugger. + +#ifndef BASE_DEBUG_DEBUGGER_H +#define BASE_DEBUG_DEBUGGER_H +#pragma once + +namespace base { +namespace debug { + +// Starts the registered system-wide JIT debugger to attach it to specified +// process. +bool SpawnDebuggerOnProcess(unsigned process_id); + +// Waits wait_seconds seconds for a debugger to attach to the current process. +// When silent is false, an exception is thrown when a debugger is detected. +bool WaitForDebugger(int wait_seconds, bool silent); + +// Returns true if the given process is being run under a debugger. +// +// On OS X, the underlying mechanism doesn't work when the sandbox is enabled. +// To get around this, this function caches its value. +// +// WARNING: Because of this, on OS X, a call MUST be made to this function +// BEFORE the sandbox is enabled. +bool BeingDebugged(); + +// Break into the debugger, assumes a debugger is present. +void BreakDebugger(); + +} // namespace debug +} // namespace base + +#endif // BASE_DEBUG_DEBUGGER_H diff --git a/base/debug/debugger_posix.cc b/base/debug/debugger_posix.cc new file mode 100644 index 0000000..a5ab066 --- /dev/null +++ b/base/debug/debugger_posix.cc @@ -0,0 +1,165 @@ +// Copyright (c) 2010 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/debug/debugger.h" + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/types.h> +#include <unistd.h> + +#include <string> +#include <vector> + +#if defined(__GLIBCXX__) +#include <cxxabi.h> +#endif + +#if defined(OS_MACOSX) +#include <AvailabilityMacros.h> +#endif + +#include <iostream> + +#include "base/basictypes.h" +#include "base/compat_execinfo.h" +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/safe_strerror_posix.h" +#include "base/scoped_ptr.h" +#include "base/string_piece.h" +#include "base/stringprintf.h" + +#if defined(USE_SYMBOLIZE) +#include "base/third_party/symbolize/symbolize.h" +#endif + +namespace base { +namespace debug { + +bool SpawnDebuggerOnProcess(unsigned /* process_id */) { + NOTIMPLEMENTED(); + return false; +} + +#if defined(OS_MACOSX) + +// Based on Apple's recommended method as described in +// http://developer.apple.com/qa/qa2004/qa1361.html +bool BeingDebugged() { + // If the process is sandboxed then we can't use the sysctl, so cache the + // value. + static bool is_set = false; + static bool being_debugged = false; + + if (is_set) { + return being_debugged; + } + + // Initialize mib, which tells sysctl what info we want. In this case, + // we're looking for information about a specific process ID. + int mib[] = { + CTL_KERN, + KERN_PROC, + KERN_PROC_PID, + getpid() + }; + + // Caution: struct kinfo_proc is marked __APPLE_API_UNSTABLE. The source and + // binary interfaces may change. + struct kinfo_proc info; + size_t info_size = sizeof(info); + + int sysctl_result = sysctl(mib, arraysize(mib), &info, &info_size, NULL, 0); + DCHECK_EQ(sysctl_result, 0); + if (sysctl_result != 0) { + is_set = true; + being_debugged = false; + return being_debugged; + } + + // This process is being debugged if the P_TRACED flag is set. + is_set = true; + being_debugged = (info.kp_proc.p_flag & P_TRACED) != 0; + return being_debugged; +} + +#elif defined(OS_LINUX) + +// We can look in /proc/self/status for TracerPid. We are likely used in crash +// handling, so we are careful not to use the heap or have side effects. +// Another option that is common is to try to ptrace yourself, but then we +// can't detach without forking(), and that's not so great. +// static +bool BeingDebugged() { + int status_fd = open("/proc/self/status", O_RDONLY); + if (status_fd == -1) + return false; + + // We assume our line will be in the first 1024 characters and that we can + // read this much all at once. In practice this will generally be true. + // This simplifies and speeds up things considerably. + char buf[1024]; + + ssize_t num_read = HANDLE_EINTR(read(status_fd, buf, sizeof(buf))); + if (HANDLE_EINTR(close(status_fd)) < 0) + return false; + + if (num_read <= 0) + return false; + + StringPiece status(buf, num_read); + StringPiece tracer("TracerPid:\t"); + + StringPiece::size_type pid_index = status.find(tracer); + if (pid_index == StringPiece::npos) + return false; + + // Our pid is 0 without a debugger, assume this for any pid starting with 0. + pid_index += tracer.size(); + return pid_index < status.size() && status[pid_index] != '0'; +} + +#elif defined(OS_FREEBSD) + +bool DebugUtil::BeingDebugged() { + // TODO(benl): can we determine this under FreeBSD? + NOTIMPLEMENTED(); + return false; +} + +#endif // defined(OS_FREEBSD) + +// We want to break into the debugger in Debug mode, and cause a crash dump in +// Release mode. Breakpad behaves as follows: +// +// +-------+-----------------+-----------------+ +// | OS | Dump on SIGTRAP | Dump on SIGABRT | +// +-------+-----------------+-----------------+ +// | Linux | N | Y | +// | Mac | Y | N | +// +-------+-----------------+-----------------+ +// +// Thus we do the following: +// Linux: Debug mode, send SIGTRAP; Release mode, send SIGABRT. +// Mac: Always send SIGTRAP. + +#if defined(NDEBUG) && !defined(OS_MACOSX) +#define DEBUG_BREAK() abort() +#elif defined(ARCH_CPU_ARM_FAMILY) +#define DEBUG_BREAK() asm("bkpt 0") +#else +#define DEBUG_BREAK() asm("int3") +#endif + +void BreakDebugger() { + DEBUG_BREAK(); +} + +} // namespace debug +} // namespace base diff --git a/base/debug/debugger_win.cc b/base/debug/debugger_win.cc new file mode 100644 index 0000000..d1d47cd --- /dev/null +++ b/base/debug/debugger_win.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2010 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/debug/debugger.h" + +#include <windows.h> +#include <dbghelp.h> + +#include "base/basictypes.h" +#include "base/debug_util.h" +#include "base/logging.h" + +namespace base { +namespace debug { + +namespace { + +// Minimalist key reader. +// Note: Does not use the CRT. +bool RegReadString(HKEY root, const wchar_t* subkey, + const wchar_t* value_name, wchar_t* buffer, int* len) { + HKEY key = NULL; + DWORD res = RegOpenKeyEx(root, subkey, 0, KEY_READ, &key); + if (ERROR_SUCCESS != res || key == NULL) + return false; + + DWORD type = 0; + DWORD buffer_size = *len * sizeof(wchar_t); + // We don't support REG_EXPAND_SZ. + res = RegQueryValueEx(key, value_name, NULL, &type, + reinterpret_cast<BYTE*>(buffer), &buffer_size); + if (ERROR_SUCCESS == res && buffer_size != 0 && type == REG_SZ) { + // Make sure the buffer is NULL terminated. + buffer[*len - 1] = 0; + *len = lstrlen(buffer); + RegCloseKey(key); + return true; + } + RegCloseKey(key); + return false; +} + +// Replaces each "%ld" in input per a value. Not efficient but it works. +// Note: Does not use the CRT. +bool StringReplace(const wchar_t* input, int value, wchar_t* output, + int output_len) { + memset(output, 0, output_len*sizeof(wchar_t)); + int input_len = lstrlen(input); + + for (int i = 0; i < input_len; ++i) { + int current_output_len = lstrlen(output); + + if (input[i] == L'%' && input[i + 1] == L'l' && input[i + 2] == L'd') { + // Make sure we have enough place left. + if ((current_output_len + 12) >= output_len) + return false; + + // Cheap _itow(). + wsprintf(output+current_output_len, L"%d", value); + i += 2; + } else { + if (current_output_len >= output_len) + return false; + output[current_output_len] = input[i]; + } + } + return true; +} + +} // namespace + +// Note: Does not use the CRT. +bool SpawnDebuggerOnProcess(unsigned process_id) { + wchar_t reg_value[1026]; + int len = arraysize(reg_value); + if (RegReadString(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", + L"Debugger", reg_value, &len)) { + wchar_t command_line[1026]; + if (StringReplace(reg_value, process_id, command_line, + arraysize(command_line))) { + // We don't mind if the debugger is present because it will simply fail + // to attach to this process. + STARTUPINFO startup_info = {0}; + startup_info.cb = sizeof(startup_info); + PROCESS_INFORMATION process_info = {0}; + + if (CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL, + &startup_info, &process_info)) { + CloseHandle(process_info.hThread); + WaitForInputIdle(process_info.hProcess, 10000); + CloseHandle(process_info.hProcess); + return true; + } + } + } + return false; +} + +bool BeingDebugged() { + return ::IsDebuggerPresent() != 0; +} + +void BreakDebugger() { + if (DebugUtil::AreDialogsSuppressed()) + _exit(1); + __debugbreak(); +} + +} // namespace debug +} // namespace base diff --git a/base/leak_annotations.h b/base/debug/leak_annotations.h index dd8280b..e1086fe 100644 --- a/base/leak_annotations.h +++ b/base/debug/leak_annotations.h @@ -1,9 +1,9 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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_LEAK_ANNOTATIONS_H_ -#define BASE_LEAK_ANNOTATIONS_H_ +#ifndef BASE_DEBUG_LEAK_ANNOTATIONS_H_ +#define BASE_DEBUG_LEAK_ANNOTATIONS_H_ #pragma once #include "build/build_config.h" @@ -25,4 +25,4 @@ #endif -#endif // BASE_LEAK_ANNOTATIONS_H_ +#endif // BASE_DEBUG_LEAK_ANNOTATIONS_H_ diff --git a/base/leak_tracker.h b/base/debug/leak_tracker.h index 96d8773..8af82a9 100644 --- a/base/leak_tracker.h +++ b/base/debug/leak_tracker.h @@ -1,9 +1,9 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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_LEAK_TRACKER_H_ -#define BASE_LEAK_TRACKER_H_ +#ifndef BASE_DEBUG_LEAK_TRACKER_H_ +#define BASE_DEBUG_LEAK_TRACKER_H_ #pragma once // Only enable leak tracking in debug builds. @@ -12,7 +12,7 @@ #endif #ifdef ENABLE_LEAK_TRACKER -#include "base/debug_util.h" +#include "base/debug/stack_trace.h" #include "base/linked_list.h" #include "base/logging.h" #endif // ENABLE_LEAK_TRACKER @@ -45,6 +45,7 @@ // If ENABLE_LEAK_TRACKER is not defined, then the check has no effect. namespace base { +namespace debug { #ifndef ENABLE_LEAK_TRACKER @@ -127,6 +128,7 @@ class LeakTracker : public LinkNode<LeakTracker<T> > { #endif // ENABLE_LEAK_TRACKER +} // namespace debug } // namespace base -#endif // BASE_LEAK_TRACKER_H_ +#endif // BASE_DEBUG_LEAK_TRACKER_H_ diff --git a/base/debug/leak_tracker_unittest.cc b/base/debug/leak_tracker_unittest.cc new file mode 100644 index 0000000..2e6a9a5 --- /dev/null +++ b/base/debug/leak_tracker_unittest.cc @@ -0,0 +1,113 @@ +// Copyright (c) 2010 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/debug/leak_tracker.h" +#include "base/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace debug { + +namespace { + +class ClassA { + private: + LeakTracker<ClassA> leak_tracker_; +}; + +class ClassB { + private: + LeakTracker<ClassB> leak_tracker_; +}; + +#ifndef ENABLE_LEAK_TRACKER + +// If leak tracking is disabled, we should do nothing. +TEST(LeakTrackerTest, NotEnabled) { + EXPECT_EQ(-1, LeakTracker<ClassA>::NumLiveInstances()); + EXPECT_EQ(-1, LeakTracker<ClassB>::NumLiveInstances()); + + // Use scoped_ptr so compiler doesn't complain about unused variables. + scoped_ptr<ClassA> a1(new ClassA); + scoped_ptr<ClassB> b1(new ClassB); + scoped_ptr<ClassB> b2(new ClassB); + + EXPECT_EQ(-1, LeakTracker<ClassA>::NumLiveInstances()); + EXPECT_EQ(-1, LeakTracker<ClassB>::NumLiveInstances()); +} + +#else + +TEST(LeakTrackerTest, Basic) { + { + ClassA a1; + + EXPECT_EQ(1, LeakTracker<ClassA>::NumLiveInstances()); + EXPECT_EQ(0, LeakTracker<ClassB>::NumLiveInstances()); + + ClassB b1; + ClassB b2; + + EXPECT_EQ(1, LeakTracker<ClassA>::NumLiveInstances()); + EXPECT_EQ(2, LeakTracker<ClassB>::NumLiveInstances()); + + scoped_ptr<ClassA> a2(new ClassA); + + EXPECT_EQ(2, LeakTracker<ClassA>::NumLiveInstances()); + EXPECT_EQ(2, LeakTracker<ClassB>::NumLiveInstances()); + + a2.reset(); + + EXPECT_EQ(1, LeakTracker<ClassA>::NumLiveInstances()); + EXPECT_EQ(2, LeakTracker<ClassB>::NumLiveInstances()); + } + + EXPECT_EQ(0, LeakTracker<ClassA>::NumLiveInstances()); + EXPECT_EQ(0, LeakTracker<ClassB>::NumLiveInstances()); +} + +// Try some orderings of create/remove to hit different cases in the linked-list +// assembly. +TEST(LeakTrackerTest, LinkedList) { + EXPECT_EQ(0, LeakTracker<ClassB>::NumLiveInstances()); + + scoped_ptr<ClassA> a1(new ClassA); + scoped_ptr<ClassA> a2(new ClassA); + scoped_ptr<ClassA> a3(new ClassA); + scoped_ptr<ClassA> a4(new ClassA); + + EXPECT_EQ(4, LeakTracker<ClassA>::NumLiveInstances()); + + // Remove the head of the list (a1). + a1.reset(); + EXPECT_EQ(3, LeakTracker<ClassA>::NumLiveInstances()); + + // Remove the tail of the list (a4). + a4.reset(); + EXPECT_EQ(2, LeakTracker<ClassA>::NumLiveInstances()); + + // Append to the new tail of the list (a3). + scoped_ptr<ClassA> a5(new ClassA); + EXPECT_EQ(3, LeakTracker<ClassA>::NumLiveInstances()); + + a2.reset(); + a3.reset(); + + EXPECT_EQ(1, LeakTracker<ClassA>::NumLiveInstances()); + + a5.reset(); + EXPECT_EQ(0, LeakTracker<ClassA>::NumLiveInstances()); +} + +TEST(LeakTrackerTest, NoOpCheckForLeaks) { + // There are no live instances of ClassA, so this should do nothing. + LeakTracker<ClassA>::CheckForLeaks(); +} + +#endif // ENABLE_LEAK_TRACKER + +} // namespace + +} // namespace debug +} // namespace base diff --git a/base/debug/stack_trace.cc b/base/debug/stack_trace.cc new file mode 100644 index 0000000..5be4c5c --- /dev/null +++ b/base/debug/stack_trace.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2010 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/debug/stack_trace.h" + +namespace base { +namespace debug { + +StackTrace::~StackTrace() { +} + +const void *const *StackTrace::Addresses(size_t* count) { + *count = count_; + if (count_) + return trace_; + return NULL; +} + +} // namespace debug +} // namespace base diff --git a/base/debug/stack_trace.h b/base/debug/stack_trace.h new file mode 100644 index 0000000..8afc32c --- /dev/null +++ b/base/debug/stack_trace.h @@ -0,0 +1,63 @@ +// Copyright (c) 2010 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_DEBUG_STACK_TRACE_H_ +#define BASE_DEBUG_STACK_TRACE_H_ +#pragma once + +#include <iosfwd> + +#include "build/build_config.h" + +#if defined(OS_WIN) +struct _EXCEPTION_POINTERS; +#endif + +namespace base { +namespace debug { + +// A stacktrace can be helpful in debugging. For example, you can include a +// stacktrace member in a object (probably around #ifndef NDEBUG) so that you +// can later see where the given object was created from. +class StackTrace { + public: + // Creates a stacktrace from the current location. + StackTrace(); + +#if defined(OS_WIN) + // Creates a stacktrace for an exception. + // Note: this function will throw an import not found (StackWalk64) exception + // on system without dbghelp 5.1. + StackTrace(_EXCEPTION_POINTERS* exception_pointers); +#endif + + // Copying and assignment are allowed with the default functions. + + ~StackTrace(); + + // Gets an array of instruction pointer values. |*count| will be set to the + // number of elements in the returned array. + const void* const* Addresses(size_t* count); + + // Prints a backtrace to stderr + void PrintBacktrace(); + + // Resolves backtrace to symbols and write to stream. + void OutputToStream(std::ostream* os); + + private: + // From http://msdn.microsoft.com/en-us/library/bb204633.aspx, + // the sum of FramesToSkip and FramesToCapture must be less than 63, + // so set it to 62. Even if on POSIX it could be a larger value, it usually + // doesn't give much more information. + static const int kMaxTraces = 62; + + void* trace_[kMaxTraces]; + int count_; +}; + +} // namespace debug +} // namespace base + +#endif // BASE_DEBUG_STACK_TRACE_H_ diff --git a/base/debug_util_posix.cc b/base/debug/stack_trace_posix.cc index 4c5ec80..e4b0ef2 100644 --- a/base/debug_util_posix.cc +++ b/base/debug/stack_trace_posix.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/debug_util.h" +#include "base/debug/stack_trace.h" #include <errno.h> #include <fcntl.h> @@ -39,7 +39,11 @@ #include "base/third_party/symbolize/symbolize.h" #endif +namespace base { +namespace debug { + namespace { + // The prefix used for mangled symbols, per the Itanium C++ ABI: // http://www.codesourcery.com/cxx-abi/abi.html#mangling const char kMangledSymbolPrefix[] = "_Z"; @@ -104,9 +108,6 @@ void DemangleSymbols(std::string* text) { bool GetBacktraceStrings(void **trace, int size, std::vector<std::string>* trace_strings, std::string* error_message) { -#ifdef ANDROID - return false; -#else bool symbolized = false; #if defined(USE_SYMBOLIZE) @@ -144,143 +145,16 @@ bool GetBacktraceStrings(void **trace, int size, #endif // defined(USE_SYMBOLIZE) return symbolized; -#endif // ANDROID } } // namespace -// static -bool DebugUtil::SpawnDebuggerOnProcess(unsigned /* process_id */) { - NOTIMPLEMENTED(); - return false; -} - -#if defined(OS_MACOSX) - -// Based on Apple's recommended method as described in -// http://developer.apple.com/qa/qa2004/qa1361.html -// static -bool DebugUtil::BeingDebugged() { - // If the process is sandboxed then we can't use the sysctl, so cache the - // value. - static bool is_set = false; - static bool being_debugged = false; - - if (is_set) { - return being_debugged; - } - - // Initialize mib, which tells sysctl what info we want. In this case, - // we're looking for information about a specific process ID. - int mib[] = { - CTL_KERN, - KERN_PROC, - KERN_PROC_PID, - getpid() - }; - - // Caution: struct kinfo_proc is marked __APPLE_API_UNSTABLE. The source and - // binary interfaces may change. - struct kinfo_proc info; - size_t info_size = sizeof(info); - - int sysctl_result = sysctl(mib, arraysize(mib), &info, &info_size, NULL, 0); - DCHECK_EQ(sysctl_result, 0); - if (sysctl_result != 0) { - is_set = true; - being_debugged = false; - return being_debugged; - } - - // This process is being debugged if the P_TRACED flag is set. - is_set = true; - being_debugged = (info.kp_proc.p_flag & P_TRACED) != 0; - return being_debugged; -} - -#elif defined(OS_LINUX) - -// We can look in /proc/self/status for TracerPid. We are likely used in crash -// handling, so we are careful not to use the heap or have side effects. -// Another option that is common is to try to ptrace yourself, but then we -// can't detach without forking(), and that's not so great. -// static -bool DebugUtil::BeingDebugged() { - int status_fd = open("/proc/self/status", O_RDONLY); - if (status_fd == -1) - return false; - - // We assume our line will be in the first 1024 characters and that we can - // read this much all at once. In practice this will generally be true. - // This simplifies and speeds up things considerably. - char buf[1024]; - - ssize_t num_read = HANDLE_EINTR(read(status_fd, buf, sizeof(buf))); - if (HANDLE_EINTR(close(status_fd)) < 0) - return false; - - if (num_read <= 0) - return false; - - base::StringPiece status(buf, num_read); - base::StringPiece tracer("TracerPid:\t"); - - base::StringPiece::size_type pid_index = status.find(tracer); - if (pid_index == base::StringPiece::npos) - return false; - - // Our pid is 0 without a debugger, assume this for any pid starting with 0. - pid_index += tracer.size(); - return pid_index < status.size() && status[pid_index] != '0'; -} - -#elif defined(OS_FREEBSD) - -bool DebugUtil::BeingDebugged() { - // TODO(benl): can we determine this under FreeBSD? - NOTIMPLEMENTED(); - return false; -} - -#endif // defined(OS_FREEBSD) - -// We want to break into the debugger in Debug mode, and cause a crash dump in -// Release mode. Breakpad behaves as follows: -// -// +-------+-----------------+-----------------+ -// | OS | Dump on SIGTRAP | Dump on SIGABRT | -// +-------+-----------------+-----------------+ -// | Linux | N | Y | -// | Mac | Y | N | -// +-------+-----------------+-----------------+ -// -// Thus we do the following: -// Linux: Debug mode, send SIGTRAP; Release mode, send SIGABRT. -// Mac: Always send SIGTRAP. - -#if defined(NDEBUG) && !defined(OS_MACOSX) -#define DEBUG_BREAK() abort() -#elif defined(ARCH_CPU_ARM_FAMILY) -#define DEBUG_BREAK() asm("bkpt 0") -#else -#define DEBUG_BREAK() asm("int3") -#endif - -// static -void DebugUtil::BreakDebugger() { - DEBUG_BREAK(); -} - StackTrace::StackTrace() { -#if (defined(OS_MACOSX) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) || defined(ANDROID) -#if defined(ANDROID) - return; -#else +#if defined(OS_MACOSX) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 if (backtrace == NULL) { count_ = 0; return; } -#endif // ANDROID #endif // Though the backtrace API man page does not list any possible negative // return values, we take no chance. @@ -288,13 +162,9 @@ StackTrace::StackTrace() { } void StackTrace::PrintBacktrace() { -#if (defined(OS_MACOSX) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) || defined(ANDROID) -#if defined(ANDROID) - return; -#else +#if defined(OS_MACOSX) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 if (backtrace_symbols_fd == NULL) return; -#endif // ANDROID #endif fflush(stderr); std::vector<std::string> trace_strings; @@ -305,13 +175,9 @@ void StackTrace::PrintBacktrace() { } void StackTrace::OutputToStream(std::ostream* os) { -#if (defined(OS_MACOSX) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) || defined(ANDROID) -#if defined(ANDROID) - return; -#else +#if defined(OS_MACOSX) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 if (backtrace_symbols == NULL) return; -#endif // ANDROID #endif std::vector<std::string> trace_strings; std::string error_message; @@ -328,3 +194,6 @@ void StackTrace::OutputToStream(std::ostream* os) { (*os) << "\t" << trace_strings[i] << "\n"; } } + +} // namespace debug +} // namespace base diff --git a/base/debug_util_unittest.cc b/base/debug/stack_trace_unittest.cc index 8d0deab..ce5f313 100644 --- a/base/debug_util_unittest.cc +++ b/base/debug/stack_trace_unittest.cc @@ -5,14 +5,22 @@ #include <sstream> #include <string> -#include "base/debug_util.h" +#include "base/debug/stack_trace.h" #include "base/logging.h" #include "testing/gtest/include/gtest/gtest.h" +namespace base { +namespace debug { + // Note: On Linux, this test currently only fully works on Debug builds. // See comments in the #ifdef soup if you intend to change this. -// Flaky, crbug.com/32070. -TEST(StackTrace, FLAKY_OutputToStream) { +#if defined(OS_WIN) +// Always fails on Windows: crbug.com/32070 +#define MAYBE_OutputToStream FAILS_OutputToStream +#else +#define MAYBE_OutputToStream OutputToStream +#endif +TEST(StackTrace, MAYBE_OutputToStream) { StackTrace trace; // Dump the trace into a string. @@ -104,3 +112,6 @@ TEST(StackTrace, DebugOutputToStream) { TEST(StackTrace, DebugPrintBacktrace) { StackTrace().PrintBacktrace(); } + +} // namespace debug +} // namespace base diff --git a/base/debug_util_win.cc b/base/debug/stack_trace_win.cc index b60ba31..653d234 100644 --- a/base/debug_util_win.cc +++ b/base/debug/stack_trace_win.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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/debug_util.h" +#include "base/debug/stack_trace.h" #include <windows.h> #include <dbghelp.h> @@ -14,59 +14,10 @@ #include "base/logging.h" #include "base/singleton.h" -namespace { - -// Minimalist key reader. -// Note: Does not use the CRT. -bool RegReadString(HKEY root, const wchar_t* subkey, - const wchar_t* value_name, wchar_t* buffer, int* len) { - HKEY key = NULL; - DWORD res = RegOpenKeyEx(root, subkey, 0, KEY_READ, &key); - if (ERROR_SUCCESS != res || key == NULL) - return false; - - DWORD type = 0; - DWORD buffer_size = *len * sizeof(wchar_t); - // We don't support REG_EXPAND_SZ. - res = RegQueryValueEx(key, value_name, NULL, &type, - reinterpret_cast<BYTE*>(buffer), &buffer_size); - if (ERROR_SUCCESS == res && buffer_size != 0 && type == REG_SZ) { - // Make sure the buffer is NULL terminated. - buffer[*len - 1] = 0; - *len = lstrlen(buffer); - RegCloseKey(key); - return true; - } - RegCloseKey(key); - return false; -} - -// Replaces each "%ld" in input per a value. Not efficient but it works. -// Note: Does not use the CRT. -bool StringReplace(const wchar_t* input, int value, wchar_t* output, - int output_len) { - memset(output, 0, output_len*sizeof(wchar_t)); - int input_len = lstrlen(input); - - for (int i = 0; i < input_len; ++i) { - int current_output_len = lstrlen(output); +namespace base { +namespace debug { - if (input[i] == L'%' && input[i + 1] == L'l' && input[i + 2] == L'd') { - // Make sure we have enough place left. - if ((current_output_len + 12) >= output_len) - return false; - - // Cheap _itow(). - wsprintf(output+current_output_len, L"%d", value); - i += 2; - } else { - if (current_output_len >= output_len) - return false; - output[current_output_len] = input[i]; - } - } - return true; -} +namespace { // SymbolContext is a threadsafe singleton that wraps the DbgHelp Sym* family // of functions. The Sym* family of functions may only be invoked by one @@ -184,47 +135,6 @@ class SymbolContext { } // namespace -// Note: Does not use the CRT. -bool DebugUtil::SpawnDebuggerOnProcess(unsigned process_id) { - wchar_t reg_value[1026]; - int len = arraysize(reg_value); - if (RegReadString(HKEY_LOCAL_MACHINE, - L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", - L"Debugger", reg_value, &len)) { - wchar_t command_line[1026]; - if (StringReplace(reg_value, process_id, command_line, - arraysize(command_line))) { - // We don't mind if the debugger is present because it will simply fail - // to attach to this process. - STARTUPINFO startup_info = {0}; - startup_info.cb = sizeof(startup_info); - PROCESS_INFORMATION process_info = {0}; - - if (CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL, - &startup_info, &process_info)) { - CloseHandle(process_info.hThread); - WaitForInputIdle(process_info.hProcess, 10000); - CloseHandle(process_info.hProcess); - return true; - } - } - } - return false; -} - -// static -bool DebugUtil::BeingDebugged() { - return ::IsDebuggerPresent() != 0; -} - -// static -void DebugUtil::BreakDebugger() { - if (suppress_dialogs_) - _exit(1); - - __debugbreak(); -} - StackTrace::StackTrace() { // When walking our own stack, use CaptureStackBackTrace(). count_ = CaptureStackBackTrace(0, arraysize(trace_), trace_, NULL); @@ -282,3 +192,6 @@ void StackTrace::OutputToStream(std::ostream* os) { context->OutputTraceToStream(trace_, count_, os); } } + +} // namespace debug +} // namespace base diff --git a/base/trace_event.cc b/base/debug/trace_event.cc index fbb35f8..616d7ca 100644 --- a/base/trace_event.cc +++ b/base/debug/trace_event.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/trace_event.h" +#include "base/debug/trace_event.h" #include "base/format_macros.h" #include "base/file_path.h" @@ -17,6 +17,7 @@ #define USE_UNRELIABLE_NOW namespace base { +namespace debug { static const char* kEventTypeNames[] = { "BEGIN", @@ -181,4 +182,5 @@ void TraceLog::Log(const std::string& msg) { #endif } -} // namespace base +} // namespace debug +} // namespace base diff --git a/base/trace_event.h b/base/debug/trace_event.h index e46e225..38b4d05 100644 --- a/base/trace_event.h +++ b/base/debug/trace_event.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -12,8 +12,8 @@ // trace report. In the future, it may use another mechansim to facilitate // real-time analysis. -#ifndef BASE_TRACE_EVENT_H_ -#define BASE_TRACE_EVENT_H_ +#ifndef BASE_DEBUG_TRACE_EVENT_H_ +#define BASE_DEBUG_TRACE_EVENT_H_ #pragma once #include "build/build_config.h" @@ -26,7 +26,7 @@ // value of the CHROMIUM_ENABLE_TRACE_EVENT define. The Windows implementation // is controlled by Event Tracing for Windows, which will turn tracing on only // if there is someone listening for the events it generates. -#include "base/trace_event_win.h" +#include "base/debug/trace_event_win.h" #else // defined(OS_WIN) #include <string> @@ -52,38 +52,41 @@ // Record that an event (of name, id) has begun. All BEGIN events should have // corresponding END events with a matching (name, id). #define TRACE_EVENT_BEGIN(name, id, extra) \ - Singleton<base::TraceLog>::get()->Trace(name, \ - base::TraceLog::EVENT_BEGIN, \ - reinterpret_cast<const void*>(id), \ - extra, \ - __FILE__, \ - __LINE__) + Singleton<base::debug::TraceLog>::get()->Trace( \ + name, \ + base::debug::TraceLog::EVENT_BEGIN, \ + reinterpret_cast<const void*>(id), \ + extra, \ + __FILE__, \ + __LINE__) // Record that an event (of name, id) has ended. All END events should have // corresponding BEGIN events with a matching (name, id). #define TRACE_EVENT_END(name, id, extra) \ - Singleton<base::TraceLog>::get()->Trace(name, \ - base::TraceLog::EVENT_END, \ - reinterpret_cast<const void*>(id), \ - extra, \ - __FILE__, \ - __LINE__) + Singleton<base::debug::TraceLog>::get()->Trace( \ + name, \ + base::debug::TraceLog::EVENT_END, \ + reinterpret_cast<const void*>(id), \ + extra, \ + __FILE__, \ + __LINE__) // Record that an event (of name, id) with no duration has happened. #define TRACE_EVENT_INSTANT(name, id, extra) \ - Singleton<base::TraceLog>::get()->Trace(name, \ - base::TraceLog::EVENT_INSTANT, \ - reinterpret_cast<const void*>(id), \ - extra, \ - __FILE__, \ - __LINE__) + Singleton<base::debug::TraceLog>::get()->Trace( \ + name, \ + base::debug::TraceLog::EVENT_INSTANT, \ + reinterpret_cast<const void*>(id), \ + extra, \ + __FILE__, \ + __LINE__) #endif // CHROMIUM_ENABLE_TRACE_EVENT namespace base { + class ProcessMetrics; -} -namespace base { +namespace debug { class TraceLog { public: @@ -138,7 +141,9 @@ class TraceLog { RepeatingTimer<TraceLog> timer_; }; -} // namespace base +} // namespace debug +} // namespace base + #endif // defined(OS_WIN) -#endif // BASE_TRACE_EVENT_H_ +#endif // BASE_DEBUG_TRACE_EVENT_H_ diff --git a/base/trace_event_win.cc b/base/debug/trace_event_win.cc index 7217283..8405699 100644 --- a/base/trace_event_win.cc +++ b/base/debug/trace_event_win.cc @@ -1,13 +1,18 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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_win.h" + +#include "base/debug/trace_event_win.h" #include "base/logging.h" #include "base/singleton.h" #include <initguid.h> // NOLINT namespace base { +namespace debug { + +using base::win::EtwEventType; +using base::win::EtwMofEvent; // {3DADA31D-19EF-4dc1-B345-037927193422} const GUID kChromeTraceProviderName = { @@ -22,7 +27,7 @@ const GUID kTraceEventClass64 = { 0x97be602d, 0x2930, 0x4ac3, 0x80, 0x46, 0xb6, 0x76, 0x3b, 0x63, 0x1d, 0xfe}; -TraceLog::TraceLog() : EtwTraceProvider(base::kChromeTraceProviderName) { +TraceLog::TraceLog() : EtwTraceProvider(kChromeTraceProviderName) { Register(); } @@ -36,7 +41,7 @@ bool TraceLog::StartTracing() { void TraceLog::TraceEvent(const char* name, size_t name_len, - base::TraceLog::EventType type, + EventType type, const void* id, const char* extra, size_t extra_len) { @@ -48,24 +53,24 @@ void TraceLog::TraceEvent(const char* name, EtwEventType etw_type = 0; switch (type) { - case base::TraceLog::EVENT_BEGIN: - etw_type = base::kTraceEventTypeBegin; + case TraceLog::EVENT_BEGIN: + etw_type = kTraceEventTypeBegin; break; - case base::TraceLog::EVENT_END: - etw_type = base::kTraceEventTypeEnd; + case TraceLog::EVENT_END: + etw_type = kTraceEventTypeEnd; break; - case base::TraceLog::EVENT_INSTANT: - etw_type = base::kTraceEventTypeInstant; + case TraceLog::EVENT_INSTANT: + etw_type = kTraceEventTypeInstant; break; default: NOTREACHED() << "Unknown event type"; - etw_type = base::kTraceEventTypeInstant; + etw_type = kTraceEventTypeInstant; break; } - EtwMofEvent<5> event(base::kTraceEventClass32, + EtwMofEvent<5> event(kTraceEventClass32, etw_type, TRACE_LEVEL_INFORMATION); event.SetField(0, name_len + 1, name); @@ -74,7 +79,7 @@ void TraceLog::TraceEvent(const char* name, // See whether we're to capture a backtrace. void* backtrace[32]; - if (enable_flags() & base::CAPTURE_STACK_TRACE) { + if (enable_flags() & CAPTURE_STACK_TRACE) { DWORD hash = 0; DWORD depth = CaptureStackBackTrace(0, arraysize(backtrace), @@ -110,4 +115,5 @@ void TraceLog::Resurrect() { StaticMemorySingletonTraits<TraceLog>::Resurrect(); } +} // namespace debug } // namespace base diff --git a/base/trace_event_win.h b/base/debug/trace_event_win.h index 77ab3fb..dd3f512 100644 --- a/base/trace_event_win.h +++ b/base/debug/trace_event_win.h @@ -3,40 +3,44 @@ // found in the LICENSE file. // This file contains the Windows-specific declarations for trace_event.h. -#ifndef BASE_TRACE_EVENT_WIN_H_ -#define BASE_TRACE_EVENT_WIN_H_ +#ifndef BASE_DEBUG_TRACE_EVENT_WIN_H_ +#define BASE_DEBUG_TRACE_EVENT_WIN_H_ #pragma once #include <string> -#include "base/event_trace_provider_win.h" +#include "base/win/event_trace_provider.h" #define TRACE_EVENT_BEGIN(name, id, extra) \ - base::TraceLog::Trace(name, \ - base::TraceLog::EVENT_BEGIN, \ - reinterpret_cast<const void*>(id), \ - extra); + base::debug::TraceLog::Trace( \ + name, \ + base::debug::TraceLog::EVENT_BEGIN, \ + reinterpret_cast<const void*>(id), \ + extra); #define TRACE_EVENT_END(name, id, extra) \ - base::TraceLog::Trace(name, \ - base::TraceLog::EVENT_END, \ - reinterpret_cast<const void*>(id), \ - extra); + base::debug::TraceLog::Trace( \ + name, \ + base::debug::TraceLog::EVENT_END, \ + reinterpret_cast<const void*>(id), \ + extra); #define TRACE_EVENT_INSTANT(name, id, extra) \ - base::TraceLog::Trace(name, \ - base::TraceLog::EVENT_INSTANT, \ - reinterpret_cast<const void*>(id), \ - extra); + base::debug::TraceLog::Trace( \ + name, \ + base::debug::TraceLog::EVENT_INSTANT, \ + reinterpret_cast<const void*>(id), \ + extra); // Fwd. template <typename Type> struct StaticMemorySingletonTraits; namespace base { +namespace debug { // This EtwTraceProvider subclass implements ETW logging // for the macros above on Windows. -class TraceLog : public EtwTraceProvider { +class TraceLog : public base::win::EtwTraceProvider { public: enum EventType { EVENT_BEGIN, @@ -95,7 +99,7 @@ class TraceLog : public EtwTraceProvider { // string will be used. void TraceEvent(const char* name, size_t name_len, - base::TraceLog::EventType type, + EventType type, const void* id, const char* extra, size_t extra_len); @@ -122,9 +126,9 @@ extern const GUID kTraceEventClass32; extern const GUID kTraceEventClass64; // The ETW event types, IDs 0x00-0x09 are reserved, so start at 0x10. -const EtwEventType kTraceEventTypeBegin = 0x10; -const EtwEventType kTraceEventTypeEnd = 0x11; -const EtwEventType kTraceEventTypeInstant = 0x12; +const base::win::EtwEventType kTraceEventTypeBegin = 0x10; +const base::win::EtwEventType kTraceEventTypeEnd = 0x11; +const base::win::EtwEventType kTraceEventTypeInstant = 0x12; // If this flag is set in enable flags enum TraceEventFlags { @@ -141,6 +145,7 @@ enum TraceEventFlags { // Forward decl. struct TraceLogSingletonTraits; +} // nemspace debug } // namespace base -#endif // BASE_TRACE_EVENT_WIN_H_ +#endif // BASE_DEBUG_TRACE_EVENT_WIN_H_ diff --git a/base/trace_event_win_unittest.cc b/base/debug/trace_event_win_unittest.cc index 79a48e3..8544bc7 100644 --- a/base/trace_event_win_unittest.cc +++ b/base/debug/trace_event_win_unittest.cc @@ -2,21 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/trace_event.h" +#include "base/debug/trace_event.h" #include <strstream> #include "base/at_exit.h" #include "base/basictypes.h" #include "base/file_util.h" -#include "base/event_trace_consumer_win.h" -#include "base/event_trace_controller_win.h" +#include "base/win/event_trace_consumer.h" +#include "base/win/event_trace_controller.h" #include "base/win/windows_version.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include <initguid.h> // NOLINT - must be last include. +namespace base { +namespace debug { + namespace { + using testing::_; using testing::AnyNumber; using testing::InSequence; @@ -24,6 +28,11 @@ using testing::Ge; using testing::Le; using testing::NotNull; +using base::win::EtwEventType; +using base::win::EtwTraceConsumerBase; +using base::win::EtwTraceController; +using base::win::EtwTraceProperties; + // Data for unittests traces. const char kEmpty[] = ""; const char kName[] = "unittest.trace_name"; @@ -81,7 +90,7 @@ class TraceEventTest: public testing::Test { } void SetUp() { - bool is_xp = base::win::GetVersion() < base::win::VERSION_VISTA; + bool is_xp = win::GetVersion() < base::win::VERSION_VISTA; if (is_xp) { // Tear down any dangling session from an earlier failing test. @@ -94,10 +103,10 @@ class TraceEventTest: public testing::Test { // start the private, in-proc session, but on XP we need the global // session created and the provider enabled before we register our // provider. - base::TraceLog* tracelog = NULL; + TraceLog* tracelog = NULL; if (!is_xp) { - base::TraceLog::Resurrect(); - tracelog = base::TraceLog::Get(); + TraceLog::Resurrect(); + tracelog = TraceLog::Get(); ASSERT_TRUE(tracelog != NULL); ASSERT_FALSE(tracelog->IsTracing()); } @@ -127,13 +136,13 @@ class TraceEventTest: public testing::Test { // Enable the TraceLog provider GUID. ASSERT_HRESULT_SUCCEEDED( - controller_.EnableProvider(base::kChromeTraceProviderName, + controller_.EnableProvider(kChromeTraceProviderName, TRACE_LEVEL_INFORMATION, 0)); if (is_xp) { - base::TraceLog::Resurrect(); - tracelog = base::TraceLog::Get(); + TraceLog::Resurrect(); + tracelog = TraceLog::Get(); } ASSERT_TRUE(tracelog != NULL); EXPECT_TRUE(tracelog->IsTracing()); @@ -186,7 +195,7 @@ class TraceEventTest: public testing::Test { private: // We want our singleton torn down after each test. - base::ShadowingAtExitManager at_exit_manager_; + ShadowingAtExitManager at_exit_manager_; EtwTraceController controller_; FilePath log_file_; TestEventConsumer consumer_; @@ -202,67 +211,67 @@ TEST_F(TraceEventTest, TraceLog) { InSequence in_sequence; // Full argument version, passing lengths explicitly. - base::TraceLog::Trace(kName, + TraceLog::Trace(kName, strlen(kName), - base::TraceLog::EVENT_BEGIN, + TraceLog::EVENT_BEGIN, kId, kExtra, strlen(kExtra)); - ExpectEvent(base::kTraceEventClass32, - base::kTraceEventTypeBegin, + ExpectEvent(kTraceEventClass32, + kTraceEventTypeBegin, kName, strlen(kName), kId, kExtra, strlen(kExtra)); // Const char* version. - base::TraceLog::Trace(static_cast<const char*>(kName), - base::TraceLog::EVENT_END, + TraceLog::Trace(static_cast<const char*>(kName), + TraceLog::EVENT_END, kId, static_cast<const char*>(kExtra)); - ExpectEvent(base::kTraceEventClass32, - base::kTraceEventTypeEnd, + ExpectEvent(kTraceEventClass32, + kTraceEventTypeEnd, kName, strlen(kName), kId, kExtra, strlen(kExtra)); // std::string extra version. - base::TraceLog::Trace(static_cast<const char*>(kName), - base::TraceLog::EVENT_INSTANT, + TraceLog::Trace(static_cast<const char*>(kName), + TraceLog::EVENT_INSTANT, kId, std::string(kExtra)); - ExpectEvent(base::kTraceEventClass32, - base::kTraceEventTypeInstant, + ExpectEvent(kTraceEventClass32, + kTraceEventTypeInstant, kName, strlen(kName), kId, kExtra, strlen(kExtra)); // Test for sanity on NULL inputs. - base::TraceLog::Trace(NULL, + TraceLog::Trace(NULL, 0, - base::TraceLog::EVENT_BEGIN, + TraceLog::EVENT_BEGIN, kId, NULL, 0); - ExpectEvent(base::kTraceEventClass32, - base::kTraceEventTypeBegin, + ExpectEvent(kTraceEventClass32, + kTraceEventTypeBegin, kEmpty, 0, kId, kEmpty, 0); - base::TraceLog::Trace(NULL, + TraceLog::Trace(NULL, -1, - base::TraceLog::EVENT_END, + TraceLog::EVENT_END, kId, NULL, -1); - ExpectEvent(base::kTraceEventClass32, - base::kTraceEventTypeEnd, + ExpectEvent(kTraceEventClass32, + kTraceEventTypeEnd, kEmpty, 0, kId, kEmpty, 0); @@ -277,25 +286,28 @@ TEST_F(TraceEventTest, Macros) { InSequence in_sequence; TRACE_EVENT_BEGIN(kName, kId, kExtra); - ExpectEvent(base::kTraceEventClass32, - base::kTraceEventTypeBegin, + ExpectEvent(kTraceEventClass32, + kTraceEventTypeBegin, kName, strlen(kName), kId, kExtra, strlen(kExtra)); TRACE_EVENT_END(kName, kId, kExtra); - ExpectEvent(base::kTraceEventClass32, - base::kTraceEventTypeEnd, + ExpectEvent(kTraceEventClass32, + kTraceEventTypeEnd, kName, strlen(kName), kId, kExtra, strlen(kExtra)); TRACE_EVENT_INSTANT(kName, kId, kExtra); - ExpectEvent(base::kTraceEventClass32, - base::kTraceEventTypeInstant, + ExpectEvent(kTraceEventClass32, + kTraceEventTypeInstant, kName, strlen(kName), kId, kExtra, strlen(kExtra)); PlayLog(); } + +} // namespace debug +} // namespace base diff --git a/base/debug_on_start.cc b/base/debug_on_start.cc index 9cabde5..15dab05 100644 --- a/base/debug_on_start.cc +++ b/base/debug_on_start.cc @@ -8,7 +8,7 @@ #include "base/base_switches.h" #include "base/basictypes.h" -#include "base/debug_util.h" +#include "base/debug/debugger.h" // Minimalist implementation to try to find a command line argument. We can use // kernel32 exported functions but not the CRT functions because we're too early @@ -56,13 +56,13 @@ int __cdecl DebugOnStart::Init() { // - Do a int3. // It will fails if we run in a sandbox. That is expected. - DebugUtil::SpawnDebuggerOnProcess(GetCurrentProcessId()); + base::debug::SpawnDebuggerOnProcess(GetCurrentProcessId()); // Wait for a debugger to come take us. - DebugUtil::WaitForDebugger(60, false); + base::debug::WaitForDebugger(60, false); } else if (FindArgument(GetCommandLine(), switches::kWaitForDebugger)) { // Wait for a debugger to come take us. - DebugUtil::WaitForDebugger(60, true); + base::debug::WaitForDebugger(60, true); } return 0; } diff --git a/base/debug_util.cc b/base/debug_util.cc index 21f5b70..4773de3 100644 --- a/base/debug_util.cc +++ b/base/debug_util.cc @@ -4,25 +4,4 @@ #include "base/debug_util.h" -#include "base/platform_thread.h" - bool DebugUtil::suppress_dialogs_ = false; - -bool DebugUtil::WaitForDebugger(int wait_seconds, bool silent) { - for (int i = 0; i < wait_seconds * 10; ++i) { - if (BeingDebugged()) { - if (!silent) - BreakDebugger(); - return true; - } - PlatformThread::Sleep(100); - } - return false; -} - -const void *const *StackTrace::Addresses(size_t* count) { - *count = count_; - if (count_) - return trace_; - return NULL; -} diff --git a/base/debug_util.h b/base/debug_util.h index a7dba3a..a643ccf 100644 --- a/base/debug_util.h +++ b/base/debug_util.h @@ -2,78 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// This is a cross platform interface for helper functions related to debuggers. -// You should use this to test if you're running under a debugger, and if you -// would like to yield (breakpoint) into the debugger. - #ifndef BASE_DEBUG_UTIL_H_ #define BASE_DEBUG_UTIL_H_ #pragma once -#include <iosfwd> - -#include "base/basictypes.h" - -#if defined(OS_WIN) -struct _EXCEPTION_POINTERS; -#endif - -// A stacktrace can be helpful in debugging. For example, you can include a -// stacktrace member in a object (probably around #ifndef NDEBUG) so that you -// can later see where the given object was created from. -class StackTrace { - public: - // Creates a stacktrace from the current location - StackTrace(); - - // Note that the default copy constructor and assignment constructors - // are OK. - -#if defined(OS_WIN) - // Creates a stacktrace for an exception. - // Note: this function will throw an import not found (StackWalk64) exception - // on system without dbghelp 5.1. - StackTrace(_EXCEPTION_POINTERS* exception_pointers); -#endif - // Gets an array of instruction pointer values. - // count: (output) the number of elements in the returned array - const void *const *Addresses(size_t* count); - // Prints a backtrace to stderr - void PrintBacktrace(); - - // Resolves backtrace to symbols and write to stream. - void OutputToStream(std::ostream* os); - - private: - // From http://msdn.microsoft.com/en-us/library/bb204633.aspx, - // the sum of FramesToSkip and FramesToCapture must be less than 63, - // so set it to 62. Even if on POSIX it could be a larger value, it usually - // doesn't give much more information. - static const int MAX_TRACES = 62; - void* trace_[MAX_TRACES]; - int count_; -}; +#include "build/build_config.h" class DebugUtil { public: - // Starts the registered system-wide JIT debugger to attach it to specified - // process. - static bool SpawnDebuggerOnProcess(unsigned process_id); - - // Waits wait_seconds seconds for a debugger to attach to the current process. - // When silent is false, an exception is thrown when a debugger is detected. - static bool WaitForDebugger(int wait_seconds, bool silent); - - // Are we running under a debugger? - // On OS X, the underlying mechanism doesn't work when the sandbox is enabled. - // To get around this, this function caches its value. - // WARNING: Because of this, on OS X, a call MUST be made to this function - // BEFORE the sandbox is enabled. - static bool BeingDebugged(); - - // Break into the debugger, assumes a debugger is present. - static void BreakDebugger(); - #if defined(OS_MACOSX) // On Mac OS X, it can take a really long time for the OS crash handler to // process a Chrome crash when debugging symbols are available. This @@ -87,6 +23,10 @@ class DebugUtil { suppress_dialogs_ = true; } + static bool AreDialogsSuppressed() { + return suppress_dialogs_; + } + private: // If true, avoid displaying any dialogs that could cause problems // in non-interactive environments. diff --git a/base/debug_util_mac.cc b/base/debug_util_mac.cc index 78679f2..a4eed66 100644 --- a/base/debug_util_mac.cc +++ b/base/debug_util_mac.cc @@ -5,6 +5,7 @@ #include "base/debug_util.h" #include <signal.h> +#include <unistd.h> #include "base/basictypes.h" diff --git a/base/file_util_mac.mm b/base/file_util_mac.mm index 811799c..43bf6e9 100644 --- a/base/file_util_mac.mm +++ b/base/file_util_mac.mm @@ -10,6 +10,7 @@ #include "base/basictypes.h" #include "base/file_path.h" #include "base/string_util.h" +#include "base/thread_restrictions.h" namespace file_util { @@ -26,6 +27,7 @@ bool GetShmemTempDir(FilePath* path) { } bool CopyFile(const FilePath& from_path, const FilePath& to_path) { + base::ThreadRestrictions::AssertIOAllowed(); return (copyfile(from_path.value().c_str(), to_path.value().c_str(), NULL, COPYFILE_ALL) == 0); } diff --git a/base/file_util_posix.cc b/base/file_util_posix.cc index 18a17d1..85123e9 100644 --- a/base/file_util_posix.cc +++ b/base/file_util_posix.cc @@ -39,6 +39,7 @@ #include "base/singleton.h" #include "base/string_util.h" #include "base/sys_string_conversions.h" +#include "base/thread_restrictions.h" #include "base/time.h" #include "base/utf_string_conversions.h" @@ -48,6 +49,7 @@ namespace { // Helper for NormalizeFilePath(), defined below. bool RealPath(const FilePath& path, FilePath* real_path) { + base::ThreadRestrictions::AssertIOAllowed(); // For realpath(). FilePath::CharType buf[PATH_MAX]; if (!realpath(path.value().c_str(), buf)) return false; @@ -63,11 +65,13 @@ bool RealPath(const FilePath& path, FilePath* real_path) { MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) typedef struct stat stat_wrapper_t; static int CallStat(const char *path, stat_wrapper_t *sb) { + base::ThreadRestrictions::AssertIOAllowed(); return stat(path, sb); } #else typedef struct stat64 stat_wrapper_t; static int CallStat(const char *path, stat_wrapper_t *sb) { + base::ThreadRestrictions::AssertIOAllowed(); return stat64(path, sb); } #endif @@ -80,6 +84,7 @@ static const char* kTempFileName = ".org.chromium.XXXXXX"; #endif bool AbsolutePath(FilePath* path) { + base::ThreadRestrictions::AssertIOAllowed(); // For realpath(). char full_path[PATH_MAX]; if (realpath(path->value().c_str(), full_path) == NULL) return false; @@ -89,6 +94,7 @@ bool AbsolutePath(FilePath* path) { int CountFilesCreatedAfter(const FilePath& path, const base::Time& comparison_time) { + base::ThreadRestrictions::AssertIOAllowed(); int file_count = 0; DIR* dir = opendir(path.value().c_str()); @@ -139,6 +145,7 @@ int CountFilesCreatedAfter(const FilePath& path, // that functionality. If not, remove from file_util_win.cc, otherwise add it // here. bool Delete(const FilePath& path, bool recursive) { + base::ThreadRestrictions::AssertIOAllowed(); const char* path_str = path.value().c_str(); stat_wrapper_t file_info; int test = CallStat(path_str, &file_info); @@ -178,6 +185,7 @@ bool Delete(const FilePath& path, bool recursive) { } bool Move(const FilePath& from_path, const FilePath& to_path) { + base::ThreadRestrictions::AssertIOAllowed(); // Windows compatibility: if to_path exists, from_path and to_path // must be the same type, either both files, or both directories. stat_wrapper_t to_file_info; @@ -202,12 +210,14 @@ bool Move(const FilePath& from_path, const FilePath& to_path) { } bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) { + base::ThreadRestrictions::AssertIOAllowed(); return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0); } bool CopyDirectory(const FilePath& from_path, const FilePath& to_path, bool recursive) { + base::ThreadRestrictions::AssertIOAllowed(); // Some old callers of CopyDirectory want it to support wildcards. // After some discussion, we decided to fix those callers. // Break loudly here if anyone tries to do this. @@ -307,14 +317,17 @@ bool CopyDirectory(const FilePath& from_path, } bool PathExists(const FilePath& path) { + base::ThreadRestrictions::AssertIOAllowed(); return access(path.value().c_str(), F_OK) == 0; } bool PathIsWritable(const FilePath& path) { + base::ThreadRestrictions::AssertIOAllowed(); return access(path.value().c_str(), W_OK) == 0; } bool DirectoryExists(const FilePath& path) { + base::ThreadRestrictions::AssertIOAllowed(); stat_wrapper_t file_info; if (CallStat(path.value().c_str(), &file_info) == 0) return S_ISDIR(file_info.st_mode); @@ -365,6 +378,7 @@ bool ReadFromFD(int fd, char* buffer, size_t bytes) { // file descriptor. |path| is set to the temporary file path. // This function does NOT unlink() the file. int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) { + base::ThreadRestrictions::AssertIOAllowed(); // For call to mkstemp(). *path = directory.Append(kTempFileName); const std::string& tmpdir_string = path->value(); // this should be OK since mkstemp just replaces characters in place @@ -374,6 +388,7 @@ int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) { } bool CreateTemporaryFile(FilePath* path) { + base::ThreadRestrictions::AssertIOAllowed(); // For call to close(). FilePath directory; if (!GetTempDir(&directory)) return false; @@ -401,6 +416,7 @@ FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { } bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) { + base::ThreadRestrictions::AssertIOAllowed(); // For call to close(). int fd = CreateAndOpenFdForTemporaryFile(dir, temp_file); return ((fd >= 0) && !close(fd)); } @@ -408,6 +424,7 @@ bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) { static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir, const FilePath::StringType& name_tmpl, FilePath* new_dir) { + base::ThreadRestrictions::AssertIOAllowed(); // For call to mkdtemp(). CHECK(name_tmpl.find("XXXXXX") != FilePath::StringType::npos) << "Directory name template must contain \"XXXXXX\"."; @@ -443,6 +460,7 @@ bool CreateNewTempDirectory(const FilePath::StringType& prefix, } bool CreateDirectory(const FilePath& full_path) { + base::ThreadRestrictions::AssertIOAllowed(); // For call to mkdir(). std::vector<FilePath> subpaths; // Collect a list of all parent directories. @@ -484,6 +502,7 @@ bool GetFileInfo(const FilePath& file_path, base::PlatformFileInfo* results) { } bool GetInode(const FilePath& path, ino_t* inode) { + base::ThreadRestrictions::AssertIOAllowed(); // For call to stat(). struct stat buffer; int result = stat(path.value().c_str(), &buffer); if (result < 0) @@ -498,10 +517,12 @@ FILE* OpenFile(const std::string& filename, const char* mode) { } FILE* OpenFile(const FilePath& filename, const char* mode) { + base::ThreadRestrictions::AssertIOAllowed(); return fopen(filename.value().c_str(), mode); } int ReadFile(const FilePath& filename, char* data, int size) { + base::ThreadRestrictions::AssertIOAllowed(); int fd = open(filename.value().c_str(), O_RDONLY); if (fd < 0) return -1; @@ -513,6 +534,7 @@ int ReadFile(const FilePath& filename, char* data, int size) { } int WriteFile(const FilePath& filename, const char* data, int size) { + base::ThreadRestrictions::AssertIOAllowed(); int fd = creat(filename.value().c_str(), 0666); if (fd < 0) return -1; @@ -540,6 +562,9 @@ int WriteFileDescriptor(const int fd, const char* data, int size) { // Gets the current working directory for the process. bool GetCurrentDirectory(FilePath* dir) { + // getcwd can return ENOENT, which implies it checks against the disk. + base::ThreadRestrictions::AssertIOAllowed(); + char system_buffer[PATH_MAX] = ""; if (!getcwd(system_buffer, sizeof(system_buffer))) { NOTREACHED(); @@ -551,6 +576,7 @@ bool GetCurrentDirectory(FilePath* dir) { // Sets the current working directory for the process. bool SetCurrentDirectory(const FilePath& path) { + base::ThreadRestrictions::AssertIOAllowed(); int ret = chdir(path.value().c_str()); return !ret; } @@ -655,6 +681,7 @@ FilePath FileEnumerator::Next() { bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo>* entries, const FilePath& source, bool show_links) { + base::ThreadRestrictions::AssertIOAllowed(); DIR* dir = opendir(source.value().c_str()); if (!dir) return false; @@ -703,6 +730,8 @@ MemoryMappedFile::MemoryMappedFile() } bool MemoryMappedFile::MapFileToMemoryInternal() { + base::ThreadRestrictions::AssertIOAllowed(); + struct stat file_stat; if (fstat(file_, &file_stat) == base::kInvalidPlatformFileValue) { LOG(ERROR) << "Couldn't fstat " << file_ << ", errno " << errno; @@ -719,6 +748,8 @@ bool MemoryMappedFile::MapFileToMemoryInternal() { } void MemoryMappedFile::CloseHandles() { + base::ThreadRestrictions::AssertIOAllowed(); + if (data_ != NULL) munmap(data_, length_); if (file_ != base::kInvalidPlatformFileValue) @@ -771,6 +802,9 @@ FilePath GetHomeDir() { if (home_dir && home_dir[0]) return FilePath(home_dir); + // g_get_home_dir calls getpwent, which can fall through to LDAP calls. + base::ThreadRestrictions::AssertIOAllowed(); + home_dir = g_get_home_dir(); if (home_dir && home_dir[0]) return FilePath(home_dir); @@ -784,6 +818,7 @@ FilePath GetHomeDir() { } bool CopyFile(const FilePath& from_path, const FilePath& to_path) { + base::ThreadRestrictions::AssertIOAllowed(); int infile = open(from_path.value().c_str(), O_RDONLY); if (infile < 0) return false; diff --git a/base/file_util_proxy.cc b/base/file_util_proxy.cc index fc96e8f..bd08909 100644 --- a/base/file_util_proxy.cc +++ b/base/file_util_proxy.cc @@ -66,7 +66,7 @@ static base::PlatformFileError PerformCommonCheckAndPreparationForMoveAndCopy( return base::PLATFORM_FILE_OK; } -} // anonymous namespace +} // anonymous namespace class MessageLoopRelay : public base::RefCountedThreadSafe<MessageLoopRelay> { @@ -442,7 +442,7 @@ class RelayReadDirectory : public MessageLoopRelay { file_util::FileEnumerator::DIRECTORIES)); FilePath current; while (!(current = file_enum.Next()).empty()) { - base::file_util_proxy::Entry entry; + base::FileUtilProxy::Entry entry; file_util::FileEnumerator::FindInfo info; file_enum.GetFindInfo(&info); entry.is_directory = file_enum.IsDirectory(info); @@ -461,7 +461,7 @@ class RelayReadDirectory : public MessageLoopRelay { private: base::FileUtilProxy::ReadDirectoryCallback* callback_; FilePath file_path_; - std::vector<base::file_util_proxy::Entry> entries_; + std::vector<base::FileUtilProxy::Entry> entries_; }; class RelayGetFileInfo : public MessageLoopRelay { diff --git a/base/file_util_proxy.h b/base/file_util_proxy.h index 09c2377..f266562 100644 --- a/base/file_util_proxy.h +++ b/base/file_util_proxy.h @@ -16,35 +16,30 @@ namespace base { -namespace file_util_proxy { - -// Holds metadata for file or directory entry. -struct Entry { - FilePath::StringType name; - bool is_directory; -}; - -} // namespace file_util_proxy - class MessageLoopProxy; class Time; // This class provides asynchronous access to common file routines. class FileUtilProxy { public: + // Holds metadata for file or directory entry. Used by ReadDirectoryCallback. + struct Entry { + FilePath::StringType name; + bool is_directory; + }; + // This callback is used by methods that report only an error code. It is // valid to pass NULL as the callback parameter to any function that takes a // StatusCallback, in which case the operation will complete silently. - typedef Callback1<base::PlatformFileError /* error code */ - >::Type StatusCallback; + typedef Callback1<PlatformFileError /* error code */>::Type StatusCallback; // Creates or opens a file with the given flags. It is invalid to pass NULL // for the callback. // If PLATFORM_FILE_CREATE is set in |file_flags| it always tries to create // a new file at the given |file_path| and calls back with // PLATFORM_FILE_ERROR_FILE_EXISTS if the |file_path| already exists. - typedef Callback3<base::PlatformFileError /* error code */, - base::PassPlatformFile, + typedef Callback3<PlatformFileError /* error code */, + PassPlatformFile, bool /* created */>::Type CreateOrOpenCallback; static bool CreateOrOpen(scoped_refptr<MessageLoopProxy> message_loop_proxy, const FilePath& file_path, @@ -53,8 +48,8 @@ class FileUtilProxy { // Creates a temporary file for writing. The path and an open file handle // are returned. It is invalid to pass NULL for the callback. - typedef Callback3<base::PlatformFileError /* error code */, - base::PassPlatformFile, + typedef Callback3<PlatformFileError /* error code */, + PassPlatformFile, FilePath>::Type CreateTemporaryCallback; static bool CreateTemporary( scoped_refptr<MessageLoopProxy> message_loop_proxy, @@ -62,7 +57,7 @@ class FileUtilProxy { // Close the given file handle. static bool Close(scoped_refptr<MessageLoopProxy> message_loop_proxy, - base::PlatformFile, + PlatformFile, StatusCallback* callback); // Ensures that the given |file_path| exist. This creates a empty new file @@ -74,7 +69,7 @@ class FileUtilProxy { // is set PLATFORM_FILE_OK. // If the file hasn't existed but it couldn't be created for some other // reasons, |created| is set false and |error code| indicates the error. - typedef Callback2<base::PlatformFileError /* error code */, + typedef Callback2<PlatformFileError /* error code */, bool /* created */>::Type EnsureFileExistsCallback; static bool EnsureFileExists( scoped_refptr<MessageLoopProxy> message_loop_proxy, @@ -83,8 +78,8 @@ class FileUtilProxy { // Retrieves the information about a file. It is invalid to pass NULL for the // callback. - typedef Callback2<base::PlatformFileError /* error code */, - const base::PlatformFileInfo& /* file_info */ + typedef Callback2<PlatformFileError /* error code */, + const PlatformFileInfo& /* file_info */ >::Type GetFileInfoCallback; static bool GetFileInfo( scoped_refptr<MessageLoopProxy> message_loop_proxy, @@ -93,12 +88,11 @@ class FileUtilProxy { static bool GetFileInfoFromPlatformFile( scoped_refptr<MessageLoopProxy> message_loop_proxy, - base::PlatformFile file, + PlatformFile file, GetFileInfoCallback* callback); - typedef Callback2<base::PlatformFileError /* error code */, - const std::vector<base::file_util_proxy::Entry>& - >::Type ReadDirectoryCallback; + typedef Callback2<PlatformFileError /* error code */, + const std::vector<Entry>&>::Type ReadDirectoryCallback; static bool ReadDirectory(scoped_refptr<MessageLoopProxy> message_loop_proxy, const FilePath& file_path, ReadDirectoryCallback* callback); @@ -148,11 +142,11 @@ class FileUtilProxy { // Reads from a file. On success, the file pointer is moved to position // |offset + bytes_to_read| in the file. The callback can be NULL. - typedef Callback2<base::PlatformFileError /* error code */, + typedef Callback2<PlatformFileError /* error code */, int /* bytes read/written */>::Type ReadWriteCallback; static bool Read( scoped_refptr<MessageLoopProxy> message_loop_proxy, - base::PlatformFile file, + PlatformFile file, int64 offset, char* buffer, int bytes_to_read, @@ -163,7 +157,7 @@ class FileUtilProxy { // |offset + bytes_to_write| in the file. The callback can be NULL. static bool Write( scoped_refptr<MessageLoopProxy> message_loop_proxy, - base::PlatformFile file, + PlatformFile file, int64 offset, const char* buffer, int bytes_to_write, @@ -172,17 +166,17 @@ class FileUtilProxy { // Touches a file. The callback can be NULL. static bool Touch( scoped_refptr<MessageLoopProxy> message_loop_proxy, - base::PlatformFile file, - const base::Time& last_access_time, - const base::Time& last_modified_time, + PlatformFile file, + const Time& last_access_time, + const Time& last_modified_time, StatusCallback* callback); // Touches a file. The callback can be NULL. static bool Touch( scoped_refptr<MessageLoopProxy> message_loop_proxy, const FilePath& file_path, - const base::Time& last_access_time, - const base::Time& last_modified_time, + const Time& last_access_time, + const Time& last_modified_time, StatusCallback* callback); // Truncates a file to the given length. If |length| is greater than the @@ -190,7 +184,7 @@ class FileUtilProxy { // The callback can be NULL. static bool Truncate( scoped_refptr<MessageLoopProxy> message_loop_proxy, - base::PlatformFile file, + PlatformFile file, int64 length, StatusCallback* callback); @@ -206,7 +200,7 @@ class FileUtilProxy { // Flushes a file. The callback can be NULL. static bool Flush( scoped_refptr<MessageLoopProxy> message_loop_proxy, - base::PlatformFile file, + PlatformFile file, StatusCallback* callback); private: diff --git a/base/file_util_win.cc b/base/file_util_win.cc index 30d314f..4645acc 100644 --- a/base/file_util_win.cc +++ b/base/file_util_win.cc @@ -19,6 +19,7 @@ #include "base/win/scoped_handle.h" #include "base/string_number_conversions.h" #include "base/string_util.h" +#include "base/thread_restrictions.h" #include "base/time.h" #include "base/utf_string_conversions.h" #include "base/win_util.h" @@ -35,6 +36,8 @@ const DWORD kFileShareAll = // Helper for NormalizeFilePath(), defined below. bool DevicePathToDriveLetterPath(const FilePath& device_path, FilePath* drive_letter_path) { + base::ThreadRestrictions::AssertIOAllowed(); + // Get the mapping of drive letters to device paths. const int kDriveMappingSize = 1024; wchar_t drive_mapping[kDriveMappingSize] = {'\0'}; @@ -75,6 +78,7 @@ bool DevicePathToDriveLetterPath(const FilePath& device_path, } // namespace std::wstring GetDirectoryFromPath(const std::wstring& path) { + base::ThreadRestrictions::AssertIOAllowed(); wchar_t path_buffer[MAX_PATH]; wchar_t* file_ptr = NULL; if (GetFullPathName(path.c_str(), MAX_PATH, path_buffer, &file_ptr) == 0) @@ -87,6 +91,7 @@ std::wstring GetDirectoryFromPath(const std::wstring& path) { } bool AbsolutePath(FilePath* path) { + base::ThreadRestrictions::AssertIOAllowed(); wchar_t file_path_buf[MAX_PATH]; if (!_wfullpath(file_path_buf, path->value().c_str(), MAX_PATH)) return false; @@ -96,6 +101,8 @@ bool AbsolutePath(FilePath* path) { int CountFilesCreatedAfter(const FilePath& path, const base::Time& comparison_time) { + base::ThreadRestrictions::AssertIOAllowed(); + int file_count = 0; FILETIME comparison_filetime(comparison_time.ToFileTime()); @@ -123,6 +130,8 @@ int CountFilesCreatedAfter(const FilePath& path, } bool Delete(const FilePath& path, bool recursive) { + base::ThreadRestrictions::AssertIOAllowed(); + if (path.value().length() >= MAX_PATH) return false; @@ -161,6 +170,8 @@ bool Delete(const FilePath& path, bool recursive) { } bool DeleteAfterReboot(const FilePath& path) { + base::ThreadRestrictions::AssertIOAllowed(); + if (path.value().length() >= MAX_PATH) return false; @@ -170,6 +181,8 @@ bool DeleteAfterReboot(const FilePath& path) { } bool Move(const FilePath& from_path, const FilePath& to_path) { + base::ThreadRestrictions::AssertIOAllowed(); + // NOTE: I suspect we could support longer paths, but that would involve // analyzing all our usage of files. if (from_path.value().length() >= MAX_PATH || @@ -189,6 +202,8 @@ bool Move(const FilePath& from_path, const FilePath& to_path) { } bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) { + base::ThreadRestrictions::AssertIOAllowed(); + // Make sure that the target file exists. HANDLE target_file = ::CreateFile( to_path.value().c_str(), @@ -208,6 +223,8 @@ bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) { } bool CopyFile(const FilePath& from_path, const FilePath& to_path) { + base::ThreadRestrictions::AssertIOAllowed(); + // NOTE: I suspect we could support longer paths, but that would involve // analyzing all our usage of files. if (from_path.value().length() >= MAX_PATH || @@ -220,6 +237,8 @@ bool CopyFile(const FilePath& from_path, const FilePath& to_path) { bool ShellCopy(const FilePath& from_path, const FilePath& to_path, bool recursive) { + base::ThreadRestrictions::AssertIOAllowed(); + // NOTE: I suspect we could support longer paths, but that would involve // analyzing all our usage of files. if (from_path.value().length() >= MAX_PATH || @@ -251,6 +270,8 @@ bool ShellCopy(const FilePath& from_path, const FilePath& to_path, bool CopyDirectory(const FilePath& from_path, const FilePath& to_path, bool recursive) { + base::ThreadRestrictions::AssertIOAllowed(); + if (recursive) return ShellCopy(from_path, to_path, true); @@ -274,6 +295,7 @@ bool CopyDirectory(const FilePath& from_path, const FilePath& to_path, bool CopyAndDeleteDirectory(const FilePath& from_path, const FilePath& to_path) { + base::ThreadRestrictions::AssertIOAllowed(); if (CopyDirectory(from_path, to_path, true)) { if (Delete(from_path, true)) { return true; @@ -288,10 +310,12 @@ bool CopyAndDeleteDirectory(const FilePath& from_path, bool PathExists(const FilePath& path) { + base::ThreadRestrictions::AssertIOAllowed(); return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES); } bool PathIsWritable(const FilePath& path) { + base::ThreadRestrictions::AssertIOAllowed(); HANDLE dir = CreateFile(path.value().c_str(), FILE_ADD_FILE, kFileShareAll, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); @@ -304,6 +328,7 @@ bool PathIsWritable(const FilePath& path) { } bool DirectoryExists(const FilePath& path) { + base::ThreadRestrictions::AssertIOAllowed(); DWORD fileattr = GetFileAttributes(path.value().c_str()); if (fileattr != INVALID_FILE_ATTRIBUTES) return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0; @@ -312,6 +337,7 @@ bool DirectoryExists(const FilePath& path) { bool GetFileCreationLocalTimeFromHandle(HANDLE file_handle, LPSYSTEMTIME creation_time) { + base::ThreadRestrictions::AssertIOAllowed(); if (!file_handle) return false; @@ -328,6 +354,7 @@ bool GetFileCreationLocalTimeFromHandle(HANDLE file_handle, bool GetFileCreationLocalTime(const std::wstring& filename, LPSYSTEMTIME creation_time) { + base::ThreadRestrictions::AssertIOAllowed(); base::win::ScopedHandle file_handle( CreateFile(filename.c_str(), GENERIC_READ, kFileShareAll, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); @@ -335,6 +362,8 @@ bool GetFileCreationLocalTime(const std::wstring& filename, } bool ResolveShortcut(FilePath* path) { + base::ThreadRestrictions::AssertIOAllowed(); + HRESULT result; base::win::ScopedComPtr<IShellLink> i_shell_link; bool is_resolved = false; @@ -370,6 +399,8 @@ bool CreateShortcutLink(const wchar_t *source, const wchar_t *destination, const wchar_t *working_dir, const wchar_t *arguments, const wchar_t *description, const wchar_t *icon, int icon_index, const wchar_t* app_id) { + base::ThreadRestrictions::AssertIOAllowed(); + // Length of arguments and description must be less than MAX_PATH. DCHECK(lstrlen(arguments) < MAX_PATH); DCHECK(lstrlen(description) < MAX_PATH); @@ -421,6 +452,8 @@ bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination, const wchar_t *working_dir, const wchar_t *arguments, const wchar_t *description, const wchar_t *icon, int icon_index, const wchar_t* app_id) { + base::ThreadRestrictions::AssertIOAllowed(); + // Length of arguments and description must be less than MAX_PATH. DCHECK(lstrlen(arguments) < MAX_PATH); DCHECK(lstrlen(description) < MAX_PATH); @@ -467,6 +500,8 @@ bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination, } bool TaskbarPinShortcutLink(const wchar_t* shortcut) { + base::ThreadRestrictions::AssertIOAllowed(); + // "Pin to taskbar" is only supported after Win7. if (base::win::GetVersion() < base::win::VERSION_WIN7) return false; @@ -477,6 +512,8 @@ bool TaskbarPinShortcutLink(const wchar_t* shortcut) { } bool TaskbarUnpinShortcutLink(const wchar_t* shortcut) { + base::ThreadRestrictions::AssertIOAllowed(); + // "Unpin from taskbar" is only supported after Win7. if (base::win::GetVersion() < base::win::VERSION_WIN7) return false; @@ -487,6 +524,8 @@ bool TaskbarUnpinShortcutLink(const wchar_t* shortcut) { } bool GetTempDir(FilePath* path) { + base::ThreadRestrictions::AssertIOAllowed(); + wchar_t temp_path[MAX_PATH + 1]; DWORD path_len = ::GetTempPath(MAX_PATH, temp_path); if (path_len >= MAX_PATH || path_len <= 0) @@ -503,6 +542,8 @@ bool GetShmemTempDir(FilePath* path) { } bool CreateTemporaryFile(FilePath* path) { + base::ThreadRestrictions::AssertIOAllowed(); + FilePath temp_file; if (!GetTempDir(path)) @@ -517,6 +558,7 @@ bool CreateTemporaryFile(FilePath* path) { } FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) { + base::ThreadRestrictions::AssertIOAllowed(); return CreateAndOpenTemporaryFile(path); } @@ -525,6 +567,7 @@ FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) { // TODO(jrg): is there equivalent call to use on Windows instead of // going 2-step? FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { + base::ThreadRestrictions::AssertIOAllowed(); if (!CreateTemporaryFileInDir(dir, path)) { return NULL; } @@ -536,6 +579,8 @@ FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) { + base::ThreadRestrictions::AssertIOAllowed(); + wchar_t temp_name[MAX_PATH + 1]; if (!GetTempFileName(dir.value().c_str(), L"", 0, temp_name)) { @@ -558,6 +603,8 @@ bool CreateTemporaryFileInDir(const FilePath& dir, bool CreateTemporaryDirInDir(const FilePath& base_dir, const FilePath::StringType& prefix, FilePath* new_dir) { + base::ThreadRestrictions::AssertIOAllowed(); + FilePath path_to_create; srand(static_cast<uint32>(time(NULL))); @@ -582,6 +629,8 @@ bool CreateTemporaryDirInDir(const FilePath& base_dir, bool CreateNewTempDirectory(const FilePath::StringType& prefix, FilePath* new_temp_path) { + base::ThreadRestrictions::AssertIOAllowed(); + FilePath system_temp_dir; if (!GetTempDir(&system_temp_dir)) return false; @@ -590,6 +639,8 @@ bool CreateNewTempDirectory(const FilePath::StringType& prefix, } bool CreateDirectory(const FilePath& full_path) { + base::ThreadRestrictions::AssertIOAllowed(); + // If the path exists, we've succeeded if it's a directory, failed otherwise. const wchar_t* full_path_str = full_path.value().c_str(); DWORD fileattr = ::GetFileAttributes(full_path_str); @@ -636,6 +687,8 @@ bool CreateDirectory(const FilePath& full_path) { } bool GetFileInfo(const FilePath& file_path, base::PlatformFileInfo* results) { + base::ThreadRestrictions::AssertIOAllowed(); + WIN32_FILE_ATTRIBUTE_DATA attr; if (!GetFileAttributesEx(file_path.value().c_str(), GetFileExInfoStandard, &attr)) { @@ -657,15 +710,18 @@ bool GetFileInfo(const FilePath& file_path, base::PlatformFileInfo* results) { } FILE* OpenFile(const FilePath& filename, const char* mode) { + base::ThreadRestrictions::AssertIOAllowed(); std::wstring w_mode = ASCIIToWide(std::string(mode)); return _wfsopen(filename.value().c_str(), w_mode.c_str(), _SH_DENYNO); } FILE* OpenFile(const std::string& filename, const char* mode) { + base::ThreadRestrictions::AssertIOAllowed(); return _fsopen(filename.c_str(), mode, _SH_DENYNO); } int ReadFile(const FilePath& filename, char* data, int size) { + base::ThreadRestrictions::AssertIOAllowed(); base::win::ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, @@ -684,6 +740,7 @@ int ReadFile(const FilePath& filename, char* data, int size) { } int WriteFile(const FilePath& filename, const char* data, int size) { + base::ThreadRestrictions::AssertIOAllowed(); base::win::ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_WRITE, 0, @@ -718,6 +775,8 @@ int WriteFile(const FilePath& filename, const char* data, int size) { bool RenameFileAndResetSecurityDescriptor(const FilePath& source_file_path, const FilePath& target_file_path) { + base::ThreadRestrictions::AssertIOAllowed(); + // The parameters to SHFileOperation must be terminated with 2 NULL chars. std::wstring source = source_file_path.value(); std::wstring target = target_file_path.value(); @@ -740,6 +799,8 @@ bool RenameFileAndResetSecurityDescriptor(const FilePath& source_file_path, // Gets the current working directory for the process. bool GetCurrentDirectory(FilePath* dir) { + base::ThreadRestrictions::AssertIOAllowed(); + wchar_t system_buffer[MAX_PATH]; system_buffer[0] = 0; DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer); @@ -755,6 +816,7 @@ bool GetCurrentDirectory(FilePath* dir) { // Sets the current working directory for the process. bool SetCurrentDirectory(const FilePath& directory) { + base::ThreadRestrictions::AssertIOAllowed(); BOOL ret = ::SetCurrentDirectory(directory.value().c_str()); return ret != 0; } @@ -812,6 +874,8 @@ FilePath FileEnumerator::GetFilename(const FindInfo& find_info) { } FilePath FileEnumerator::Next() { + base::ThreadRestrictions::AssertIOAllowed(); + while (has_find_data_ || !pending_paths_.empty()) { if (!has_find_data_) { // The last find FindFirstFile operation is done, prepare a new one. @@ -883,6 +947,8 @@ MemoryMappedFile::MemoryMappedFile() } bool MemoryMappedFile::MapFileToMemoryInternal() { + base::ThreadRestrictions::AssertIOAllowed(); + if (file_ == INVALID_HANDLE_VALUE) return false; @@ -926,12 +992,14 @@ void MemoryMappedFile::CloseHandles() { bool HasFileBeenModifiedSince(const FileEnumerator::FindInfo& find_info, const base::Time& cutoff_time) { + base::ThreadRestrictions::AssertIOAllowed(); long result = CompareFileTime(&find_info.ftLastWriteTime, &cutoff_time.ToFileTime()); return result == 1 || result == 0; } bool NormalizeFilePath(const FilePath& path, FilePath* real_path) { + base::ThreadRestrictions::AssertIOAllowed(); FilePath mapped_file; if (!NormalizeToNativeFilePath(path, &mapped_file)) return false; @@ -943,6 +1011,7 @@ bool NormalizeFilePath(const FilePath& path, FilePath* real_path) { } bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) { + base::ThreadRestrictions::AssertIOAllowed(); // In Vista, GetFinalPathNameByHandle() would give us the real path // from a file handle. If we ever deprecate XP, consider changing the // code below to a call to GetFinalPathNameByHandle(). The method this @@ -998,6 +1067,7 @@ bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) { bool PreReadImage(const wchar_t* file_path, size_t size_to_read, size_t step_size) { + base::ThreadRestrictions::AssertIOAllowed(); if (base::win::GetVersion() > base::win::VERSION_XP) { // Vista+ branch. On these OSes, the forced reads through the DLL actually // slows warm starts. The solution is to sequentially read file contents diff --git a/base/file_version_info_win.cc b/base/file_version_info_win.cc index d1049b8..6c69708 100644 --- a/base/file_version_info_win.cc +++ b/base/file_version_info_win.cc @@ -10,12 +10,14 @@ #include "base/file_version_info.h" #include "base/logging.h" #include "base/path_service.h" +#include "base/thread_restrictions.h" // This has to be last. #include <strsafe.h> FileVersionInfoWin::FileVersionInfoWin(void* data, int language, int code_page) : language_(language), code_page_(code_page) { + base::ThreadRestrictions::AssertIOAllowed(); data_.reset((char*) data); fixed_file_info_ = NULL; UINT size; @@ -43,6 +45,8 @@ FileVersionInfo* FileVersionInfo::CreateFileVersionInfoForCurrentModule() { // static FileVersionInfo* FileVersionInfo::CreateFileVersionInfo( const FilePath& file_path) { + base::ThreadRestrictions::AssertIOAllowed(); + DWORD dummy; const wchar_t* path = file_path.value().c_str(); DWORD length = ::GetFileVersionInfoSize(path, &dummy); diff --git a/base/gtk_util.cc b/base/gtk_util.cc deleted file mode 100644 index 79fc485..0000000 --- a/base/gtk_util.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2010 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/gtk_util.h" - -namespace gtk_util { - -namespace { - -// Common implementation of ConvertAcceleratorsFromWindowsStyle() and -// RemoveWindowsStyleAccelerators(). -// Replaces all ampersands (as used in our grd files to indicate mnemonics) -// to |target|. Similarly any underscores get replaced with two underscores as -// is needed by pango. -std::string ConvertAmperstandsTo(const std::string& label, - const std::string& target) { - std::string ret; - ret.reserve(label.length() * 2); - for (size_t i = 0; i < label.length(); ++i) { - if ('_' == label[i]) { - ret.push_back('_'); - ret.push_back('_'); - } else if ('&' == label[i]) { - if (i + 1 < label.length() && '&' == label[i + 1]) { - ret.push_back('&'); - ++i; - } else { - ret.append(target); - } - } else { - ret.push_back(label[i]); - } - } - - return ret; -} - -} // namespace - -std::string ConvertAcceleratorsFromWindowsStyle(const std::string& label) { - return ConvertAmperstandsTo(label, "_"); -} - -std::string RemoveWindowsStyleAccelerators(const std::string& label) { - return ConvertAmperstandsTo(label, ""); -} - -} // namespace gtk_util diff --git a/base/gtk_util.h b/base/gtk_util.h deleted file mode 100644 index 435780d..0000000 --- a/base/gtk_util.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2010 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_GTK_UTIL_H_ -#define BASE_GTK_UTIL_H_ -#pragma once - -#include <string> - -namespace gtk_util { - -// Change windows accelerator style to GTK style. (GTK uses _ for -// accelerators. Windows uses & with && as an escape for &.) -std::string ConvertAcceleratorsFromWindowsStyle(const std::string& label); - -// Removes the "&" accelerators from a Windows label. -std::string RemoveWindowsStyleAccelerators(const std::string& label); - -} // namespace gtk_util - -#endif // BASE_GTK_UTIL_H_ diff --git a/base/hmac_openssl.cc b/base/hmac_openssl.cc new file mode 100644 index 0000000..f45d3a7 --- /dev/null +++ b/base/hmac_openssl.cc @@ -0,0 +1,57 @@ +// Copyright (c) 2010 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/hmac.h" + +#include <openssl/hmac.h> + +#include <algorithm> +#include <vector> + +#include "base/logging.h" +#include "base/openssl_util.h" +#include "base/scoped_ptr.h" +#include "base/stl_util-inl.h" + +namespace base { + +struct HMACPlatformData { + std::vector<unsigned char> key; +}; + +HMAC::HMAC(HashAlgorithm hash_alg) + : hash_alg_(hash_alg), plat_(new HMACPlatformData()) { + // Only SHA-1 and SHA-256 hash algorithms are supported now. + DCHECK(hash_alg_ == SHA1 || hash_alg_ == SHA256); +} + +bool HMAC::Init(const unsigned char* key, int key_length) { + // Init must not be called more than once on the same HMAC object. + DCHECK(plat_->key.empty()); + + plat_->key.assign(key, key + key_length); + return true; +} + +HMAC::~HMAC() { + // Zero out key copy. + plat_->key.assign(plat_->key.size(), 0); + STLClearObject(&plat_->key); +} + +bool HMAC::Sign(const std::string& data, + unsigned char* digest, + int digest_length) { + DCHECK_GE(digest_length, 0); + DCHECK(!plat_->key.empty()); // Init must be called before Sign. + + ScopedOpenSSLSafeSizeBuffer<EVP_MAX_MD_SIZE> result(digest, digest_length); + return ::HMAC(hash_alg_ == SHA1 ? EVP_sha1() : EVP_sha256(), + &plat_->key[0], plat_->key.size(), + reinterpret_cast<const unsigned char*>(data.data()), + data.size(), + result.safe_buffer(), NULL); +} + +} // namespace base diff --git a/base/i18n/icu_string_conversions.cc b/base/i18n/icu_string_conversions.cc index 9014a7b..c353feb 100644 --- a/base/i18n/icu_string_conversions.cc +++ b/base/i18n/icu_string_conversions.cc @@ -9,9 +9,11 @@ #include "base/basictypes.h" #include "base/logging.h" #include "base/string_util.h" +#include "base/utf_string_conversions.h" #include "unicode/ucnv.h" #include "unicode/ucnv_cb.h" #include "unicode/ucnv_err.h" +#include "unicode/unorm.h" #include "unicode/ustring.h" namespace base { @@ -264,4 +266,28 @@ bool CodepageToWide(const std::string& encoded, #endif // defined(WCHAR_T_IS_UTF32) } +bool ConvertToUtf8AndNormalize(const std::string& text, + const std::string& charset, + std::string* result) { + result->clear(); + string16 utf16; + if (!CodepageToUTF16( + text, charset.c_str(), OnStringConversionError::FAIL, &utf16)) + return false; + + UErrorCode status = U_ZERO_ERROR; + size_t max_length = utf16.length() + 1; + string16 normalized_utf16; + int actual_length = unorm_normalize( + utf16.c_str(), utf16.length(), UNORM_NFC, 0, + WriteInto(&normalized_utf16, max_length), + static_cast<int>(max_length), &status); + if (!U_SUCCESS(status)) + return false; + normalized_utf16.resize(actual_length); + + return UTF16ToUTF8(normalized_utf16.data(), + normalized_utf16.length(), result); +} + } // namespace base diff --git a/base/i18n/icu_string_conversions.h b/base/i18n/icu_string_conversions.h index 1495cae..901771b 100644 --- a/base/i18n/icu_string_conversions.h +++ b/base/i18n/icu_string_conversions.h @@ -64,6 +64,12 @@ bool CodepageToWide(const std::string& encoded, OnStringConversionError::Type on_error, std::wstring* wide); +// Converts from any codepage to UTF-8 and ensures the resulting UTF-8 is +// normalized. +bool ConvertToUtf8AndNormalize(const std::string& text, + const std::string& charset, + std::string* result); + } // namespace base #endif // BASE_I18N_ICU_STRING_CONVERSIONS_H_ diff --git a/base/i18n/icu_string_conversions_unittest.cc b/base/i18n/icu_string_conversions_unittest.cc index 2083fa9..40b0fed 100644 --- a/base/i18n/icu_string_conversions_unittest.cc +++ b/base/i18n/icu_string_conversions_unittest.cc @@ -11,6 +11,7 @@ #include "base/basictypes.h" #include "base/i18n/icu_string_conversions.h" #include "base/logging.h" +#include "base/string_piece.h" #include "base/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" @@ -325,4 +326,33 @@ TEST(ICUStringConversionsTest, ConvertBetweenCodepageAndUTF16) { } } +static const struct { + const char* encoded; + const char* codepage_name; + bool expected_success; + const char* expected_value; +} kConvertAndNormalizeCases[] = { + {"foo-\xe4.html", "iso-8859-1", true, "foo-\xc3\xa4.html"}, + {"foo-\xe4.html", "iso-8859-7", true, "foo-\xce\xb4.html"}, + {"foo-\xe4.html", "foo-bar", false, ""}, + {"foo-\xff.html", "ascii", false, ""}, + {"foo.html", "ascii", true, "foo.html"}, + {"foo-a\xcc\x88.html", "utf-8", true, "foo-\xc3\xa4.html"}, + {"\x95\x32\x82\x36\xD2\xBB", "gb18030", true, "\xF0\xA0\x80\x80\xE4\xB8\x80"}, + {"\xA7\x41\xA6\x6E", "big5", true, "\xE4\xBD\xA0\xE5\xA5\xBD"}, + // Windows-1258 does have a combining character at xD2 (which is U+0309). + // The sequence of (U+00E2, U+0309) is also encoded as U+1EA9. + {"foo\xE2\xD2", "windows-1258", true, "foo\xE1\xBA\xA9"}, +}; +TEST(ICUStringConversionsTest, ConvertToUtf8AndNormalize) { + std::string result; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kConvertAndNormalizeCases); ++i) { + bool success = ConvertToUtf8AndNormalize( + kConvertAndNormalizeCases[i].encoded, + kConvertAndNormalizeCases[i].codepage_name, &result); + EXPECT_EQ(kConvertAndNormalizeCases[i].expected_success, success); + EXPECT_EQ(kConvertAndNormalizeCases[i].expected_value, result); + } +} + } // namespace base diff --git a/base/i18n/rtl.cc b/base/i18n/rtl.cc index f5381a2..9ff12d8 100644 --- a/base/i18n/rtl.cc +++ b/base/i18n/rtl.cc @@ -18,25 +18,57 @@ #include <gtk/gtk.h> #endif +namespace { + +// Extract language and country, ignore keywords, concatenate using dash. +std::string GetLocaleString(const icu::Locale& locale) { + const char* language = locale.getLanguage(); + const char* country = locale.getCountry(); + + std::string result = + (language != NULL && *language != '\0') ? language : "und"; + + if (country != NULL && *country != '\0') { + result += '-'; + result += country; + } + + return result; +} + +} // namespace + namespace base { namespace i18n { // Represents the locale-specific ICU text direction. static TextDirection g_icu_text_direction = UNKNOWN_DIRECTION; +#if defined(OS_WIN) void GetLanguageAndRegionFromOS(std::string* lang, std::string* region) { // Later we may have to change this to be OS-dependent so that // it's not affected by ICU's default locale. It's all right // to do this way because SetICUDefaultLocale is internal // to this file and we know that it's not yet called when this function // is called. - icu::Locale locale = icu::Locale::getDefault(); + const icu::Locale& locale = icu::Locale::getDefault(); const char* language = locale.getLanguage(); const char* country = locale.getCountry(); DCHECK(language); *lang = language; *region = country; } +#endif + +// Convert the ICU default locale to a string. +std::string GetConfiguredLocale() { + return GetLocaleString(icu::Locale::getDefault()); +} + +// Convert the ICU canonicalized locale to a string. +std::string GetCanonicalLocale(const char* locale) { + return GetLocaleString(icu::Locale::createCanonical(locale)); +} // Convert Chrome locale name to ICU locale name std::string ICULocaleName(const std::string& locale_string) { @@ -50,13 +82,14 @@ std::string ICULocaleName(const std::string& locale_string) { // locale. If it's es-RR other than es-ES, map to es-RR. Otherwise, map // to es-MX (the most populous in Spanish-speaking Latin America). if (LowerCaseEqualsASCII(locale_string, "es-419")) { - std::string lang, region; - GetLanguageAndRegionFromOS(&lang, ®ion); - if (LowerCaseEqualsASCII(lang, "es") && - !LowerCaseEqualsASCII(region, "es")) { - lang.append("-"); - lang.append(region); - return lang; + const icu::Locale& locale = icu::Locale::getDefault(); + std::string language = locale.getLanguage(); + const char* country = locale.getCountry(); + if (LowerCaseEqualsASCII(language, "es") && + !LowerCaseEqualsASCII(country, "es")) { + language += '-'; + language += country; + return language; } return "es-MX"; } diff --git a/base/i18n/rtl.h b/base/i18n/rtl.h index 2fe932c..52b1a2b 100644 --- a/base/i18n/rtl.h +++ b/base/i18n/rtl.h @@ -29,8 +29,18 @@ enum TextDirection { LEFT_TO_RIGHT, }; -// Get language and region from the OS. +#if defined(OS_WIN) +// Get language and region from the OS. Used by Chrome Frame. void GetLanguageAndRegionFromOS(std::string* lang, std::string* region); +#endif + +// Get the locale that the currently running process has been configured to use. +// The return value is of the form language[-country] (e.g., en-US) where the +// language is the 2 or 3 letter code from ISO-639. +std::string GetConfiguredLocale(); + +// Canonicalize a string (eg. a POSIX locale string) to a Chrome locale name. +std::string GetCanonicalLocale(const char* locale); // Sets the default locale of ICU. // Once the application locale of Chrome in GetApplicationLocale is determined, @@ -136,7 +146,7 @@ void WrapPathWithLTRFormatting(const FilePath& path, // string is wrapped with LRE (Left-To-Right Embedding) and PDF (Pop // Directional Formatting) marks and returned. In LTR locale, the string itself // is returned. -string16 GetDisplayStringInLTRDirectionality(const string16& text) +string16 GetDisplayStringInLTRDirectionality(const string16& text) WARN_UNUSED_RESULT; // Strip the beginning (U+202A..U+202B, U+202D..U+202E) and/or ending (U+202C) diff --git a/base/leak_tracker_unittest.cc b/base/leak_tracker_unittest.cc deleted file mode 100644 index 0217b17..0000000 --- a/base/leak_tracker_unittest.cc +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2009 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/leak_tracker.h" -#include "base/scoped_ptr.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -class ClassA { - private: - base::LeakTracker<ClassA> leak_tracker_; -}; - -class ClassB { - private: - base::LeakTracker<ClassB> leak_tracker_; -}; - -#ifndef ENABLE_LEAK_TRACKER - -// If leak tracking is disabled, we should do nothing. -TEST(LeakTrackerTest, NotEnabled) { - EXPECT_EQ(-1, base::LeakTracker<ClassA>::NumLiveInstances()); - EXPECT_EQ(-1, base::LeakTracker<ClassB>::NumLiveInstances()); - - // Use scoped_ptr so compiler doesn't complain about unused variables. - scoped_ptr<ClassA> a1(new ClassA); - scoped_ptr<ClassB> b1(new ClassB); - scoped_ptr<ClassB> b2(new ClassB); - - EXPECT_EQ(-1, base::LeakTracker<ClassA>::NumLiveInstances()); - EXPECT_EQ(-1, base::LeakTracker<ClassB>::NumLiveInstances()); -} - -#else - -TEST(LeakTrackerTest, Basic) { - { - ClassA a1; - - EXPECT_EQ(1, base::LeakTracker<ClassA>::NumLiveInstances()); - EXPECT_EQ(0, base::LeakTracker<ClassB>::NumLiveInstances()); - - ClassB b1; - ClassB b2; - - EXPECT_EQ(1, base::LeakTracker<ClassA>::NumLiveInstances()); - EXPECT_EQ(2, base::LeakTracker<ClassB>::NumLiveInstances()); - - scoped_ptr<ClassA> a2(new ClassA); - - EXPECT_EQ(2, base::LeakTracker<ClassA>::NumLiveInstances()); - EXPECT_EQ(2, base::LeakTracker<ClassB>::NumLiveInstances()); - - a2.reset(); - - EXPECT_EQ(1, base::LeakTracker<ClassA>::NumLiveInstances()); - EXPECT_EQ(2, base::LeakTracker<ClassB>::NumLiveInstances()); - } - - EXPECT_EQ(0, base::LeakTracker<ClassA>::NumLiveInstances()); - EXPECT_EQ(0, base::LeakTracker<ClassB>::NumLiveInstances()); -} - -// Try some orderings of create/remove to hit different cases in the linked-list -// assembly. -TEST(LeakTrackerTest, LinkedList) { - EXPECT_EQ(0, base::LeakTracker<ClassB>::NumLiveInstances()); - - scoped_ptr<ClassA> a1(new ClassA); - scoped_ptr<ClassA> a2(new ClassA); - scoped_ptr<ClassA> a3(new ClassA); - scoped_ptr<ClassA> a4(new ClassA); - - EXPECT_EQ(4, base::LeakTracker<ClassA>::NumLiveInstances()); - - // Remove the head of the list (a1). - a1.reset(); - EXPECT_EQ(3, base::LeakTracker<ClassA>::NumLiveInstances()); - - // Remove the tail of the list (a4). - a4.reset(); - EXPECT_EQ(2, base::LeakTracker<ClassA>::NumLiveInstances()); - - // Append to the new tail of the list (a3). - scoped_ptr<ClassA> a5(new ClassA); - EXPECT_EQ(3, base::LeakTracker<ClassA>::NumLiveInstances()); - - a2.reset(); - a3.reset(); - - EXPECT_EQ(1, base::LeakTracker<ClassA>::NumLiveInstances()); - - a5.reset(); - EXPECT_EQ(0, base::LeakTracker<ClassA>::NumLiveInstances()); -} - -TEST(LeakTrackerTest, NoOpCheckForLeaks) { - // There are no live instances of ClassA, so this should do nothing. - base::LeakTracker<ClassA>::CheckForLeaks(); -} - -#endif // ENABLE_LEAK_TRACKER - -} // namespace diff --git a/base/logging.cc b/base/logging.cc index 46f22c5..92dd33d 100644 --- a/base/logging.cc +++ b/base/logging.cc @@ -42,7 +42,8 @@ typedef pthread_mutex_t* MutexHandle; #include "base/base_switches.h" #include "base/command_line.h" -#include "base/debug_util.h" +#include "base/debug/debugger.h" +#include "base/debug/stack_trace.h" #include "base/eintr_wrapper.h" #include "base/lock_impl.h" #if defined(OS_POSIX) @@ -224,7 +225,7 @@ class LoggingLock { #if DEBUG // Keep the error code for debugging int error = GetLastError(); // NOLINT - DebugUtil::BreakDebugger(); + base::debug::BreakDebugger(); #endif // Return nicely without putting initialized to true. return; @@ -339,7 +340,7 @@ bool InitializeLogFileHandle() { return true; } -void BaseInitLoggingImpl(const PathChar* new_log_file, +bool BaseInitLoggingImpl(const PathChar* new_log_file, LoggingDestination logging_dest, LogLockingState lock_log, OldFileDeletionState delete_old) { @@ -357,7 +358,8 @@ void BaseInitLoggingImpl(const PathChar* new_log_file, command_line->HasSwitch(switches::kVModule)) { g_vlog_info = new VlogInfo(command_line->GetSwitchValueASCII(switches::kV), - command_line->GetSwitchValueASCII(switches::kVModule)); + command_line->GetSwitchValueASCII(switches::kVModule), + &min_log_level); } #endif @@ -377,7 +379,7 @@ void BaseInitLoggingImpl(const PathChar* new_log_file, // ignore file options if logging is disabled or only to system if (logging_destination == LOG_NONE || logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG) - return; + return true; if (!log_file_name) log_file_name = new PathString(); @@ -385,17 +387,21 @@ void BaseInitLoggingImpl(const PathChar* new_log_file, if (delete_old == DELETE_OLD_LOG_FILE) DeleteFilePath(*log_file_name); - InitializeLogFileHandle(); + return InitializeLogFileHandle(); } void SetMinLogLevel(int level) { - min_log_level = level; + min_log_level = std::min(LOG_ERROR_REPORT, level); } int GetMinLogLevel() { return min_log_level; } +int GetVlogVerbosity() { + return std::max(-1, LOG_INFO - GetMinLogLevel()); +} + int GetVlogLevelHelper(const char* file, size_t N) { #ifdef ANDROID return 0; @@ -403,8 +409,12 @@ int GetVlogLevelHelper(const char* file, size_t N) { DCHECK_GT(N, 0U); return g_vlog_info ? g_vlog_info->GetVlogLevel(base::StringPiece(file, N - 1)) : +<<<<<<< HEAD VlogInfo::kDefaultVlogLevel; #endif +======= + GetVlogVerbosity(); +>>>>>>> chromium.org at r65505 } void SetLogItems(bool enable_process_id, bool enable_thread_id, @@ -431,6 +441,10 @@ void SetLogMessageHandler(LogMessageHandlerFunction handler) { log_message_handler = handler; } +LogMessageHandlerFunction GetLogMessageHandler() { + return log_message_handler; +} + // MSVC doesn't like complex extern templates and DLLs. #if !defined(COMPILER_MSVC) // Explicit instantiations for commonly used comparisons. @@ -515,39 +529,39 @@ LogMessage::SaveLastError::~SaveLastError() { LogMessage::LogMessage(const char* file, int line, LogSeverity severity, int ctr) - : severity_(severity) { + : severity_(severity), file_(file), line_(line) { Init(file, line); } LogMessage::LogMessage(const char* file, int line, const CheckOpString& result) - : severity_(LOG_FATAL) { + : severity_(LOG_FATAL), file_(file), line_(line) { Init(file, line); stream_ << "Check failed: " << (*result.str_); } LogMessage::LogMessage(const char* file, int line, LogSeverity severity, const CheckOpString& result) - : severity_(severity) { + : severity_(severity), file_(file), line_(line) { Init(file, line); stream_ << "Check failed: " << (*result.str_); } LogMessage::LogMessage(const char* file, int line) - : severity_(LOG_INFO) { + : severity_(LOG_INFO), file_(file), line_(line) { Init(file, line); } LogMessage::LogMessage(const char* file, int line, LogSeverity severity) - : severity_(severity) { + : severity_(severity), file_(file), line_(line) { Init(file, line); } // writes the common header info to the stream void LogMessage::Init(const char* file, int line) { - // log only the filename - const char* last_slash = strrchr(file, '\\'); - if (last_slash) - file = last_slash + 1; + base::StringPiece filename(file); + size_t last_slash_pos = filename.find_last_of("\\/"); + if (last_slash_pos != base::StringPiece::npos) + filename.remove_prefix(last_slash_pos + 1); // TODO(darin): It might be nice if the columns were fixed width. @@ -576,8 +590,12 @@ void LogMessage::Init(const char* file, int line) { } if (log_tickcount) stream_ << TickCount() << ':'; - stream_ << log_severity_names[severity_] << ":" << file << - "(" << line << ")] "; + if (severity_ >= 0) + stream_ << log_severity_names[severity_]; + else + stream_ << "VERBOSE" << -severity_; + + stream_ << ":" << file << "(" << line << ")] "; message_start_ = stream_.tellp(); } @@ -592,7 +610,7 @@ LogMessage::~LogMessage() { #ifndef NDEBUG if (severity_ == LOG_FATAL) { // Include a stack trace on a fatal. - StackTrace trace; + base::debug::StackTrace trace; stream_ << std::endl; // Newline to separate from log message. trace.OutputToStream(&stream_); } @@ -601,8 +619,11 @@ LogMessage::~LogMessage() { std::string str_newline(stream_.str()); // Give any log message handler first dibs on the message. - if (log_message_handler && log_message_handler(severity_, str_newline)) + if (log_message_handler && log_message_handler(severity_, file_, line_, + message_start_, str_newline)) { + // The handler took care of it, no further processing. return; + } if (logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG || logging_destination == LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG) { @@ -649,8 +670,8 @@ LogMessage::~LogMessage() { if (severity_ == LOG_FATAL) { // display a message or break into the debugger on a fatal error - if (DebugUtil::BeingDebugged()) { - DebugUtil::BreakDebugger(); + if (base::debug::BeingDebugged()) { + base::debug::BreakDebugger(); } else { if (log_assert_handler) { // make a copy of the string for the handler out of paranoia @@ -665,7 +686,7 @@ LogMessage::~LogMessage() { DisplayDebugMessageInDialog(stream_.str()); #endif // Crash the process to generate a dump. - DebugUtil::BreakDebugger(); + base::debug::BreakDebugger(); } } } else if (severity_ == LOG_ERROR_REPORT) { @@ -804,7 +825,7 @@ void RawLog(int level, const char* message) { } if (level == LOG_FATAL) - DebugUtil::BreakDebugger(); + base::debug::BreakDebugger(); } } // namespace logging diff --git a/base/logging.h b/base/logging.h index 981669a..6689eec 100644 --- a/base/logging.h +++ b/base/logging.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -81,16 +81,22 @@ // // These always log at the INFO log level (when they log at all). // The verbose logging can also be turned on module-by-module. For instance, -// --vmodule=profile=2,icon_loader=1,browser_*=3 --v=0 +// --vmodule=profile=2,icon_loader=1,browser_*=3,*/chromeos/*=4 --v=0 // will cause: // a. VLOG(2) and lower messages to be printed from profile.{h,cc} // b. VLOG(1) and lower messages to be printed from icon_loader.{h,cc} // c. VLOG(3) and lower messages to be printed from files prefixed with // "browser" -// d. VLOG(0) and lower messages to be printed from elsewhere +// d. VLOG(4) and lower messages to be printed from files under a +// "chromeos" directory. +// e. VLOG(0) and lower messages to be printed from elsewhere // // The wildcarding functionality shown by (c) supports both '*' (match -// 0 or more characters) and '?' (match any single character) wildcards. +// 0 or more characters) and '?' (match any single character) +// wildcards. Any pattern containing a forward or backward slash will +// be tested against the whole pathname and not just the module. +// E.g., "*/foo/bar/*=2" would change the logging level for all code +// in source files under a "foo/bar" directory. // // There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as // @@ -179,7 +185,7 @@ typedef char PathChar; // Implementation of the InitLogging() method declared below. We use a // more-specific name so we can #define it above without affecting other code // that has named stuff "InitLogging". -void BaseInitLoggingImpl(const PathChar* log_file, +bool BaseInitLoggingImpl(const PathChar* log_file, LoggingDestination logging_dest, LogLockingState lock_log, OldFileDeletionState delete_old); @@ -194,22 +200,27 @@ void BaseInitLoggingImpl(const PathChar* log_file, // The default log file is initialized to "debug.log" in the application // directory. You probably don't want this, especially since the program // directory may not be writable on an enduser's system. -inline void InitLogging(const PathChar* log_file, +inline bool InitLogging(const PathChar* log_file, LoggingDestination logging_dest, LogLockingState lock_log, OldFileDeletionState delete_old) { - BaseInitLoggingImpl(log_file, logging_dest, lock_log, delete_old); + return BaseInitLoggingImpl(log_file, logging_dest, lock_log, delete_old); } // Sets the log level. Anything at or above this level will be written to the // log file/displayed to the user (if applicable). Anything below this level -// will be silently ignored. The log level defaults to 0 (everything is logged) -// if this function is not called. +// will be silently ignored. The log level defaults to 0 (everything is logged +// up to level INFO) if this function is not called. +// Note that log messages for VLOG(x) are logged at level -x, so setting +// the min log level to negative values enables verbose logging. void SetMinLogLevel(int level); // Gets the current log level. int GetMinLogLevel(); +// Gets the VLOG default verbosity level. +int GetVlogVerbosity(); + // Gets the current vlog level for the given file (usually taken from // __FILE__). @@ -239,6 +250,7 @@ void SetShowErrorDialogs(bool enable_dialogs); // (e.g. a silent one for Unit Tests) typedef void (*LogAssertHandlerFunction)(const std::string& str); void SetLogAssertHandler(LogAssertHandlerFunction handler); + // Sets the Log Report Handler that will be used to notify of check failures // in non-debug mode. The default handler shows a dialog box and continues // the execution, however clients can use this function to override with their @@ -250,10 +262,15 @@ void SetLogReportHandler(LogReportHandlerFunction handler); // it's sent to other log destinations (if any). // Returns true to signal that it handled the message and the message // should not be sent to other log destinations. -typedef bool (*LogMessageHandlerFunction)(int severity, const std::string& str); +typedef bool (*LogMessageHandlerFunction)(int severity, + const char* file, int line, size_t message_start, const std::string& str); void SetLogMessageHandler(LogMessageHandlerFunction handler); +LogMessageHandlerFunction GetLogMessageHandler(); typedef int LogSeverity; +const LogSeverity LOG_VERBOSE = -1; // This is level 1 verbosity +// Note: the log severities are used to index into the array of names, +// see log_severity_names. const LogSeverity LOG_INFO = 0; const LogSeverity LOG_WARNING = 1; const LogSeverity LOG_ERROR = 2; @@ -310,6 +327,10 @@ const LogSeverity LOG_DFATAL = LOG_FATAL; // Needed for LOG_IS_ON(ERROR). const LogSeverity LOG_0 = LOG_ERROR; +// As special cases, we can assume that LOG_IS_ON(ERROR_REPORT) and +// LOG_IS_ON(FATAL) always hold. Also, LOG_IS_ON(DFATAL) always holds +// in debug mode. In particular, CHECK()s will always fire if they +// fail. #define LOG_IS_ON(severity) \ ((::logging::LOG_ ## severity) >= ::logging::GetMinLogLevel()) @@ -342,9 +363,16 @@ const LogSeverity LOG_0 = LOG_ERROR; #define SYSLOG(severity) LOG(severity) #define SYSLOG_IF(severity, condition) LOG_IF(severity, condition) -#define VLOG(verboselevel) LOG_IF(INFO, VLOG_IS_ON(verboselevel)) -#define VLOG_IF(verboselevel, condition) \ - LOG_IF(INFO, VLOG_IS_ON(verboselevel) && (condition)) +// The VLOG macros log with negative verbosities. +#define VLOG_STREAM(verbose_level) \ + logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream() + +#define VLOG(verbose_level) \ + LAZY_STREAM(VLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level)) + +#define VLOG_IF(verbose_level, condition) \ + LAZY_STREAM(VLOG_STREAM(verbose_level), \ + VLOG_IS_ON(verbose_level) && (condition)) // TODO(akalin): Add more VLOG variants, e.g. VPLOG. @@ -392,9 +420,6 @@ const LogSeverity LOG_0 = LOG_ERROR; // // We make sure CHECK et al. always evaluates their arguments, as // doing CHECK(FunctionWithSideEffect()) is a common idiom. -// -// TODO(akalin): Fix the problem where if the min log level is > -// FATAL, CHECK() et al. won't terminate the program. #define CHECK(condition) \ LAZY_STREAM(LOG_STREAM(FATAL), !(condition)) \ << "Check failed: " #condition ". " @@ -451,43 +476,28 @@ extern template std::string* MakeCheckOpString<std::string, std::string>( #val1 " " #op " " #val2)) \ logging::LogMessage(__FILE__, __LINE__, _result).stream() -// Helper functions for string comparisons. -// To avoid bloat, the definitions are in logging.cc. -// -// TODO(akalin): Actually have the implementations in logging.cc, or -// remove these. -#define DECLARE_CHECK_STROP_IMPL(func, expected) \ - std::string* Check##func##expected##Impl(const char* s1, \ - const char* s2, \ - const char* names); -DECLARE_CHECK_STROP_IMPL(strcmp, true) -DECLARE_CHECK_STROP_IMPL(strcmp, false) -DECLARE_CHECK_STROP_IMPL(_stricmp, true) -DECLARE_CHECK_STROP_IMPL(_stricmp, false) -#undef DECLARE_CHECK_STROP_IMPL - -// Helper macro for string comparisons. -// Don't use this macro directly in your code, use CHECK_STREQ et al below. -#define CHECK_STROP(func, op, expected, s1, s2) \ - while (CheckOpString _result = \ - logging::Check##func##expected##Impl((s1), (s2), \ - #s1 " " #op " " #s2)) \ - LOG(FATAL) << *_result.str_ - -// String (char*) equality/inequality checks. -// CASE versions are case-insensitive. -// -// Note that "s1" and "s2" may be temporary strings which are destroyed -// by the compiler at the end of the current "full expression" -// (e.g. CHECK_STREQ(Foo().c_str(), Bar().c_str())). - -#define CHECK_STREQ(s1, s2) CHECK_STROP(strcmp, ==, true, s1, s2) -#define CHECK_STRNE(s1, s2) CHECK_STROP(strcmp, !=, false, s1, s2) -#define CHECK_STRCASEEQ(s1, s2) CHECK_STROP(_stricmp, ==, true, s1, s2) -#define CHECK_STRCASENE(s1, s2) CHECK_STROP(_stricmp, !=, false, s1, s2) - -#define CHECK_INDEX(I,A) CHECK(I < (sizeof(A)/sizeof(A[0]))) -#define CHECK_BOUND(B,A) CHECK(B <= (sizeof(A)/sizeof(A[0]))) +// Helper functions for CHECK_OP macro. +// The (int, int) specialization works around the issue that the compiler +// will not instantiate the template version of the function on values of +// unnamed enum type - see comment below. +#define DEFINE_CHECK_OP_IMPL(name, op) \ + template <class t1, class t2> \ + inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \ + const char* names) { \ + if (v1 op v2) return NULL; \ + else return MakeCheckOpString(v1, v2, names); \ + } \ + inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \ + if (v1 op v2) return NULL; \ + else return MakeCheckOpString(v1, v2, names); \ + } +DEFINE_CHECK_OP_IMPL(EQ, ==) +DEFINE_CHECK_OP_IMPL(NE, !=) +DEFINE_CHECK_OP_IMPL(LE, <=) +DEFINE_CHECK_OP_IMPL(LT, < ) +DEFINE_CHECK_OP_IMPL(GE, >=) +DEFINE_CHECK_OP_IMPL(GT, > ) +#undef DEFINE_CHECK_OP_IMPL #define CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2) #define CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2) @@ -525,6 +535,7 @@ DECLARE_CHECK_STROP_IMPL(_stricmp, false) #if ENABLE_DLOG +#define DLOG_IS_ON(severity) LOG_IS_ON(severity) #define DLOG_IF(severity, condition) LOG_IF(severity, condition) #define DLOG_ASSERT(condition) LOG_ASSERT(condition) #define DPLOG_IF(severity, condition) PLOG_IF(severity, condition) @@ -540,6 +551,7 @@ DECLARE_CHECK_STROP_IMPL(_stricmp, false) #define DLOG_EAT_STREAM_PARAMETERS \ true ? (void) 0 : ::logging::LogMessageVoidify() & LOG_STREAM(FATAL) +#define DLOG_IS_ON(severity) false #define DLOG_IF(severity, condition) DLOG_EAT_STREAM_PARAMETERS #define DLOG_ASSERT(condition) DLOG_EAT_STREAM_PARAMETERS #define DPLOG_IF(severity, condition) DLOG_EAT_STREAM_PARAMETERS @@ -559,8 +571,6 @@ enum { DEBUG_MODE = ENABLE_DLOG }; #undef ENABLE_DLOG -#define DLOG_IS_ON(severity) (::logging::DEBUG_MODE && LOG_IS_ON(severity)) - #define DLOG(severity) \ LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity)) @@ -586,51 +596,56 @@ enum { DEBUG_MODE = ENABLE_DLOG }; #if defined(NDEBUG) -// Set to true in InitLogging when we want to enable the dchecks in release. -extern bool g_enable_dcheck; -#define DCHECK_IS_ON() (::logging::g_enable_dcheck) -#define DCHECK_SEVERITY ERROR_REPORT +#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \ + COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(ClassName , ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_ERROR_REPORT const LogSeverity LOG_DCHECK = LOG_ERROR_REPORT; +// This is set to true in InitLogging when we want to enable the +// DCHECKs in release. +extern bool g_enable_dcheck; +#define DCHECK_IS_ON() (::logging::g_enable_dcheck && LOG_IS_ON(DCHECK)) #else // defined(NDEBUG) -// On a regular debug build, we want to have DCHECKS enabled. -#define DCHECK_IS_ON() (true) -#define DCHECK_SEVERITY FATAL +// On a regular debug build, we want to have DCHECKs enabled. +#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \ + COMPACT_GOOGLE_LOG_EX_FATAL(ClassName , ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_FATAL const LogSeverity LOG_DCHECK = LOG_FATAL; +#define DCHECK_IS_ON() true #endif // defined(NDEBUG) #else // ENABLE_DCHECK -#define DCHECK_IS_ON() (false) -#define DCHECK_SEVERITY FATAL -const LogSeverity LOG_DCHECK = LOG_FATAL; +// These are just dummy values since DCHECK_IS_ON() is always false in +// this case. +#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \ + COMPACT_GOOGLE_LOG_EX_INFO(ClassName , ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_INFO +const LogSeverity LOG_DCHECK = LOG_INFO; +#define DCHECK_IS_ON() false #endif // ENABLE_DCHECK +#undef ENABLE_DCHECK -// Unlike CHECK et al., DCHECK et al. *does* evaluate their arguments -// lazily. - -// DCHECK et al. also make sure to reference |condition| regardless of +// DCHECK et al. make sure to reference |condition| regardless of // whether DCHECKs are enabled; this is so that we don't get unused // variable warnings if the only use of a variable is in a DCHECK. // This behavior is different from DLOG_IF et al. -#define DCHECK(condition) \ - !DCHECK_IS_ON() ? (void) 0 : \ - LOG_IF(DCHECK_SEVERITY, !(condition)) \ +#define DCHECK(condition) \ + LAZY_STREAM(LOG_STREAM(DCHECK), DCHECK_IS_ON() && !(condition)) \ << "Check failed: " #condition ". " -#define DPCHECK(condition) \ - !DCHECK_IS_ON() ? (void) 0 : \ - PLOG_IF(DCHECK_SEVERITY, !(condition)) \ +#define DPCHECK(condition) \ + LAZY_STREAM(PLOG_STREAM(DCHECK), DCHECK_IS_ON() && !(condition)) \ << "Check failed: " #condition ". " // Helper macro for binary operators. // Don't use this macro directly in your code, use DCHECK_EQ et al below. #define DCHECK_OP(name, op, val1, val2) \ - if (DLOG_IS_ON(DCHECK_SEVERITY)) \ + if (DCHECK_IS_ON()) \ if (logging::CheckOpString _result = \ logging::Check##name##Impl((val1), (val2), \ #val1 " " #op " " #val2)) \ @@ -638,9 +653,10 @@ const LogSeverity LOG_DCHECK = LOG_FATAL; __FILE__, __LINE__, ::logging::LOG_DCHECK, \ _result).stream() -// Equality/Inequality checks - compare two values, and log a LOG_FATAL message -// including the two values when the result is not as expected. The values -// must have operator<<(ostream, ...) defined. +// Equality/Inequality checks - compare two values, and log a +// LOG_DCHECK message including the two values when the result is not +// as expected. The values must have operator<<(ostream, ...) +// defined. // // You may append to the error message like so: // DCHECK_NE(1, 2) << ": The world must be ending!"; @@ -663,49 +679,6 @@ const LogSeverity LOG_DCHECK = LOG_FATAL; #define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2) #define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2) -// Helper macro for string comparisons. -// Don't use this macro directly in your code, use DCHECK_STREQ et al below. -#define DCHECK_STROP(func, op, expected, s1, s2) \ - if (DCHECK_IS_ON()) CHECK_STROP(func, op, expected, s1, s2) - -// String (char*) equality/inequality checks. -// CASE versions are case-insensitive. -// -// Note that "s1" and "s2" may be temporary strings which are destroyed -// by the compiler at the end of the current "full expression" -// (e.g. DCHECK_STREQ(Foo().c_str(), Bar().c_str())). - -#define DCHECK_STREQ(s1, s2) DCHECK_STROP(strcmp, ==, true, s1, s2) -#define DCHECK_STRNE(s1, s2) DCHECK_STROP(strcmp, !=, false, s1, s2) -#define DCHECK_STRCASEEQ(s1, s2) DCHECK_STROP(_stricmp, ==, true, s1, s2) -#define DCHECK_STRCASENE(s1, s2) DCHECK_STROP(_stricmp, !=, false, s1, s2) - -#define DCHECK_INDEX(I,A) DCHECK(I < (sizeof(A)/sizeof(A[0]))) -#define DCHECK_BOUND(B,A) DCHECK(B <= (sizeof(A)/sizeof(A[0]))) - -// Helper functions for CHECK_OP macro. -// The (int, int) specialization works around the issue that the compiler -// will not instantiate the template version of the function on values of -// unnamed enum type - see comment below. -#define DEFINE_CHECK_OP_IMPL(name, op) \ - template <class t1, class t2> \ - inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \ - const char* names) { \ - if (v1 op v2) return NULL; \ - else return MakeCheckOpString(v1, v2, names); \ - } \ - inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \ - if (v1 op v2) return NULL; \ - else return MakeCheckOpString(v1, v2, names); \ - } -DEFINE_CHECK_OP_IMPL(EQ, ==) -DEFINE_CHECK_OP_IMPL(NE, !=) -DEFINE_CHECK_OP_IMPL(LE, <=) -DEFINE_CHECK_OP_IMPL(LT, < ) -DEFINE_CHECK_OP_IMPL(GE, >=) -DEFINE_CHECK_OP_IMPL(GT, > ) -#undef DEFINE_CHECK_OP_IMPL - #define NOTREACHED() DCHECK(false) // Redefine the standard assert to use our nice log files @@ -761,6 +734,10 @@ class LogMessage { std::ostringstream stream_; size_t message_start_; // Offset of the start of the message (past prefix // info). + // The file and line information passed in to the constructor. + const char* file_; + const int line_; + #if defined(OS_WIN) // Stores the current value of GetLastError in the constructor and restores // it in the destructor by calling SetLastError. diff --git a/base/logging_unittest.cc b/base/logging_unittest.cc index fa28ec6..4b7fdbc 100644 --- a/base/logging_unittest.cc +++ b/base/logging_unittest.cc @@ -14,23 +14,35 @@ namespace { using ::testing::Return; +// Needs to be global since log assert handlers can't maintain state. +int log_sink_call_count = 0; + +void LogSink(const std::string& str) { + ++log_sink_call_count; +} + // Class to make sure any manipulations we do to the min log level are // contained (i.e., do not affect other unit tests). -class MinLogLevelSaver { +class LogStateSaver { public: - MinLogLevelSaver() : old_min_log_level_(GetMinLogLevel()) {} + LogStateSaver() : old_min_log_level_(GetMinLogLevel()) {} - ~MinLogLevelSaver() { SetMinLogLevel(old_min_log_level_); } + ~LogStateSaver() { + SetMinLogLevel(old_min_log_level_); + SetLogAssertHandler(NULL); + SetLogReportHandler(NULL); + log_sink_call_count = 0; + } private: int old_min_log_level_; - DISALLOW_COPY_AND_ASSIGN(MinLogLevelSaver); + DISALLOW_COPY_AND_ASSIGN(LogStateSaver); }; class LoggingTest : public testing::Test { private: - MinLogLevelSaver min_log_level_saver_; + LogStateSaver log_state_saver_; }; class MockLogSource { @@ -68,6 +80,64 @@ TEST_F(LoggingTest, BasicLogging) { DVLOG_IF(0, true) << mock_log_source.Log(); } +TEST_F(LoggingTest, LogIsOn) { +#if defined(NDEBUG) + const bool kDfatalIsFatal = false; +#else // defined(NDEBUG) + const bool kDfatalIsFatal = true; +#endif // defined(NDEBUG) + + SetMinLogLevel(LOG_INFO); + EXPECT_TRUE(LOG_IS_ON(INFO)); + EXPECT_TRUE(LOG_IS_ON(WARNING)); + EXPECT_TRUE(LOG_IS_ON(ERROR)); + EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT)); + EXPECT_TRUE(LOG_IS_ON(FATAL)); + EXPECT_TRUE(LOG_IS_ON(DFATAL)); + + SetMinLogLevel(LOG_WARNING); + EXPECT_FALSE(LOG_IS_ON(INFO)); + EXPECT_TRUE(LOG_IS_ON(WARNING)); + EXPECT_TRUE(LOG_IS_ON(ERROR)); + EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT)); + EXPECT_TRUE(LOG_IS_ON(FATAL)); + EXPECT_TRUE(LOG_IS_ON(DFATAL)); + + SetMinLogLevel(LOG_ERROR); + EXPECT_FALSE(LOG_IS_ON(INFO)); + EXPECT_FALSE(LOG_IS_ON(WARNING)); + EXPECT_TRUE(LOG_IS_ON(ERROR)); + EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT)); + EXPECT_TRUE(LOG_IS_ON(FATAL)); + EXPECT_TRUE(LOG_IS_ON(DFATAL)); + + SetMinLogLevel(LOG_ERROR_REPORT); + EXPECT_FALSE(LOG_IS_ON(INFO)); + EXPECT_FALSE(LOG_IS_ON(WARNING)); + EXPECT_FALSE(LOG_IS_ON(ERROR)); + EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT)); + EXPECT_TRUE(LOG_IS_ON(FATAL)); + EXPECT_EQ(kDfatalIsFatal, LOG_IS_ON(DFATAL)); + + // LOG_IS_ON(ERROR_REPORT) should always be true. + SetMinLogLevel(LOG_FATAL); + EXPECT_FALSE(LOG_IS_ON(INFO)); + EXPECT_FALSE(LOG_IS_ON(WARNING)); + EXPECT_FALSE(LOG_IS_ON(ERROR)); + EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT)); + EXPECT_TRUE(LOG_IS_ON(FATAL)); + EXPECT_EQ(kDfatalIsFatal, LOG_IS_ON(DFATAL)); + + // So should LOG_IS_ON(FATAL). + SetMinLogLevel(LOG_FATAL + 1); + EXPECT_FALSE(LOG_IS_ON(INFO)); + EXPECT_FALSE(LOG_IS_ON(WARNING)); + EXPECT_FALSE(LOG_IS_ON(ERROR)); + EXPECT_TRUE(LOG_IS_ON(ERROR_REPORT)); + EXPECT_TRUE(LOG_IS_ON(FATAL)); + EXPECT_EQ(kDfatalIsFatal, LOG_IS_ON(DFATAL)); +} + TEST_F(LoggingTest, LoggingIsLazy) { MockLogSource mock_log_source; EXPECT_CALL(mock_log_source, Log()).Times(0); @@ -93,14 +163,13 @@ TEST_F(LoggingTest, LoggingIsLazy) { DVLOG_IF(1, true) << mock_log_source.Log(); } -TEST_F(LoggingTest, ChecksAreNotLazy) { +TEST_F(LoggingTest, CheckStreamsAreLazy) { MockLogSource mock_log_source, uncalled_mock_log_source; EXPECT_CALL(mock_log_source, Log()).Times(8). WillRepeatedly(Return("check message")); EXPECT_CALL(uncalled_mock_log_source, Log()).Times(0); - SetMinLogLevel(LOG_FATAL + 1); - EXPECT_FALSE(LOG_IS_ON(FATAL)); + SetLogAssertHandler(&LogSink); CHECK(mock_log_source.Log()) << uncalled_mock_log_source.Log(); PCHECK(!mock_log_source.Log()) << mock_log_source.Log(); @@ -122,22 +191,46 @@ TEST_F(LoggingTest, DebugLoggingReleaseBehavior) { DVLOG_IF(1, debug_only_variable) << "test"; } -TEST_F(LoggingTest, DchecksAreLazy) { +TEST_F(LoggingTest, DcheckStreamsAreLazy) { MockLogSource mock_log_source; EXPECT_CALL(mock_log_source, Log()).Times(0); #if !defined(LOGGING_IS_OFFICIAL_BUILD) && defined(NDEBUG) // Unofficial release build. logging::g_enable_dcheck = false; -#else // !defined(LOGGING_IS_OFFICIAL_BUILD) && defined(NDEBUG) - SetMinLogLevel(LOG_FATAL + 1); - EXPECT_FALSE(LOG_IS_ON(FATAL)); -#endif // !defined(LOGGING_IS_OFFICIAL_BUILD) && defined(NDEBUG) DCHECK(mock_log_source.Log()) << mock_log_source.Log(); DPCHECK(mock_log_source.Log()) << mock_log_source.Log(); DCHECK_EQ(0, 0) << mock_log_source.Log(); DCHECK_EQ(mock_log_source.Log(), static_cast<const char*>(NULL)) << mock_log_source.Log(); +#endif // !defined(LOGGING_IS_OFFICIAL_BUILD) && defined(NDEBUG) +} + +TEST_F(LoggingTest, Dcheck) { +#if defined(LOGGING_IS_OFFICIAL_BUILD) + // Official build. + EXPECT_FALSE(DCHECK_IS_ON()); + EXPECT_FALSE(DLOG_IS_ON(DCHECK)); +#elif defined(NDEBUG) + // Unofficial release build. + logging::g_enable_dcheck = true; + logging::SetLogReportHandler(&LogSink); + EXPECT_TRUE(DCHECK_IS_ON()); + EXPECT_FALSE(DLOG_IS_ON(DCHECK)); +#else + // Unofficial debug build. + logging::SetLogAssertHandler(&LogSink); + EXPECT_TRUE(DCHECK_IS_ON()); + EXPECT_TRUE(DLOG_IS_ON(DCHECK)); +#endif // defined(LOGGING_IS_OFFICIAL_BUILD) + + EXPECT_EQ(0, log_sink_call_count); + DCHECK(false); + EXPECT_EQ(DCHECK_IS_ON() ? 1 : 0, log_sink_call_count); + DPCHECK(false); + EXPECT_EQ(DCHECK_IS_ON() ? 2 : 0, log_sink_call_count); + DCHECK_EQ(0, 1); + EXPECT_EQ(DCHECK_IS_ON() ? 3 : 0, log_sink_call_count); } TEST_F(LoggingTest, DcheckReleaseBehavior) { diff --git a/base/logging_win.cc b/base/logging_win.cc index f17cf34..42610b5 100644 --- a/base/logging_win.cc +++ b/base/logging_win.cc @@ -16,31 +16,40 @@ Singleton<logging::LogEventProvider, LogEventSingletonTraits> log_provider; namespace logging { +using base::win::EtwEventLevel; +using base::win::EtwMofEvent; + DEFINE_GUID(kLogEventId, 0x7fe69228, 0x633e, 0x4f06, 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7); LogEventProvider::LogEventProvider() : old_log_level_(LOG_NONE) { } -bool LogEventProvider::LogMessage(int severity, const std::string& message) { +bool LogEventProvider::LogMessage(logging::LogSeverity severity, + const char* file, int line, size_t message_start, + const std::string& message) { EtwEventLevel level = TRACE_LEVEL_NONE; // Convert the log severity to the most appropriate ETW trace level. - switch (severity) { - case LOG_INFO: - level = TRACE_LEVEL_INFORMATION; - break; - case LOG_WARNING: - level = TRACE_LEVEL_WARNING; - break; - case LOG_ERROR: - case LOG_ERROR_REPORT: - level = TRACE_LEVEL_ERROR; - break; - case LOG_FATAL: - level = TRACE_LEVEL_FATAL; - break; - }; + if (severity >= 0) { + switch (severity) { + case LOG_INFO: + level = TRACE_LEVEL_INFORMATION; + break; + case LOG_WARNING: + level = TRACE_LEVEL_WARNING; + break; + case LOG_ERROR: + case LOG_ERROR_REPORT: + level = TRACE_LEVEL_ERROR; + break; + case LOG_FATAL: + level = TRACE_LEVEL_FATAL; + break; + } + } else { // severity < 0 is VLOG verbosity levels. + level = TRACE_LEVEL_INFORMATION - severity; + } // Bail if we're not logging, not at that level, // or if we're post-atexit handling. @@ -48,27 +57,43 @@ bool LogEventProvider::LogMessage(int severity, const std::string& message) { if (provider == NULL || level > provider->enable_level()) return false; - // And now log the event, with stack trace if one is - // requested per our enable flags. - if (provider->enable_flags() & ENABLE_STACK_TRACE_CAPTURE) { + // And now log the event. + if (provider->enable_flags() & ENABLE_LOG_MESSAGE_ONLY) { + EtwMofEvent<1> event(kLogEventId, LOG_MESSAGE, level); + event.SetField(0, message.length() + 1 - message_start, + message.c_str() + message_start); + + provider->Log(event.get()); + } else { const size_t kMaxBacktraceDepth = 32; void* backtrace[kMaxBacktraceDepth]; - DWORD depth = CaptureStackBackTrace(2, kMaxBacktraceDepth, backtrace, NULL); - EtwMofEvent<3> event(kLogEventId, LOG_MESSAGE_WITH_STACKTRACE, level); + DWORD depth = 0; + // Capture a stack trace if one is requested. + // requested per our enable flags. + if (provider->enable_flags() & ENABLE_STACK_TRACE_CAPTURE) + depth = CaptureStackBackTrace(2, kMaxBacktraceDepth, backtrace, NULL); + + EtwMofEvent<5> event(kLogEventId, LOG_MESSAGE_FULL, level); + if (file == NULL) + file = ""; + + // Add the stack trace. event.SetField(0, sizeof(depth), &depth); event.SetField(1, sizeof(backtrace[0]) * depth, &backtrace); - event.SetField(2, message.length() + 1, message.c_str()); + // The line. + event.SetField(2, sizeof(line), &line); + // The file. + event.SetField(3, strlen(file) + 1, file); + // And finally the message. + event.SetField(4, message.length() + 1 - message_start, + message.c_str() + message_start); provider->Log(event.get()); - } else { - EtwMofEvent<1> event(kLogEventId, LOG_MESSAGE, level); - event.SetField(0, message.length() + 1, message.c_str()); - provider->Log(event.get()); } // Don't increase verbosity in other log destinations. - if (severity >= provider->old_log_level_) + if (severity < provider->old_log_level_) return true; return false; @@ -95,21 +120,17 @@ void LogEventProvider::OnEventsEnabled() { // Convert the new trace level to a logging severity // and enable logging at that level. EtwEventLevel level = enable_level(); - switch (level) { - case TRACE_LEVEL_NONE: - case TRACE_LEVEL_FATAL: - SetMinLogLevel(LOG_FATAL); - break; - case TRACE_LEVEL_ERROR: - SetMinLogLevel(LOG_ERROR); - break; - case TRACE_LEVEL_WARNING: - SetMinLogLevel(LOG_WARNING); - break; - case TRACE_LEVEL_INFORMATION: - case TRACE_LEVEL_VERBOSE: - SetMinLogLevel(LOG_INFO); - break; + if (level == TRACE_LEVEL_NONE || level == TRACE_LEVEL_FATAL) { + SetMinLogLevel(LOG_FATAL); + } else if (level == TRACE_LEVEL_ERROR) { + SetMinLogLevel(LOG_ERROR); + } else if (level == TRACE_LEVEL_WARNING) { + SetMinLogLevel(LOG_WARNING); + } else if (level == TRACE_LEVEL_INFORMATION) { + SetMinLogLevel(LOG_INFO); + } else if (level >= TRACE_LEVEL_VERBOSE) { + // Above INFO, we enable verbose levels with negative severities. + SetMinLogLevel(TRACE_LEVEL_INFORMATION - level); } } diff --git a/base/logging_win.h b/base/logging_win.h index f2e6e5a..695c7f2 100644 --- a/base/logging_win.h +++ b/base/logging_win.h @@ -8,7 +8,7 @@ #include <string> #include "base/basictypes.h" -#include "base/event_trace_provider_win.h" +#include "base/win/event_trace_provider.h" #include "base/logging.h" namespace logging { @@ -21,6 +21,10 @@ enum LogEnableMask { // If this bit is set in our provider enable mask, we will include // a stack trace with every log message. ENABLE_STACK_TRACE_CAPTURE = 0x0001, + // If this bit is set in our provider enable mask, the provider will log + // a LOG message with only the textual content of the message, and no + // stack trace. + ENABLE_LOG_MESSAGE_ONLY = 0x0002, }; // The message types our log event provider generates. @@ -31,15 +35,23 @@ enum LogMessageTypes { // A message with a stack trace, followed by the zero-terminated // message text. LOG_MESSAGE_WITH_STACKTRACE = 11, + // A message with: + // a stack trace, + // the line number as a four byte integer, + // the file as a zero terminated UTF8 string, + // the zero-terminated UTF8 message text. + LOG_MESSAGE_FULL = 12, }; // Trace provider class to drive log control and transport // with Event Tracing for Windows. -class LogEventProvider : public EtwTraceProvider { +class LogEventProvider : public base::win::EtwTraceProvider { public: LogEventProvider(); - static bool LogMessage(int severity, const std::string& message); + static bool LogMessage(logging::LogSeverity severity, const char* file, + int line, size_t message_start, const std::string& str); + static void Initialize(const GUID& provider_name); static void Uninitialize(); diff --git a/base/mac_util.h b/base/mac_util.h index e590d30..182fcc8 100644 --- a/base/mac_util.h +++ b/base/mac_util.h @@ -24,8 +24,10 @@ class NSWindow; // Adapted from NSPathUtilities.h and NSObjCRuntime.h. #if __LP64__ || NS_BUILD_32_LIKE_64 typedef unsigned long NSSearchPathDirectory; +typedef unsigned long NSSearchPathDomainMask; #else typedef unsigned int NSSearchPathDirectory; +typedef unsigned int NSSearchPathDomainMask; #endif namespace mac_util { @@ -74,11 +76,23 @@ OSType CreatorCodeForCFBundleRef(CFBundleRef bundle); // app bundle's creator code anyway. OSType CreatorCodeForApplication(); +// Searches for directories for the given key in only the given |domain_mask|. +// If found, fills result (which must always be non-NULL) with the +// first found directory and returns true. Otherwise, returns false. +bool GetSearchPathDirectory(NSSearchPathDirectory directory, + NSSearchPathDomainMask domain_mask, + FilePath* result); + // Searches for directories for the given key in only the user domain. // If found, fills result (which must always be non-NULL) with the // first found directory and returns true. Otherwise, returns false. bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result); +// Searches for directories for the given key in only the local domain. +// If found, fills result (which must always be non-NULL) with the +// first found directory and returns true. Otherwise, returns false. +bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result); + // Returns the ~/Library directory. FilePath GetUserLibraryPath(); diff --git a/base/mac_util.mm b/base/mac_util.mm index f4e988a..4e6b105 100644 --- a/base/mac_util.mm +++ b/base/mac_util.mm @@ -219,10 +219,12 @@ OSType CreatorCodeForApplication() { return CreatorCodeForCFBundleRef(bundle); } -bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) { +bool GetSearchPathDirectory(NSSearchPathDirectory directory, + NSSearchPathDomainMask domain_mask, + FilePath* result) { DCHECK(result); NSArray* dirs = - NSSearchPathForDirectoriesInDomains(directory, NSUserDomainMask, YES); + NSSearchPathForDirectoriesInDomains(directory, domain_mask, YES); if ([dirs count] < 1) { return false; } @@ -231,6 +233,14 @@ bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) { return true; } +bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result) { + return GetSearchPathDirectory(directory, NSLocalDomainMask, result); +} + +bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) { + return GetSearchPathDirectory(directory, NSUserDomainMask, result); +} + FilePath GetUserLibraryPath() { FilePath user_library_path; if (!GetUserDirectory(NSLibraryDirectory, &user_library_path)) { diff --git a/base/message_loop.cc b/base/message_loop.cc index 2a92f4f..c4ff167 100644 --- a/base/message_loop.cc +++ b/base/message_loop.cc @@ -23,9 +23,13 @@ #if defined(OS_POSIX) && !defined(OS_MACOSX) #include "base/message_pump_glib.h" #endif +#if defined(TOUCH_UI) +#include "base/message_pump_glib_x.h" +#endif using base::Time; using base::TimeDelta; +using base::TimeTicks; namespace { @@ -134,8 +138,14 @@ MessageLoop::MessageLoop(Type type) #elif defined(OS_MACOSX) #define MESSAGE_PUMP_UI base::MessagePumpMac::Create() #define MESSAGE_PUMP_IO new base::MessagePumpLibevent() +<<<<<<< HEAD #elif defined(ANDROID) #define MESSAGE_PUMP_UI new base::MessagePumpDefault() +======= +#elif defined(TOUCH_UI) +// TODO(sadrul): enable the new message pump when ready +#define MESSAGE_PUMP_UI new base::MessagePumpForUI() +>>>>>>> chromium.org at r65505 #define MESSAGE_PUMP_IO new base::MessagePumpLibevent() #elif defined(OS_POSIX) // POSIX but not MACOSX. #define MESSAGE_PUMP_UI new base::MessagePumpForUI() @@ -330,7 +340,7 @@ void MessageLoop::PostTask_Helper( if (delay_ms > 0) { pending_task.delayed_run_time = - Time::Now() + TimeDelta::FromMilliseconds(delay_ms); + TimeTicks::Now() + TimeDelta::FromMilliseconds(delay_ms); #if defined(OS_WIN) if (high_resolution_timer_expiration_.is_null()) { @@ -343,7 +353,7 @@ void MessageLoop::PostTask_Helper( delay_ms < (2 * Time::kMinLowResolutionThresholdMs); if (needs_high_res_timers) { Time::ActivateHighResolutionTimer(true); - high_resolution_timer_expiration_ = base::TimeTicks::Now() + + high_resolution_timer_expiration_ = TimeTicks::Now() + TimeDelta::FromMilliseconds(kHighResolutionTimerModeLeaseTimeMs); } } @@ -354,9 +364,9 @@ void MessageLoop::PostTask_Helper( #if defined(OS_WIN) if (!high_resolution_timer_expiration_.is_null()) { - if (base::TimeTicks::Now() > high_resolution_timer_expiration_) { + if (TimeTicks::Now() > high_resolution_timer_expiration_) { Time::ActivateHighResolutionTimer(false); - high_resolution_timer_expiration_ = base::TimeTicks(); + high_resolution_timer_expiration_ = TimeTicks(); } } #endif @@ -411,9 +421,9 @@ void MessageLoop::RunTask(Task* task) { HistogramEvent(kTaskRunEvent); FOR_EACH_OBSERVER(TaskObserver, task_observers_, - WillProcessTask(task->tracked_birth_time())); + WillProcessTask(task)); task->Run(); - FOR_EACH_OBSERVER(TaskObserver, task_observers_, DidProcessTask()); + FOR_EACH_OBSERVER(TaskObserver, task_observers_, DidProcessTask(task)); delete task; nestable_tasks_allowed_ = true; @@ -475,7 +485,7 @@ bool MessageLoop::DeletePendingTasks() { // TODO(darin): Delete all tasks once it is safe to do so. // Until it is totally safe, just do it when running Purify or // Valgrind. -#if defined(PURIFY) +#if defined(PURIFY) || defined(USE_HEAPCHECKER) delete pending_task.task; #elif defined(OS_POSIX) if (RUNNING_ON_VALGRIND) @@ -488,7 +498,7 @@ bool MessageLoop::DeletePendingTasks() { // TODO(darin): Delete all tasks once it is safe to do so. // Until it is totaly safe, only delete them under Purify and Valgrind. Task* task = NULL; -#if defined(PURIFY) +#if defined(PURIFY) || defined(USE_HEAPCHECKER) task = deferred_non_nestable_work_queue_.front().task; #elif defined(OS_POSIX) if (RUNNING_ON_VALGRIND) @@ -538,15 +548,26 @@ bool MessageLoop::DoWork() { return false; } -bool MessageLoop::DoDelayedWork(Time* next_delayed_work_time) { +bool MessageLoop::DoDelayedWork(base::TimeTicks* next_delayed_work_time) { if (!nestable_tasks_allowed_ || delayed_work_queue_.empty()) { - *next_delayed_work_time = Time(); + recent_time_ = *next_delayed_work_time = TimeTicks(); return false; } - if (delayed_work_queue_.top().delayed_run_time > Time::Now()) { - *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time; - return false; + // When we "fall behind," there will be a lot of tasks in the delayed work + // queue that are ready to run. To increase efficiency when we fall behind, + // we will only call Time::Now() intermittently, and then process all tasks + // that are ready to run before calling it again. As a result, the more we + // fall behind (and have a lot of ready-to-run delayed tasks), the more + // efficient we'll be at handling the tasks. + + TimeTicks next_run_time = delayed_work_queue_.top().delayed_run_time; + if (next_run_time > recent_time_) { + recent_time_ = TimeTicks::Now(); // Get a better view of Now(); + if (next_run_time > recent_time_) { + *next_delayed_work_time = next_run_time; + return false; + } } PendingTask pending_task = delayed_work_queue_.top(); diff --git a/base/message_loop.h b/base/message_loop.h index 94e0096..a5a94bc 100644 --- a/base/message_loop.h +++ b/base/message_loop.h @@ -26,6 +26,9 @@ #include "base/message_pump_glib.h" #endif #endif +#if defined(TOUCH_UI) +#include "base/message_pump_glib_x_dispatch.h" +#endif namespace base { class Histogram; @@ -72,10 +75,10 @@ class MessageLoop : public base::MessagePump::Delegate { TaskObserver(); // This method is called before processing a task. - virtual void WillProcessTask(base::TimeTicks birth_time) = 0; + virtual void WillProcessTask(const Task* task) = 0; // This method is called after processing a task. - virtual void DidProcessTask() = 0; + virtual void DidProcessTask(const Task* task) = 0; protected: virtual ~TaskObserver(); @@ -146,7 +149,7 @@ class MessageLoop : public base::MessagePump::Delegate { // as the thread that calls PostDelayedTask(FROM_HERE, ), then T MUST inherit // from RefCountedThreadSafe<T>! template <class T> - void DeleteSoon(const tracked_objects::Location& from_here, T* object) { + void DeleteSoon(const tracked_objects::Location& from_here, const T* object) { PostNonNestableTask(from_here, new DeleteTask<T>(object)); } @@ -161,7 +164,8 @@ class MessageLoop : public base::MessagePump::Delegate { // PostDelayedTask(FROM_HERE, ), then T MUST inherit from // RefCountedThreadSafe<T>! template <class T> - void ReleaseSoon(const tracked_objects::Location& from_here, T* object) { + void ReleaseSoon(const tracked_objects::Location& from_here, + const T* object) { PostNonNestableTask(from_here, new ReleaseTask<T>(object)); } @@ -289,7 +293,11 @@ class MessageLoop : public base::MessagePump::Delegate { typedef base::MessagePumpWin::Dispatcher Dispatcher; typedef base::MessagePumpForUI::Observer Observer; #elif !defined(OS_MACOSX) +#if defined(TOUCH_UI) + typedef base::MessagePumpGlibXDispatcher Dispatcher; +#else typedef base::MessagePumpForUI::Dispatcher Dispatcher; +#endif typedef base::MessagePumpForUI::Observer Observer; #endif @@ -333,10 +341,10 @@ class MessageLoop : public base::MessagePump::Delegate { // This structure is copied around by value. struct PendingTask { - Task* task; // The task to run. - base::Time delayed_run_time; // The time when the task should be run. - int sequence_num; // Used to facilitate sorting by run time. - bool nestable; // True if OK to dispatch from a nested loop. + Task* task; // The task to run. + base::TimeTicks delayed_run_time; // The time when the task should be run. + int sequence_num; // Secondary sort key for run time. + bool nestable; // OK to dispatch from a nested loop. PendingTask(Task* task, bool nestable) : task(task), sequence_num(0), nestable(nestable) { @@ -421,7 +429,7 @@ class MessageLoop : public base::MessagePump::Delegate { // base::MessagePump::Delegate methods: virtual bool DoWork(); - virtual bool DoDelayedWork(base::Time* next_delayed_work_time); + virtual bool DoDelayedWork(base::TimeTicks* next_delayed_work_time); virtual bool DoIdleWork(); // Start recording histogram info about events and action IF it was enabled @@ -442,6 +450,9 @@ class MessageLoop : public base::MessagePump::Delegate { // Contains delayed tasks, sorted by their 'delayed_run_time' property. DelayedTaskQueue delayed_work_queue_; + // A recent snapshot of Time::Now(), used to check delayed_work_queue_. + base::TimeTicks recent_time_; + // A queue of non-nestable tasks that we had to defer because when it came // time to execute them we were in a nested message loop. They will execute // once we're out of nested message loops. diff --git a/base/message_loop_proxy.cc b/base/message_loop_proxy.cc index bc7088d..a38db39 100644 --- a/base/message_loop_proxy.cc +++ b/base/message_loop_proxy.cc @@ -12,7 +12,7 @@ MessageLoopProxy::MessageLoopProxy() { MessageLoopProxy::~MessageLoopProxy() { } -void MessageLoopProxy::OnDestruct() { +void MessageLoopProxy::OnDestruct() const { delete this; } diff --git a/base/message_loop_proxy.h b/base/message_loop_proxy.h index 5d6708e..4d38155 100644 --- a/base/message_loop_proxy.h +++ b/base/message_loop_proxy.h @@ -64,11 +64,11 @@ class MessageLoopProxy // Called when the proxy is about to be deleted. Subclasses can override this // to provide deletion on specific threads. - virtual void OnDestruct(); + virtual void OnDestruct() const; }; struct MessageLoopProxyTraits { - static void Destruct(MessageLoopProxy* proxy) { + static void Destruct(const MessageLoopProxy* proxy) { proxy->OnDestruct(); } }; diff --git a/base/message_loop_proxy_impl.cc b/base/message_loop_proxy_impl.cc index 983a406..2e0f809 100644 --- a/base/message_loop_proxy_impl.cc +++ b/base/message_loop_proxy_impl.cc @@ -71,7 +71,7 @@ bool MessageLoopProxyImpl::PostTaskHelper( return ret; } -void MessageLoopProxyImpl::OnDestruct() { +void MessageLoopProxyImpl::OnDestruct() const { bool delete_later = false; { AutoLock lock(message_loop_lock_); @@ -93,7 +93,7 @@ void MessageLoopProxyImpl::WillDestroyCurrentMessageLoop() { scoped_refptr<MessageLoopProxy> MessageLoopProxy::CreateForCurrentThread() { - scoped_refptr<MessageLoopProxy> ret = new MessageLoopProxyImpl(); + scoped_refptr<MessageLoopProxy> ret(new MessageLoopProxyImpl()); return ret; } diff --git a/base/message_loop_proxy_impl.h b/base/message_loop_proxy_impl.h index b93bb64..87ae70a 100644 --- a/base/message_loop_proxy_impl.h +++ b/base/message_loop_proxy_impl.h @@ -39,7 +39,7 @@ class MessageLoopProxyImpl : public MessageLoopProxy, protected: // Override OnDestruct so that we can delete the object on the target message // loop if it still exists. - virtual void OnDestruct(); + virtual void OnDestruct() const; private: MessageLoopProxyImpl(); @@ -50,7 +50,7 @@ class MessageLoopProxyImpl : public MessageLoopProxy, friend class MessageLoopProxy; // The lock that protects access to target_message_loop_. - Lock message_loop_lock_; + mutable Lock message_loop_lock_; MessageLoop* target_message_loop_; DISALLOW_COPY_AND_ASSIGN(MessageLoopProxyImpl); diff --git a/base/message_loop_proxy_impl_unittest.cc b/base/message_loop_proxy_impl_unittest.cc index a3cb800..8d8ef4e 100644 --- a/base/message_loop_proxy_impl_unittest.cc +++ b/base/message_loop_proxy_impl_unittest.cc @@ -12,20 +12,20 @@ class MessageLoopProxyImplTest : public testing::Test { public: - void Release() { + void Release() const { AssertOnIOThread(); Quit(); } - void Quit() { + void Quit() const { loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask); } - void AssertOnIOThread() { + void AssertOnIOThread() const { ASSERT_TRUE(io_thread_->message_loop_proxy()->BelongsToCurrentThread()); } - void AssertOnFileThread() { + void AssertOnFileThread() const { ASSERT_TRUE(file_thread_->message_loop_proxy()->BelongsToCurrentThread()); } @@ -79,7 +79,7 @@ class MessageLoopProxyImplTest : public testing::Test { scoped_ptr<base::Thread> file_thread_; private: - MessageLoop loop_; + mutable MessageLoop loop_; }; diff --git a/base/message_loop_unittest.cc b/base/message_loop_unittest.cc index 741eb71..a3c3307 100644 --- a/base/message_loop_unittest.cc +++ b/base/message_loop_unittest.cc @@ -95,7 +95,7 @@ void RunTest_PostTask(MessageLoop::Type message_loop_type) { MessageLoop loop(message_loop_type); // Add tests to message loop - scoped_refptr<Foo> foo = new Foo(); + scoped_refptr<Foo> foo(new Foo()); std::string a("a"), b("b"), c("c"), d("d"); MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( foo.get(), &Foo::Test0)); @@ -111,7 +111,7 @@ void RunTest_PostTask(MessageLoop::Type message_loop_type) { foo.get(), &Foo::Test2Mixed, a, &d)); // After all tests, post a message that will shut down the message loop - scoped_refptr<QuitMsgLoop> quit = new QuitMsgLoop(); + scoped_refptr<QuitMsgLoop> quit(new QuitMsgLoop()); MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( quit.get(), &QuitMsgLoop::QuitNow)); @@ -126,7 +126,7 @@ void RunTest_PostTask_SEH(MessageLoop::Type message_loop_type) { MessageLoop loop(message_loop_type); // Add tests to message loop - scoped_refptr<Foo> foo = new Foo(); + scoped_refptr<Foo> foo(new Foo()); std::string a("a"), b("b"), c("c"), d("d"); MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( foo.get(), &Foo::Test0)); @@ -142,7 +142,7 @@ void RunTest_PostTask_SEH(MessageLoop::Type message_loop_type) { foo.get(), &Foo::Test2Mixed, a, &d)); // After all tests, post a message that will shut down the message loop - scoped_refptr<QuitMsgLoop> quit = new QuitMsgLoop(); + scoped_refptr<QuitMsgLoop> quit(new QuitMsgLoop()); MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( quit.get(), &QuitMsgLoop::QuitNow)); @@ -1488,14 +1488,16 @@ class DummyTaskObserver : public MessageLoop::TaskObserver { virtual ~DummyTaskObserver() {} - virtual void WillProcessTask(base::TimeTicks /* birth_time */) { + virtual void WillProcessTask(const Task* task) { num_tasks_started_++; + EXPECT_TRUE(task != NULL); EXPECT_LE(num_tasks_started_, num_tasks_); EXPECT_EQ(num_tasks_started_, num_tasks_processed_ + 1); } - virtual void DidProcessTask() { + virtual void DidProcessTask(const Task* task) { num_tasks_processed_++; + EXPECT_TRUE(task != NULL); EXPECT_LE(num_tasks_started_, num_tasks_); EXPECT_EQ(num_tasks_started_, num_tasks_processed_); } diff --git a/base/message_pump.h b/base/message_pump.h index f8a097d..a354724 100644 --- a/base/message_pump.h +++ b/base/message_pump.h @@ -10,7 +10,7 @@ namespace base { -class Time; +class TimeTicks; class MessagePump : public RefCountedThreadSafe<MessagePump> { public: @@ -33,7 +33,7 @@ class MessagePump : public RefCountedThreadSafe<MessagePump> { // |next_delayed_work_time| is null (per Time::is_null), then the queue of // future delayed work (timer events) is currently empty, and no additional // calls to this function need to be scheduled. - virtual bool DoDelayedWork(Time* next_delayed_work_time) = 0; + virtual bool DoDelayedWork(TimeTicks* next_delayed_work_time) = 0; // Called from within Run just before the message pump goes to sleep. // Returns true to indicate that idle work was done. @@ -116,7 +116,7 @@ class MessagePump : public RefCountedThreadSafe<MessagePump> { // Schedule a DoDelayedWork callback to happen at the specified time, // cancelling any pending DoDelayedWork callback. This method may only be // used on the thread that called Run. - virtual void ScheduleDelayedWork(const Time& delayed_work_time) = 0; + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) = 0; }; } // namespace base diff --git a/base/message_pump_default.cc b/base/message_pump_default.cc index 518684a..d9eddc4 100644 --- a/base/message_pump_default.cc +++ b/base/message_pump_default.cc @@ -41,13 +41,13 @@ void MessagePumpDefault::Run(Delegate* delegate) { if (delayed_work_time_.is_null()) { event_.Wait(); } else { - TimeDelta delay = delayed_work_time_ - Time::Now(); + TimeDelta delay = delayed_work_time_ - TimeTicks::Now(); if (delay > TimeDelta()) { event_.TimedWait(delay); } else { // It looks like delayed_work_time_ indicates a time in the past, so we // need to call DoDelayedWork now. - delayed_work_time_ = Time(); + delayed_work_time_ = TimeTicks(); } } // Since event_ is auto-reset, we don't need to do anything special here @@ -67,7 +67,8 @@ void MessagePumpDefault::ScheduleWork() { event_.Signal(); } -void MessagePumpDefault::ScheduleDelayedWork(const Time& delayed_work_time) { +void MessagePumpDefault::ScheduleDelayedWork( + const TimeTicks& delayed_work_time) { // We know that we can't be blocked on Wait right now since this method can // only be called on the same thread as Run, so we only need to update our // record of how long to sleep when we do sleep. diff --git a/base/message_pump_default.h b/base/message_pump_default.h index 0ac6cd4..3dfbf1c 100644 --- a/base/message_pump_default.h +++ b/base/message_pump_default.h @@ -21,7 +21,7 @@ class MessagePumpDefault : public MessagePump { virtual void Run(Delegate* delegate); virtual void Quit(); virtual void ScheduleWork(); - virtual void ScheduleDelayedWork(const Time& delayed_work_time); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); private: // This flag is set to false when Run should return. @@ -31,7 +31,7 @@ class MessagePumpDefault : public MessagePump { WaitableEvent event_; // The time at which we should call DoDelayedWork. - Time delayed_work_time_; + TimeTicks delayed_work_time_; DISALLOW_COPY_AND_ASSIGN(MessagePumpDefault); }; diff --git a/base/message_pump_glib.cc b/base/message_pump_glib.cc index e85a712..fa5b726 100644 --- a/base/message_pump_glib.cc +++ b/base/message_pump_glib.cc @@ -21,7 +21,7 @@ const char kWorkScheduled = '\0'; // Return a timeout suitable for the glib loop, -1 to block forever, // 0 to return right away, or a timeout in milliseconds from now. -int GetTimeIntervalMilliseconds(base::Time from) { +int GetTimeIntervalMilliseconds(const base::TimeTicks& from) { if (from.is_null()) return -1; @@ -29,7 +29,7 @@ int GetTimeIntervalMilliseconds(base::Time from) { // value in milliseconds. If there are 5.5ms left, should the delay be 5 or // 6? It should be 6 to avoid executing delayed work too early. int delay = static_cast<int>( - ceil((from - base::Time::Now()).InMillisecondsF())); + ceil((from - base::TimeTicks::Now()).InMillisecondsF())); // If this value is negative, then we need to run delayed work soon. return delay < 0 ? 0 : delay; @@ -207,8 +207,7 @@ void MessagePumpForUI::RunWithDispatcher(Delegate* delegate, // Don't block if we think we have more work to do. bool block = !more_work_is_plausible; - // g_main_context_iteration returns true if events have been dispatched. - more_work_is_plausible = g_main_context_iteration(context_, block); + more_work_is_plausible = RunOnce(context_, block); if (state_->should_quit) break; @@ -232,6 +231,11 @@ void MessagePumpForUI::RunWithDispatcher(Delegate* delegate, state_ = previous_state; } +bool MessagePumpForUI::RunOnce(GMainContext* context, bool block) { + // g_main_context_iteration returns true if events have been dispatched. + return g_main_context_iteration(context, block); +} + // Return the timeout we want passed to poll. int MessagePumpForUI::HandlePrepare() { // We know we have work, but we haven't called HandleDispatch yet. Don't let @@ -299,6 +303,10 @@ void MessagePumpForUI::RemoveObserver(Observer* observer) { observers_.RemoveObserver(observer); } +MessagePumpForUI::Dispatcher* MessagePumpForUI::GetDispatcher() { + return state_ ? state_->dispatcher : NULL; +} + void MessagePumpForUI::WillProcessEvent(GdkEvent* event) { FOR_EACH_OBSERVER(Observer, observers_, WillProcessEvent(event)); } @@ -325,26 +333,28 @@ void MessagePumpForUI::ScheduleWork() { } } -void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) { +void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { // We need to wake up the loop in case the poll timeout needs to be // adjusted. This will cause us to try to do work, but that's ok. delayed_work_time_ = delayed_work_time; ScheduleWork(); } -// static -void MessagePumpForUI::EventDispatcher(GdkEvent* event, gpointer data) { - MessagePumpForUI* message_pump = reinterpret_cast<MessagePumpForUI*>(data); - - message_pump->WillProcessEvent(event); - if (message_pump->state_ && // state_ may be null during tests. - message_pump->state_->dispatcher) { - if (!message_pump->state_->dispatcher->Dispatch(event)) - message_pump->state_->should_quit = true; +void MessagePumpForUI::DispatchEvents(GdkEvent* event) { + WillProcessEvent(event); + if (state_ && state_->dispatcher) { // state_ may be null during tests. + if (!state_->dispatcher->Dispatch(event)) + state_->should_quit = true; } else { gtk_main_do_event(event); } - message_pump->DidProcessEvent(event); + DidProcessEvent(event); +} + +// static +void MessagePumpForUI::EventDispatcher(GdkEvent* event, gpointer data) { + MessagePumpForUI* message_pump = reinterpret_cast<MessagePumpForUI*>(data); + message_pump->DispatchEvents(event); } } // namespace base diff --git a/base/message_pump_glib.h b/base/message_pump_glib.h index f6d022a..06635de 100644 --- a/base/message_pump_glib.h +++ b/base/message_pump_glib.h @@ -57,10 +57,15 @@ class MessagePumpForUI : public MessagePump { // Like MessagePump::Run, but GdkEvent objects are routed through dispatcher. virtual void RunWithDispatcher(Delegate* delegate, Dispatcher* dispatcher); + // Run a single iteration of the mainloop. A return value of true indicates + // that an event was handled. |block| indicates if it should wait if no event + // is ready for processing. + virtual bool RunOnce(GMainContext* context, bool block); + virtual void Run(Delegate* delegate) { RunWithDispatcher(delegate, NULL); } virtual void Quit(); virtual void ScheduleWork(); - virtual void ScheduleDelayedWork(const Time& delayed_work_time); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); // Internal methods used for processing the pump callbacks. They are // public for simplicity but should not be used directly. HandlePrepare @@ -79,6 +84,14 @@ class MessagePumpForUI : public MessagePump { // receiving a notification callback. void RemoveObserver(Observer* observer); + // Dispatch an available GdkEvent. Essentially this allows a subclass to do + // some task before/after calling the default handler (EventDispatcher). + virtual void DispatchEvents(GdkEvent* event); + + protected: + // Returns the dispatcher for the current run state (|state_->dispatcher|). + Dispatcher* GetDispatcher(); + private: // We may make recursive calls to Run, so we save state that needs to be // separate between them in this structure type. @@ -103,7 +116,7 @@ class MessagePumpForUI : public MessagePump { GMainContext* context_; // This is the time when we need to do delayed work. - Time delayed_work_time_; + TimeTicks delayed_work_time_; // The work source. It is shared by all calls to Run and destroyed when // the message pump is destroyed. diff --git a/base/message_pump_glib_x.cc b/base/message_pump_glib_x.cc new file mode 100644 index 0000000..675774e --- /dev/null +++ b/base/message_pump_glib_x.cc @@ -0,0 +1,117 @@ +// Copyright (c) 2010 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/message_pump_glib_x.h" + +#include <gdk/gdkx.h> +#include <X11/Xlib.h> + +#include "base/message_pump_glib_x_dispatch.h" + +namespace { + +gboolean PlaceholderDispatch(GSource* source, + GSourceFunc cb, + gpointer data) { + return TRUE; +} + +} // namespace + +namespace base { + +MessagePumpGlibX::MessagePumpGlibX() : base::MessagePumpForUI(), + gdksource_(NULL), + dispatching_event_(false), + capture_x_events_(0), + capture_gdk_events_(0) { + gdk_event_handler_set(&EventDispatcherX, this, NULL); + + InitializeEventsToCapture(); +} + +MessagePumpGlibX::~MessagePumpGlibX() { +} + +bool MessagePumpGlibX::RunOnce(GMainContext* context, bool block) { + GdkDisplay* gdisp = gdk_display_get_default(); + Display* display = GDK_DISPLAY_XDISPLAY(gdisp); + if (XPending(display)) { + XEvent xev; + XPeekEvent(display, &xev); + if (capture_x_events_[xev.type]) { + XNextEvent(display, &xev); + + bool processed = static_cast<MessagePumpGlibXDispatcher*> + (GetDispatcher())->Dispatch(&xev); + + if (!processed) { + DLOG(WARNING) << "Event (" << xev.type << ") not handled."; + } + } else { + // TODO(sad): A couple of extra events can still sneak in during this. + // Those should be sent back to the X queue from the dispatcher + // EventDispatcherX. + g_main_context_iteration(context, FALSE); + } + } + + bool retvalue; + if (gdksource_) { + // Replace the dispatch callback of the GDK event source temporarily so that + // it doesn't read events from X. + gboolean (*cb)(GSource*, GSourceFunc, void*) = + gdksource_->source_funcs->dispatch; + gdksource_->source_funcs->dispatch = PlaceholderDispatch; + + dispatching_event_ = true; + retvalue = g_main_context_iteration(context, block); + dispatching_event_ = false; + + gdksource_->source_funcs->dispatch = cb; + } else { + retvalue = g_main_context_iteration(context, block); + } + + return retvalue; +} + +void MessagePumpGlibX::InitializeEventsToCapture(void) { + // TODO(sad): Decide which events we want to capture and update the tables + // accordingly. + capture_x_events_[KeyPress] = true; + capture_gdk_events_[GDK_KEY_PRESS] = true; + + capture_x_events_[KeyRelease] = true; + capture_gdk_events_[GDK_KEY_RELEASE] = true; + + capture_x_events_[ButtonPress] = true; + capture_gdk_events_[GDK_BUTTON_PRESS] = true; + + capture_x_events_[ButtonRelease] = true; + capture_gdk_events_[GDK_BUTTON_RELEASE] = true; + + capture_x_events_[MotionNotify] = true; + capture_gdk_events_[GDK_MOTION_NOTIFY] = true; +} + +void MessagePumpGlibX::EventDispatcherX(GdkEvent* event, gpointer data) { + MessagePumpGlibX* pump_x = reinterpret_cast<MessagePumpGlibX*>(data); + + if (!pump_x->gdksource_) { + pump_x->gdksource_ = g_main_current_source(); + } else if (!pump_x->IsDispatchingEvent()) { + if (event->type != GDK_NOTHING && + pump_x->capture_gdk_events_[event->type]) { + // TODO(sad): An X event is caught by the GDK handler. Put it back in the + // X queue so that we catch it in the next iteration. When done, the + // following DLOG statement will be removed. + DLOG(WARNING) << "GDK received an event it shouldn't have"; + } + } + + pump_x->DispatchEvents(event); +} + +} // namespace base diff --git a/base/message_pump_glib_x.h b/base/message_pump_glib_x.h new file mode 100644 index 0000000..2f50731 --- /dev/null +++ b/base/message_pump_glib_x.h @@ -0,0 +1,63 @@ +// Copyright (c) 2010 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_MESSAGE_PUMP_GLIB_X_H +#define BASE_MESSAGE_PUMP_GLIB_X_H + +#include "base/message_pump.h" +#include "base/message_pump_glib.h" + +#include <bitset> + +#include <glib.h> +#include <gtk/gtk.h> +#include <X11/X.h> + +namespace base { + +class MessagePumpGlibX : public MessagePumpForUI { + public: + MessagePumpGlibX(); + virtual ~MessagePumpGlibX(); + + // MessagePumpForUI implementation. + virtual bool RunOnce(GMainContext* context, bool block); + + // Indicates whether a GDK event was injected by chrome (when |true|) or if it + // was captured and being processed by GDK (when |false|). + bool IsDispatchingEvent(void) { return dispatching_event_; } + + private: + static void EventDispatcherX(GdkEvent* event, gpointer data); + + // Update the lookup table and flag the events that should be captured and + // processed so that GDK doesn't get to them. + void InitializeEventsToCapture(void); + + // The event source for GDK events. + GSource* gdksource_; + + // Indicates whether a GDK event was injected by chrome (when |true|) or if it + // was captured and being processed by GDK (when |false|). + bool dispatching_event_; + +#if ! GTK_CHECK_VERSION(2,18,0) +// GDK_EVENT_LAST was introduced in GTK+ 2.18.0. For earlier versions, we pick a +// large enough value (the value of GDK_EVENT_LAST in 2.18.0) so that it works +// for all versions. +#define GDK_EVENT_LAST 37 +#endif + + // We do not want to process all the events ourselves. So we use a lookup + // table to quickly check if a particular event should be handled by us or if + // it should be passed on to the default GDK handler. + std::bitset<LASTEvent> capture_x_events_; + std::bitset<GDK_EVENT_LAST> capture_gdk_events_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpGlibX); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_GLIB_X_H diff --git a/base/message_pump_glib_x_dispatch.h b/base/message_pump_glib_x_dispatch.h new file mode 100644 index 0000000..95364a2 --- /dev/null +++ b/base/message_pump_glib_x_dispatch.h @@ -0,0 +1,28 @@ +// Copyright (c) 2010 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_MESSAGE_PUMP_GLIB_X_DISPATCH_H +#define BASE_MESSAGE_PUMP_GLIB_X_DISPATCH_H + +#include "base/message_pump.h" +#include "base/message_pump_glib.h" + +typedef union _XEvent XEvent; + +namespace base { + +// The message pump used for TOUCH_UI on linux is MessagePumpGlibX, which can +// dispatch both GdkEvents* and XEvents* captured directly from X. +// MessagePumpForUI::Dispatcher provides the mechanism for dispatching +// GdkEvents. This class provides additional mechanism for dispatching XEvents. +class MessagePumpGlibXDispatcher : public MessagePumpForUI::Dispatcher { + public: + // Dispatches the event. If true is returned processing continues as + // normal. If false is returned, the nested loop exits immediately. + virtual bool Dispatch(XEvent* xevent) = 0; +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_GLIB_X_DISPATCH_H diff --git a/base/message_pump_libevent.cc b/base/message_pump_libevent.cc index d325957..1410f79 100644 --- a/base/message_pump_libevent.cc +++ b/base/message_pump_libevent.cc @@ -290,7 +290,7 @@ void MessagePumpLibevent::Run(Delegate* delegate) { if (delayed_work_time_.is_null()) { event_base_loop(event_base_, EVLOOP_ONCE); } else { - TimeDelta delay = delayed_work_time_ - Time::Now(); + TimeDelta delay = delayed_work_time_ - TimeTicks::Now(); if (delay > TimeDelta()) { struct timeval poll_tv; poll_tv.tv_sec = delay.InSeconds(); @@ -303,7 +303,7 @@ void MessagePumpLibevent::Run(Delegate* delegate) { } else { // It looks like delayed_work_time_ indicates a time in the past, so we // need to call DoDelayedWork now. - delayed_work_time_ = Time(); + delayed_work_time_ = TimeTicks(); } } } @@ -326,7 +326,8 @@ void MessagePumpLibevent::ScheduleWork() { << "[nwrite:" << nwrite << "] [errno:" << errno << "]"; } -void MessagePumpLibevent::ScheduleDelayedWork(const Time& delayed_work_time) { +void MessagePumpLibevent::ScheduleDelayedWork( + const TimeTicks& delayed_work_time) { // We know that we can't be blocked on Wait right now since this method can // only be called on the same thread as Run, so we only need to update our // record of how long to sleep when we do sleep. diff --git a/base/message_pump_libevent.h b/base/message_pump_libevent.h index f271612..d8d000d 100644 --- a/base/message_pump_libevent.h +++ b/base/message_pump_libevent.h @@ -119,7 +119,7 @@ class MessagePumpLibevent : public MessagePump { virtual void Run(Delegate* delegate); virtual void Quit(); virtual void ScheduleWork(); - virtual void ScheduleDelayedWork(const Time& delayed_work_time); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); private: void WillProcessIOEvent(); @@ -135,7 +135,7 @@ class MessagePumpLibevent : public MessagePump { bool in_run_; // The time at which we should call DoDelayedWork. - Time delayed_work_time_; + TimeTicks delayed_work_time_; // Libevent dispatcher. Watches all sockets registered with it, and sends // readiness callbacks when a socket is ready for I/O. diff --git a/base/message_pump_mac.h b/base/message_pump_mac.h index 59a7329..e016d54 100644 --- a/base/message_pump_mac.h +++ b/base/message_pump_mac.h @@ -44,7 +44,7 @@ class NSAutoreleasePool; namespace base { -class Time; +class TimeTicks; class MessagePumpCFRunLoopBase : public MessagePump { // Needs access to CreateAutoreleasePool. @@ -61,7 +61,7 @@ class MessagePumpCFRunLoopBase : public MessagePump { virtual void DoRun(Delegate* delegate) = 0; virtual void ScheduleWork(); - virtual void ScheduleDelayedWork(const Time& delayed_work_time); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); protected: // Accessors for private data members to be used by subclasses. diff --git a/base/message_pump_mac.mm b/base/message_pump_mac.mm index 16a9a59..9091006 100644 --- a/base/message_pump_mac.mm +++ b/base/message_pump_mac.mm @@ -225,11 +225,17 @@ void MessagePumpCFRunLoopBase::ScheduleWork() { // Must be called on the run loop thread. void MessagePumpCFRunLoopBase::ScheduleDelayedWork( - const Time& delayed_work_time) { + const TimeTicks& delayed_work_time) { + // TODO(jar): We may need a more efficient way to go between these times, but + // the difference will change not only when we sleep/wake, it will also change + // when the user changes the wall clock time :-/. + Time absolute_work_time = + (delayed_work_time - TimeTicks::Now()) + Time::Now(); + Time::Exploded exploded; - delayed_work_time.UTCExplode(&exploded); + absolute_work_time.UTCExplode(&exploded); double seconds = exploded.second + - (static_cast<double>((delayed_work_time.ToInternalValue()) % + (static_cast<double>((absolute_work_time.ToInternalValue()) % Time::kMicrosecondsPerSecond) / Time::kMicrosecondsPerSecond); CFGregorianDate gregorian = { @@ -320,12 +326,12 @@ bool MessagePumpCFRunLoopBase::RunDelayedWork() { // released promptly even in the absence of UI events. MessagePumpScopedAutoreleasePool autorelease_pool(this); - Time next_time; + TimeTicks next_time; delegate_->DoDelayedWork(&next_time); bool more_work = !next_time.is_null(); if (more_work) { - TimeDelta delay = next_time - Time::Now(); + TimeDelta delay = next_time - TimeTicks::Now(); if (delay > TimeDelta()) { // There's more delayed work to be done in the future. ScheduleDelayedWork(next_time); diff --git a/base/message_pump_win.cc b/base/message_pump_win.cc index d0afd51..0df888a 100644 --- a/base/message_pump_win.cc +++ b/base/message_pump_win.cc @@ -66,7 +66,8 @@ int MessagePumpWin::GetCurrentDelay() const { // Be careful here. TimeDelta has a precision of microseconds, but we want a // value in milliseconds. If there are 5.5ms left, should the delay be 5 or // 6? It should be 6 to avoid executing delayed work too early. - double timeout = ceil((delayed_work_time_ - Time::Now()).InMillisecondsF()); + double timeout = + ceil((delayed_work_time_ - TimeTicks::Now()).InMillisecondsF()); // If this value is negative, then we need to run delayed work soon. int delay = static_cast<int>(timeout); @@ -96,7 +97,7 @@ void MessagePumpForUI::ScheduleWork() { PostMessage(message_hwnd_, kMsgHaveWork, reinterpret_cast<WPARAM>(this), 0); } -void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) { +void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { // // We would *like* to provide high resolution timers. Windows timers using // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup @@ -409,7 +410,7 @@ void MessagePumpForIO::ScheduleWork() { DCHECK(ret); } -void MessagePumpForIO::ScheduleDelayedWork(const Time& delayed_work_time) { +void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { // We know that we can't be blocked right now since this method can only be // called on the same thread as Run, so we only need to update our record of // how long to sleep when we do sleep. diff --git a/base/message_pump_win.h b/base/message_pump_win.h index d7d53cb..d57fe1d 100644 --- a/base/message_pump_win.h +++ b/base/message_pump_win.h @@ -98,7 +98,7 @@ class MessagePumpWin : public MessagePump { ObserverList<Observer> observers_; // The time at which delayed work should run. - Time delayed_work_time_; + TimeTicks delayed_work_time_; // A boolean value used to indicate if there is a kMsgDoWork message pending // in the Windows Message queue. There is at most one such message, and it @@ -167,7 +167,7 @@ class MessagePumpForUI : public MessagePumpWin { // MessagePump methods: virtual void ScheduleWork(); - virtual void ScheduleDelayedWork(const Time& delayed_work_time); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); // Applications can call this to encourage us to process all pending WM_PAINT // messages. This method will process all paint messages the Windows Message @@ -319,7 +319,7 @@ class MessagePumpForIO : public MessagePumpWin { // MessagePump methods: virtual void ScheduleWork(); - virtual void ScheduleDelayedWork(const Time& delayed_work_time); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); // Register the handler to be used when asynchronous IO for the given file // completes. The registration persists as long as |file_handle| is valid, so diff --git a/base/metrics/field_trial_unittest.cc b/base/metrics/field_trial_unittest.cc index aea8e85..a8138bb 100644 --- a/base/metrics/field_trial_unittest.cc +++ b/base/metrics/field_trial_unittest.cc @@ -100,7 +100,7 @@ TEST_F(FieldTrialTest, FiftyFiftyProbability) { int counter = 0; do { std::string name = base::StringPrintf("FiftyFifty%d", ++counter); - scoped_refptr<FieldTrial> trial = new FieldTrial(name, 2); + scoped_refptr<FieldTrial> trial(new FieldTrial(name, 2)); trial->AppendGroup("first", 1); // 50% chance of being chosen. if (trial->group() != FieldTrial::kNotParticipating) { first_winner = true; diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc index e07b3a8..2003f25 100644 --- a/base/metrics/histogram.cc +++ b/base/metrics/histogram.cc @@ -61,6 +61,7 @@ Histogram::Histogram(const std::string& name, Sample minimum, bucket_count_(bucket_count), flags_(kNoFlags), ranges_(bucket_count + 1, 0), + range_checksum_(0), sample_() { Initialize(); } @@ -73,6 +74,7 @@ Histogram::Histogram(const std::string& name, TimeDelta minimum, bucket_count_(bucket_count), flags_(kNoFlags), ranges_(bucket_count + 1, 0), + range_checksum_(0), sample_() { Initialize(); } @@ -86,6 +88,7 @@ Histogram::~Histogram() { // Just to make sure most derived class did this properly... DCHECK(ValidateBucketRanges()); + DCHECK(HasValidRangeChecksum()); } bool Histogram::PrintEmptyBucket(size_t index) const { @@ -218,7 +221,7 @@ void Histogram::Initialize() { // We have to be careful that we don't pick a ratio between starting points in // consecutive buckets that is sooo small, that the integer bounds are the same // (effectively making one bucket get no values). We need to avoid: -// (ranges_[i] == ranges_[i + 1] +// ranges_[i] == ranges_[i + 1] // To avoid that, we just do a fine-grained bucket width as far as we need to // until we get a ratio that moves us along at least 2 units at a time. From // that bucket onward we do use the exponential growth of buckets. @@ -244,6 +247,7 @@ void Histogram::InitializeBucketRange() { ++current; // Just do a narrow bucket, and keep trying. SetBucketRange(bucket_index, current); } + ResetRangeChecksum(); DCHECK_EQ(bucket_count(), bucket_index); } @@ -287,6 +291,23 @@ double Histogram::GetBucketSize(Count current, size_t i) const { return current/denominator; } +void Histogram::ResetRangeChecksum() { + range_checksum_ = CalculateRangeChecksum(); +} + +bool Histogram::HasValidRangeChecksum() const { + return CalculateRangeChecksum() == range_checksum_; +} + +Histogram::Sample Histogram::CalculateRangeChecksum() const { + DCHECK_EQ(ranges_.size(), bucket_count() + 1); + Sample checksum = 0; + for (size_t index = 0; index < bucket_count(); ++index) { + checksum += ranges(index); + } + return checksum; +} + //------------------------------------------------------------------------------ // The following two methods can be overridden to provide a thread safe // version of this class. The cost of locking is low... but an error in each @@ -417,6 +438,7 @@ std::string Histogram::SerializeHistogramInfo(const Histogram& histogram, pickle.WriteInt(histogram.declared_min()); pickle.WriteInt(histogram.declared_max()); pickle.WriteSize(histogram.bucket_count()); + pickle.WriteInt(histogram.range_checksum()); pickle.WriteInt(histogram.histogram_type()); pickle.WriteInt(histogram.flags()); @@ -432,19 +454,21 @@ bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { Pickle pickle(histogram_info.data(), static_cast<int>(histogram_info.size())); - void* iter = NULL; - size_t bucket_count; + std::string histogram_name; int declared_min; int declared_max; + size_t bucket_count; + int range_checksum; int histogram_type; int pickle_flags; - std::string histogram_name; SampleSet sample; + void* iter = NULL; if (!pickle.ReadString(&iter, &histogram_name) || !pickle.ReadInt(&iter, &declared_min) || !pickle.ReadInt(&iter, &declared_max) || !pickle.ReadSize(&iter, &bucket_count) || + !pickle.ReadInt(&iter, &range_checksum) || !pickle.ReadInt(&iter, &histogram_type) || !pickle.ReadInt(&iter, &pickle_flags) || !sample.Histogram::SampleSet::Deserialize(&iter, pickle)) { @@ -483,6 +507,7 @@ bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { DCHECK_EQ(render_histogram->declared_min(), declared_min); DCHECK_EQ(render_histogram->declared_max(), declared_max); DCHECK_EQ(render_histogram->bucket_count(), bucket_count); + DCHECK_EQ(render_histogram->range_checksum(), range_checksum); DCHECK_EQ(render_histogram->histogram_type(), histogram_type); if (render_histogram->flags() & kIPCSerializationSourceFlag) { @@ -497,13 +522,64 @@ bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { } //------------------------------------------------------------------------------ +// Methods for the validating a sample and a related histogram. +//------------------------------------------------------------------------------ + +Histogram::Inconsistencies Histogram::FindCorruption( + const SampleSet& snapshot) const { + int inconsistencies = NO_INCONSISTENCIES; + Sample previous_range = -1; // Bottom range is always 0. + Sample checksum = 0; + int64 count = 0; + for (size_t index = 0; index < bucket_count(); ++index) { + count += snapshot.counts(index); + int new_range = ranges(index); + checksum += new_range; + if (previous_range >= new_range) + inconsistencies |= BUCKET_ORDER_ERROR; + previous_range = new_range; + } + + if (checksum != range_checksum_) + inconsistencies |= RANGE_CHECKSUM_ERROR; + + int64 delta64 = snapshot.redundant_count() - count; + if (delta64 != 0) { + int delta = static_cast<int>(delta64); + if (delta != delta64) + delta = INT_MAX; // Flag all giant errors as INT_MAX. + // Since snapshots of histograms are taken asynchronously relative to + // sampling (and snapped from different threads), it is pretty likely that + // we'll catch a redundant count that doesn't match the sample count. We + // allow for a certain amount of slop before flagging this as an + // inconsistency. Even with an inconsistency, we'll snapshot it again (for + // UMA in about a half hour, so we'll eventually get the data, if it was + // not the result of a corruption. If histograms show that 1 is "too tight" + // then we may try to use 2 or 3 for this slop value. + const int kCommonRaceBasedCountMismatch = 1; + if (delta > 0) { + UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountHigh", delta); + if (delta > kCommonRaceBasedCountMismatch) + inconsistencies |= COUNT_HIGH_ERROR; + } else { + DCHECK_GT(0, delta); + UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountLow", -delta); + if (-delta > kCommonRaceBasedCountMismatch) + inconsistencies |= COUNT_LOW_ERROR; + } + } + return static_cast<Inconsistencies>(inconsistencies); +} + +//------------------------------------------------------------------------------ // Methods for the Histogram::SampleSet class //------------------------------------------------------------------------------ Histogram::SampleSet::SampleSet() : counts_(), sum_(0), - square_sum_(0) { + square_sum_(0), + redundant_count_(0) { } Histogram::SampleSet::~SampleSet() { @@ -524,9 +600,11 @@ void Histogram::SampleSet::Accumulate(Sample value, Count count, counts_[index] += count; sum_ += count * value; square_sum_ += (count * value) * static_cast<int64>(value); + redundant_count_ += count; DCHECK_GE(counts_[index], 0); DCHECK_GE(sum_, 0); DCHECK_GE(square_sum_, 0); + DCHECK_GE(redundant_count_, 0); } Count Histogram::SampleSet::TotalCount() const { @@ -536,6 +614,7 @@ Count Histogram::SampleSet::TotalCount() const { ++it) { total += *it; } + DCHECK_EQ(total, redundant_count_); return total; } @@ -543,6 +622,7 @@ void Histogram::SampleSet::Add(const SampleSet& other) { DCHECK_EQ(counts_.size(), other.counts_.size()); sum_ += other.sum_; square_sum_ += other.square_sum_; + redundant_count_ += other.redundant_count_; for (size_t index = 0; index < counts_.size(); ++index) counts_[index] += other.counts_[index]; } @@ -554,6 +634,7 @@ void Histogram::SampleSet::Subtract(const SampleSet& other) { // calculated). As a result, we don't currently CHCEK() for positive values. sum_ -= other.sum_; square_sum_ -= other.square_sum_; + redundant_count_ -= other.redundant_count_; for (size_t index = 0; index < counts_.size(); ++index) { counts_[index] -= other.counts_[index]; DCHECK_GE(counts_[index], 0); @@ -563,6 +644,7 @@ void Histogram::SampleSet::Subtract(const SampleSet& other) { bool Histogram::SampleSet::Serialize(Pickle* pickle) const { pickle->WriteInt64(sum_); pickle->WriteInt64(square_sum_); + pickle->WriteInt64(redundant_count_); pickle->WriteSize(counts_.size()); for (size_t index = 0; index < counts_.size(); ++index) { @@ -576,11 +658,13 @@ bool Histogram::SampleSet::Deserialize(void** iter, const Pickle& pickle) { DCHECK_EQ(counts_.size(), 0u); DCHECK_EQ(sum_, 0); DCHECK_EQ(square_sum_, 0); + DCHECK_EQ(redundant_count_, 0); size_t counts_size; if (!pickle.ReadInt64(iter, &sum_) || !pickle.ReadInt64(iter, &square_sum_) || + !pickle.ReadInt64(iter, &redundant_count_) || !pickle.ReadSize(iter, &counts_size)) { return false; } @@ -588,14 +672,16 @@ bool Histogram::SampleSet::Deserialize(void** iter, const Pickle& pickle) { if (counts_size == 0) return false; + int count = 0; for (size_t index = 0; index < counts_size; ++index) { int i; if (!pickle.ReadInt(iter, &i)) return false; counts_.push_back(i); + count += i; } - - return true; + DCHECK_EQ(count, redundant_count_); + return count == redundant_count_; } //------------------------------------------------------------------------------ @@ -694,6 +780,7 @@ void LinearHistogram::InitializeBucketRange() { (bucket_count() - 2); SetBucketRange(i, static_cast<int> (linear_range + 0.5)); } + ResetRangeChecksum(); } double LinearHistogram::GetBucketSize(Count current, size_t i) const { @@ -740,7 +827,7 @@ BooleanHistogram::BooleanHistogram(const std::string& name) scoped_refptr<Histogram> CustomHistogram::FactoryGet( const std::string& name, - const std::vector<int>& custom_ranges, + const std::vector<Sample>& custom_ranges, Flags flags) { scoped_refptr<Histogram> histogram(NULL); @@ -774,7 +861,7 @@ Histogram::ClassType CustomHistogram::histogram_type() const { } CustomHistogram::CustomHistogram(const std::string& name, - const std::vector<int>& custom_ranges) + const std::vector<Sample>& custom_ranges) : Histogram(name, custom_ranges[1], custom_ranges.back(), custom_ranges.size()) { DCHECK_GT(custom_ranges.size(), 1u); @@ -789,6 +876,7 @@ void CustomHistogram::InitializeBucketRange() { DCHECK_LE(ranges_vector_->size(), bucket_count()); for (size_t index = 0; index < ranges_vector_->size(); ++index) SetBucketRange(index, (*ranges_vector_)[index]); + ResetRangeChecksum(); } double CustomHistogram::GetBucketSize(Count current, size_t i) const { diff --git a/base/metrics/histogram.h b/base/metrics/histogram.h index 20f67c2..b87c891 100644 --- a/base/metrics/histogram.h +++ b/base/metrics/histogram.h @@ -36,6 +36,7 @@ #include <string> #include <vector> +#include "base/gtest_prod_util.h" #include "base/ref_counted.h" #include "base/logging.h" #include "base/time.h" @@ -243,8 +244,8 @@ class Histogram : public base::RefCountedThreadSafe<Histogram> { typedef std::vector<Count> Counts; typedef std::vector<Sample> Ranges; - /* These enums are meant to facilitate deserialization of renderer histograms - into the browser. */ + // These enums are used to facilitate deserialization of renderer histograms + // into the browser. enum ClassType { HISTOGRAM, LINEAR_HISTOGRAM, @@ -273,6 +274,16 @@ class Histogram : public base::RefCountedThreadSafe<Histogram> { kHexRangePrintingFlag = 0x8000, // Fancy bucket-naming supported. }; + enum Inconsistencies { + NO_INCONSISTENCIES = 0x0, + RANGE_CHECKSUM_ERROR = 0x1, + BUCKET_ORDER_ERROR = 0x2, + COUNT_HIGH_ERROR = 0x4, + COUNT_LOW_ERROR = 0x8, + + NEVER_EXCEEDED_VALUE = 0x10 + }; + struct DescriptionPair { Sample sample; const char* description; // Null means end of a list of pairs. @@ -298,6 +309,7 @@ class Histogram : public base::RefCountedThreadSafe<Histogram> { Count TotalCount() const; int64 sum() const { return sum_; } int64 square_sum() const { return square_sum_; } + int64 redundant_count() const { return redundant_count_; } // Arithmetic manipulation of corresponding elements of the set. void Add(const SampleSet& other); @@ -315,7 +327,21 @@ class Histogram : public base::RefCountedThreadSafe<Histogram> { // without shared memory at some point. int64 sum_; // sum of samples. int64 square_sum_; // sum of squares of samples. + + private: + // Allow tests to corrupt our innards for testing purposes. + FRIEND_TEST(HistogramTest, CorruptSampleCounts); + + // To help identify memory corruption, we reduntantly save the number of + // samples we've accumulated into all of our buckets. We can compare this + // count to the sum of the counts in all buckets, and detect problems. Note + // that due to races in histogram accumulation (if a histogram is indeed + // updated on several threads simultaneously), the tallies might mismatch, + // and also the snapshotting code may asynchronously get a mismatch (though + // generally either race based mismatch cause is VERY rare). + int64 redundant_count_; }; + //---------------------------------------------------------------------------- // minimum should start from 1. 0 is invalid as a minimum. 0 is an implicit // default underflow bucket. @@ -367,6 +393,13 @@ class Histogram : public base::RefCountedThreadSafe<Histogram> { // browser process. static bool DeserializeHistogramInfo(const std::string& histogram_info); + // Check to see if bucket ranges, counts and tallies in the snapshot are + // consistent with the bucket ranges and checksums in our histogram. This can + // produce a false-alarm if a race occurred in the reading of the data during + // a SnapShot process, but should otherwise be false at all times (unless we + // have memory over-writes, or DRAM failures). + virtual Inconsistencies FindCorruption(const SampleSet& snapshot) const; + //---------------------------------------------------------------------------- // Accessors for factory constuction, serialization and testing. //---------------------------------------------------------------------------- @@ -375,6 +408,7 @@ class Histogram : public base::RefCountedThreadSafe<Histogram> { Sample declared_min() const { return declared_min_; } Sample declared_max() const { return declared_max_; } virtual Sample ranges(size_t i) const { return ranges_[i];} + Sample range_checksum() const { return range_checksum_; } virtual size_t bucket_count() const { return bucket_count_; } // Snapshot the current complete set of sample data. // Override with atomic/locked snapshot if needed. @@ -409,6 +443,9 @@ class Histogram : public base::RefCountedThreadSafe<Histogram> { // Get normalized size, relative to the ranges_[i]. virtual double GetBucketSize(Count current, size_t i) const; + // Recalculate range_checksum_. + void ResetRangeChecksum(); + // Return a string description of what goes in a given bucket. // Most commonly this is the numeric value, but in derived classes it may // be a name (or string description) given to the bucket. @@ -430,9 +467,18 @@ class Histogram : public base::RefCountedThreadSafe<Histogram> { bool ValidateBucketRanges() const; private: + // Allow tests to corrupt our innards for testing purposes. + FRIEND_TEST(HistogramTest, CorruptBucketBounds); + FRIEND_TEST(HistogramTest, CorruptSampleCounts); + // Post constructor initialization. void Initialize(); + // Return true iff the range_checksum_ matches current ranges_ vector. + bool HasValidRangeChecksum() const; + + Sample CalculateRangeChecksum() const; + //---------------------------------------------------------------------------- // Helpers for emitting Ascii graphic. Each method appends data to output. @@ -477,6 +523,11 @@ class Histogram : public base::RefCountedThreadSafe<Histogram> { // The dimension of ranges_ is bucket_count + 1. Ranges ranges_; + // For redundancy, we store the sum of all the sample ranges when ranges are + // generated. If ever there is ever a difference, then the histogram must + // have been corrupted. + Sample range_checksum_; + // Finally, provide the state that changes with the addition of each new // sample. SampleSet sample_; @@ -561,11 +612,11 @@ class CustomHistogram : public Histogram { virtual ClassType histogram_type() const; static scoped_refptr<Histogram> FactoryGet(const std::string& name, - const std::vector<int>& custom_ranges, Flags flags); + const std::vector<Sample>& custom_ranges, Flags flags); protected: CustomHistogram(const std::string& name, - const std::vector<int>& custom_ranges); + const std::vector<Sample>& custom_ranges); // Initialize ranges_ mapping. virtual void InitializeBucketRange(); @@ -573,7 +624,7 @@ class CustomHistogram : public Histogram { private: // Temporary pointer used during construction/initialization, and then NULLed. - const std::vector<int>* ranges_vector_; + const std::vector<Sample>* ranges_vector_; DISALLOW_COPY_AND_ASSIGN(CustomHistogram); }; diff --git a/base/metrics/histogram_unittest.cc b/base/metrics/histogram_unittest.cc index e7e3983..b9c51ad 100644 --- a/base/metrics/histogram_unittest.cc +++ b/base/metrics/histogram_unittest.cc @@ -308,4 +308,57 @@ TEST(HistogramTest, BucketPlacementTest) { } } // namespace + +//------------------------------------------------------------------------------ +// We can't be an an anonymous namespace while being friends, so we pop back +// out to the base namespace here. We need to be friends to corrupt the +// internals of the histogram and/or sampleset. +TEST(HistogramTest, CorruptSampleCounts) { + scoped_refptr<Histogram> histogram = Histogram::FactoryGet( + "Histogram", 1, 64, 8, Histogram::kNoFlags); // As per header file. + + EXPECT_EQ(0, histogram->sample_.redundant_count()); + histogram->Add(20); // Add some samples. + histogram->Add(40); + EXPECT_EQ(2, histogram->sample_.redundant_count()); + + Histogram::SampleSet snapshot; + histogram->SnapshotSample(&snapshot); + EXPECT_EQ(Histogram::NO_INCONSISTENCIES, 0); + EXPECT_EQ(0, histogram->FindCorruption(snapshot)); // No default corruption. + EXPECT_EQ(2, snapshot.redundant_count()); + + snapshot.counts_[3] += 100; // Sample count won't match redundant count. + EXPECT_EQ(Histogram::COUNT_LOW_ERROR, histogram->FindCorruption(snapshot)); + snapshot.counts_[2] -= 200; + EXPECT_EQ(Histogram::COUNT_HIGH_ERROR, histogram->FindCorruption(snapshot)); + + // But we can't spot a corruption if it is compensated for. + snapshot.counts_[1] += 100; + EXPECT_EQ(0, histogram->FindCorruption(snapshot)); +} + +TEST(HistogramTest, CorruptBucketBounds) { + scoped_refptr<Histogram> histogram = Histogram::FactoryGet( + "Histogram", 1, 64, 8, Histogram::kNoFlags); // As per header file. + + Histogram::SampleSet snapshot; + histogram->SnapshotSample(&snapshot); + EXPECT_EQ(Histogram::NO_INCONSISTENCIES, 0); + EXPECT_EQ(0, histogram->FindCorruption(snapshot)); // No default corruption. + + std::swap(histogram->ranges_[1], histogram->ranges_[2]); + EXPECT_EQ(Histogram::BUCKET_ORDER_ERROR, histogram->FindCorruption(snapshot)); + + std::swap(histogram->ranges_[1], histogram->ranges_[2]); + EXPECT_EQ(0, histogram->FindCorruption(snapshot)); + + ++histogram->ranges_[3]; + EXPECT_EQ(Histogram::RANGE_CHECKSUM_ERROR, + histogram->FindCorruption(snapshot)); + + // Repair histogram so that destructor won't DCHECK(). + --histogram->ranges_[3]; +} + } // namespace base diff --git a/base/metrics/nacl_histogram.cc b/base/metrics/nacl_histogram.cc new file mode 100644 index 0000000..c89e646 --- /dev/null +++ b/base/metrics/nacl_histogram.cc @@ -0,0 +1,23 @@ +// Copyright (c) 2010 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. + +// nacl_histogram provides an enum and defines a macro that uses definitions +// from histogram.h. Note that a histogram is an object that aggregates +// statistics, and can summarize them in various forms, including ASCII +// graphical, HTML, and numerically. +// nacl_histogram information is used to compare how many times a native +// client module has been loaded, compared to the number of chrome starts +// and number of new tabs created. + + +#include "base/metrics/histogram.h" +#include "base/metrics/nacl_histogram.h" + +// To log histogram data about NaCl.Startups, call this function with +// a NaClHistogramValue passed in as |hvalue| +void UmaNaclHistogramEnumeration(NaClHistogramValue histogram_value) { + UMA_HISTOGRAM_ENUMERATION("NaCl.Startups", histogram_value, + NACL_MAX_HISTOGRAM); +} + diff --git a/base/metrics/nacl_histogram.h b/base/metrics/nacl_histogram.h new file mode 100644 index 0000000..479b571 --- /dev/null +++ b/base/metrics/nacl_histogram.h @@ -0,0 +1,29 @@ +// Copyright (c) 2010 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. + +// nacl_histogram provides an enum and defines a macro that uses definitions +// from histogram.h. Note that a histogram is an object that aggregates +// statistics, and can summarize them in various forms, including ASCII +// graphical, HTML, and numerically. +// nacl_histogram information is used to compare how many times a native +// client module has been loaded, compared to the number of chrome starts +// and number of new tabs created. + +#ifndef BASE_METRICS_NACL_HISTOGRAM_H_ +#define BASE_METRICS_NACL_HISTOGRAM_H_ +#pragma once + +enum NaClHistogramValue { + FIRST_TAB_NACL_BASELINE, // First tab created - a baseline for NaCl starts. + NEW_TAB_NACL_BASELINE, // New tab created -- a baseline for NaCl starts. + NACL_STARTED, // NaCl process started + NACL_MAX_HISTOGRAM // DO NOT USE -- used by histogram for max bound. +}; + +// To log histogram data about NaCl.Startups, call this macro with +// a NaClHistogramValue passed in as |histogram_value| +void UmaNaclHistogramEnumeration(NaClHistogramValue histogram_value); + +#endif // BASE_METRICS_NACL_HISTOGRAM_H_ + diff --git a/base/metrics/stats_table.cc b/base/metrics/stats_table.cc index 261f533..bf93395 100644 --- a/base/metrics/stats_table.cc +++ b/base/metrics/stats_table.cc @@ -164,7 +164,7 @@ StatsTable::Private* StatsTable::Private::New(const std::string& name, return NULL; #else scoped_ptr<Private> priv(new Private()); - if (!priv->shared_memory_.Create(name, false, true, size)) + if (!priv->shared_memory_.CreateNamed(name, true, size)) return NULL; if (!priv->shared_memory_.Map(size)) return NULL; diff --git a/base/move.h b/base/move.h deleted file mode 100644 index 118eb0a..0000000 --- a/base/move.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2010 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_MOVE_H_ -#define BASE_MOVE_H_ -#pragma once - -#include <algorithm> - -namespace base { - -// The move function provides a functional form of swap to move a swappable -// value leverage the compiler return value optimization and avoiding copy. -// -// The type being moved must have an efficient swap() and be default -// contructable and copyable. -// -// C++0x will contain an std::move() function that makes use of rvalue -// references to achieve the same result. When std::move() is available, it -// can replace base::move(). -// -// For example, the following code will not produce any copies of the string. -// -// std::string f() { -// std::string result("Hello World"); -// return result; -// } -// -// class Class { -// public: -// Class(std::string x) // Pass sink argument by value -// : m_(move(x)); // Move x into m_ -// private: -// std::string m_; -// }; -// -// int main() { -// Class x(f()); // the result string of f() is allocated as the temp argument -// // to x() and then swapped into the member m_. No Strings -// // are copied by this call. -// ... - -template <typename T> // T models Regular -T move(T& x) { // NOLINT : Non-const ref for standard library conformance - using std::swap; - T result; - swap(x, result); - return result; -} - -} // namespace base - -#endif // BASE_MOVE_H_ - diff --git a/base/nsimage_cache_mac.mm b/base/nsimage_cache_mac.mm index 566a7f9..e693ed4 100644 --- a/base/nsimage_cache_mac.mm +++ b/base/nsimage_cache_mac.mm @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -35,8 +35,8 @@ NSImage* ImageNamed(NSString* name) { NSImage* result = [image_cache objectForKey:name]; if (!result) { - DLOG_IF(INFO, [[name pathExtension] length] == 0) - << "Suggest including the extension in the image name"; + DVLOG_IF(1, [[name pathExtension] length] == 0) << "Suggest including the " + "extension in the image name"; NSString* path = [mac_util::MainAppBundle() pathForImageResource:name]; if (path) { diff --git a/base/nss_util.cc b/base/nss_util.cc index 5c7cafd..fd91142 100644 --- a/base/nss_util.cc +++ b/base/nss_util.cc @@ -14,7 +14,7 @@ #include <secmod.h> #if defined(OS_LINUX) -#include <linux/magic.h> +#include <linux/nfs_fs.h> #include <sys/vfs.h> #endif @@ -22,6 +22,7 @@ #include "base/logging.h" #include "base/singleton.h" #include "base/stringprintf.h" +#include "base/thread_restrictions.h" // USE_NSS means we use NSS for everything crypto-related. If USE_NSS is not // defined, such as on Mac and Windows, we use NSS for SSL only -- we don't @@ -310,6 +311,10 @@ void EnsureNSPRInit() { } void EnsureNSSInit() { + // Initializing SSL causes us to do blocking IO. + // Temporarily allow it until we fix + // http://code.google.com/p/chromium/issues/detail?id=59847 + base::ThreadRestrictions::ScopedAllowIO allow_io; Singleton<NSSInitSingleton>::get(); } diff --git a/base/openssl_util.h b/base/openssl_util.h new file mode 100644 index 0000000..4f564cf --- /dev/null +++ b/base/openssl_util.h @@ -0,0 +1,53 @@ +// Copyright (c) 2010 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_OPENNSSL_UTIL_H_ +#define BASE_OPENNSSL_UTIL_H_ +#pragma once + +#include "base/basictypes.h" + +namespace base { + +// Provides a buffer of at least MIN_SIZE bytes, for use when calling OpenSSL's +// SHA256, HMAC, etc functions, adapting the buffer sizing rules to meet those +// of the our base wrapper APIs. +// This allows the library to write directly to the caller's buffer if it is of +// sufficient size, but if not it will write to temporary |min_sized_buffer_| +// of required size and then its content is automatically copied out on +// destruction, with truncation as appropriate. +template<int MIN_SIZE> +class ScopedOpenSSLSafeSizeBuffer { + public: + ScopedOpenSSLSafeSizeBuffer(unsigned char* output, size_t output_len) + : output_(output), + output_len_(output_len) { + } + + ~ScopedOpenSSLSafeSizeBuffer() { + if (output_len_ < MIN_SIZE) { + // Copy the temporary buffer out, truncating as needed. + memcpy(output_, min_sized_buffer_, output_len_); + } + // else... any writing already happened directly into |output_|. + } + + unsigned char* safe_buffer() { + return output_len_ < MIN_SIZE ? min_sized_buffer_ : output_; + } + + private: + // Pointer to the caller's data area and it's associated size, where data + // written via safe_buffer() will [eventually] end up. + unsigned char* output_; + size_t output_len_; + + // Temporary buffer writen into in the case where the caller's + // buffer is not of sufficient size. + unsigned char min_sized_buffer_[MIN_SIZE]; +}; + +} // namespace base + +#endif // BASE_NSS_UTIL_H_ diff --git a/base/path_service_unittest.cc b/base/path_service_unittest.cc index 4c41aed..c65f7a5 100644 --- a/base/path_service_unittest.cc +++ b/base/path_service_unittest.cc @@ -21,6 +21,12 @@ namespace { bool ReturnsValidPath(int dir_type) { FilePath path; bool result = PathService::Get(dir_type, &path); +#if defined(OS_POSIX) + // If chromium has never been started on this account, the cache path will not + // exist. + if (dir_type == base::DIR_USER_CACHE) + return result && !path.value().empty(); +#endif return result && !path.value().empty() && file_util::PathExists(path); } diff --git a/base/platform_file_posix.cc b/base/platform_file_posix.cc index b691f00..8af078b 100644 --- a/base/platform_file_posix.cc +++ b/base/platform_file_posix.cc @@ -54,8 +54,9 @@ PlatformFile CreatePlatformFile(const FilePath& name, int flags, open_flags |= O_RDWR; } else if (flags & PLATFORM_FILE_WRITE) { open_flags |= O_WRONLY; - } else if (!(flags & PLATFORM_FILE_READ || - flags & PLATFORM_FILE_WRITE_ATTRIBUTES)) { + } else if (!(flags & PLATFORM_FILE_READ) && + !(flags & PLATFORM_FILE_WRITE_ATTRIBUTES) && + !(flags & PLATFORM_FILE_OPEN_ALWAYS)) { NOTREACHED(); } diff --git a/base/platform_file_win.cc b/base/platform_file_win.cc index 3aa02e8..34d8e45 100644 --- a/base/platform_file_win.cc +++ b/base/platform_file_win.cc @@ -6,6 +6,7 @@ #include "base/file_path.h" #include "base/logging.h" +#include "base/thread_restrictions.h" namespace base { @@ -13,6 +14,8 @@ PlatformFile CreatePlatformFile(const FilePath& name, int flags, bool* created, PlatformFileError* error_code) { + base::ThreadRestrictions::AssertIOAllowed(); + DWORD disposition = 0; if (flags & PLATFORM_FILE_OPEN) @@ -108,10 +111,12 @@ PlatformFile CreatePlatformFile(const std::wstring& name, int flags, } bool ClosePlatformFile(PlatformFile file) { + base::ThreadRestrictions::AssertIOAllowed(); return (CloseHandle(file) != 0); } int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) { + base::ThreadRestrictions::AssertIOAllowed(); if (file == kInvalidPlatformFileValue) return -1; @@ -133,6 +138,7 @@ int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) { int WritePlatformFile(PlatformFile file, int64 offset, const char* data, int size) { + base::ThreadRestrictions::AssertIOAllowed(); if (file == kInvalidPlatformFileValue) return -1; @@ -151,6 +157,7 @@ int WritePlatformFile(PlatformFile file, int64 offset, } bool TruncatePlatformFile(PlatformFile file, int64 length) { + base::ThreadRestrictions::AssertIOAllowed(); if (file == kInvalidPlatformFileValue) return false; @@ -176,11 +183,13 @@ bool TruncatePlatformFile(PlatformFile file, int64 length) { } bool FlushPlatformFile(PlatformFile file) { + base::ThreadRestrictions::AssertIOAllowed(); return ((file != kInvalidPlatformFileValue) && ::FlushFileBuffers(file)); } bool TouchPlatformFile(PlatformFile file, const base::Time& last_access_time, const base::Time& last_modified_time) { + base::ThreadRestrictions::AssertIOAllowed(); if (file == kInvalidPlatformFileValue) return false; @@ -191,6 +200,7 @@ bool TouchPlatformFile(PlatformFile file, const base::Time& last_access_time, } bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) { + base::ThreadRestrictions::AssertIOAllowed(); if (!info) return false; diff --git a/base/process_util_linux.cc b/base/process_util_linux.cc index 231db59..6138c07 100644 --- a/base/process_util_linux.cc +++ b/base/process_util_linux.cc @@ -22,6 +22,7 @@ #include "base/string_tokenizer.h" #include "base/string_util.h" #include "base/sys_info.h" +#include "base/thread_restrictions.h" namespace { @@ -33,6 +34,9 @@ enum ParsingState { // Reads /proc/<pid>/stat and populates |proc_stats| with the values split by // spaces. Returns true if successful. bool GetProcStats(pid_t pid, std::vector<std::string>* proc_stats) { + // Synchronously reading files in /proc is safe. + base::ThreadRestrictions::ScopedAllowIO allow_io; + FilePath stat_file("/proc"); stat_file = stat_file.Append(base::IntToString(pid)); stat_file = stat_file.Append("stat"); @@ -49,6 +53,9 @@ bool GetProcStats(pid_t pid, std::vector<std::string>* proc_stats) { // null characters. We tokenize it into a vector of strings using '\0' as a // delimiter. bool GetProcCmdline(pid_t pid, std::vector<std::string>* proc_cmd_line_args) { + // Synchronously reading files in /proc is safe. + base::ThreadRestrictions::ScopedAllowIO allow_io; + FilePath cmd_line_file("/proc"); cmd_line_file = cmd_line_file.Append(base::IntToString(pid)); cmd_line_file = cmd_line_file.Append("cmdline"); @@ -66,6 +73,9 @@ bool GetProcCmdline(pid_t pid, std::vector<std::string>* proc_cmd_line_args) { namespace base { ProcessId GetParentProcessId(ProcessHandle process) { + // Synchronously reading files in /proc is safe. + base::ThreadRestrictions::ScopedAllowIO allow_io; + FilePath stat_file("/proc"); stat_file = stat_file.Append(base::IntToString(process)); stat_file = stat_file.Append("status"); @@ -307,6 +317,9 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, // close approximation. // See http://www.pixelbeat.org/scripts/ps_mem.py bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { + // Synchronously reading files in /proc is safe. + base::ThreadRestrictions::ScopedAllowIO allow_io; + FilePath stat_file = FilePath("/proc").Append(base::IntToString(process_)).Append("smaps"); std::string smaps; @@ -380,6 +393,9 @@ bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING // in your kernel configuration. bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { + // Synchronously reading files in /proc is safe. + base::ThreadRestrictions::ScopedAllowIO allow_io; + std::string proc_io_contents; FilePath io_file("/proc"); io_file = io_file.Append(base::IntToString(process_)); @@ -447,6 +463,9 @@ int ParseProcStatCPU(const std::string& input) { // Get the total CPU of a single process. Return value is number of jiffies // on success or -1 on error. static int GetProcessCPU(pid_t pid) { + // Synchronously reading files in /proc is safe. + base::ThreadRestrictions::ScopedAllowIO allow_io; + // Use /proc/<pid>/task to find all threads and parse their /stat file. FilePath path = FilePath(StringPrintf("/proc/%d/task/", pid)); @@ -534,6 +553,9 @@ const size_t kMemCacheIndex = 10; } // namespace size_t GetSystemCommitCharge() { + // Synchronously reading files in /proc is safe. + base::ThreadRestrictions::ScopedAllowIO allow_io; + // Used memory is: total - free - buffers - caches FilePath meminfo_file("/proc/meminfo"); std::string meminfo_data; diff --git a/base/process_util_mac.mm b/base/process_util_mac.mm index cae47bf..b167aa2 100644 --- a/base/process_util_mac.mm +++ b/base/process_util_mac.mm @@ -23,7 +23,7 @@ #include <new> #include <string> -#include "base/debug_util.h" +#include "base/debug/debugger.h" #include "base/eintr_wrapper.h" #include "base/logging.h" #include "base/string_util.h" @@ -166,7 +166,7 @@ bool ProcessIterator::CheckForNextProcess() { } bool NamedProcessIterator::IncludeEntry() { - return (base::SysWideToUTF8(executable_name_) == entry().exe_file() && + return (SysWideToUTF8(executable_name_) == entry().exe_file() && ProcessIterator::IncludeEntry()); } @@ -187,7 +187,7 @@ ProcessMetrics::ProcessMetrics(ProcessHandle process, last_time_(0), last_system_time_(0), port_provider_(port_provider) { - processor_count_ = base::SysInfo::NumberOfProcessors(); + processor_count_ = SysInfo::NumberOfProcessors(); } // static @@ -402,7 +402,7 @@ void* oom_killer_malloc(struct _malloc_zone_t* zone, size_t size) { void* result = g_old_malloc(zone, size); if (!result && size) - DebugUtil::BreakDebugger(); + debug::BreakDebugger(); return result; } @@ -411,7 +411,7 @@ void* oom_killer_calloc(struct _malloc_zone_t* zone, size_t size) { void* result = g_old_calloc(zone, num_items, size); if (!result && num_items && size) - DebugUtil::BreakDebugger(); + debug::BreakDebugger(); return result; } @@ -419,7 +419,7 @@ void* oom_killer_valloc(struct _malloc_zone_t* zone, size_t size) { void* result = g_old_valloc(zone, size); if (!result && size) - DebugUtil::BreakDebugger(); + debug::BreakDebugger(); return result; } @@ -428,7 +428,7 @@ void* oom_killer_realloc(struct _malloc_zone_t* zone, size_t size) { void* result = g_old_realloc(zone, ptr, size); if (!result && size) - DebugUtil::BreakDebugger(); + debug::BreakDebugger(); return result; } @@ -441,7 +441,7 @@ void* oom_killer_memalign(struct _malloc_zone_t* zone, // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c ). if (!result && size && alignment >= sizeof(void*) && (alignment & (alignment - 1)) == 0) { - DebugUtil::BreakDebugger(); + debug::BreakDebugger(); } return result; } @@ -450,7 +450,7 @@ void* oom_killer_malloc_purgeable(struct _malloc_zone_t* zone, size_t size) { void* result = g_old_malloc_purgeable(zone, size); if (!result && size) - DebugUtil::BreakDebugger(); + debug::BreakDebugger(); return result; } @@ -459,7 +459,7 @@ void* oom_killer_calloc_purgeable(struct _malloc_zone_t* zone, size_t size) { void* result = g_old_calloc_purgeable(zone, num_items, size); if (!result && num_items && size) - DebugUtil::BreakDebugger(); + debug::BreakDebugger(); return result; } @@ -467,7 +467,7 @@ void* oom_killer_valloc_purgeable(struct _malloc_zone_t* zone, size_t size) { void* result = g_old_valloc_purgeable(zone, size); if (!result && size) - DebugUtil::BreakDebugger(); + debug::BreakDebugger(); return result; } @@ -476,7 +476,7 @@ void* oom_killer_realloc_purgeable(struct _malloc_zone_t* zone, size_t size) { void* result = g_old_realloc_purgeable(zone, ptr, size); if (!result && size) - DebugUtil::BreakDebugger(); + debug::BreakDebugger(); return result; } @@ -489,7 +489,7 @@ void* oom_killer_memalign_purgeable(struct _malloc_zone_t* zone, // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c ). if (!result && size && alignment >= sizeof(void*) && (alignment & (alignment - 1)) == 0) { - DebugUtil::BreakDebugger(); + debug::BreakDebugger(); } return result; } @@ -497,7 +497,7 @@ void* oom_killer_memalign_purgeable(struct _malloc_zone_t* zone, // === C++ operator new === void oom_killer_new() { - DebugUtil::BreakDebugger(); + debug::BreakDebugger(); } // === Core Foundation CFAllocators === @@ -513,7 +513,7 @@ void* oom_killer_cfallocator_system_default(CFIndex alloc_size, void* info) { void* result = g_old_cfallocator_system_default(alloc_size, hint, info); if (!result) - DebugUtil::BreakDebugger(); + debug::BreakDebugger(); return result; } @@ -522,7 +522,7 @@ void* oom_killer_cfallocator_malloc(CFIndex alloc_size, void* info) { void* result = g_old_cfallocator_malloc(alloc_size, hint, info); if (!result) - DebugUtil::BreakDebugger(); + debug::BreakDebugger(); return result; } @@ -531,7 +531,7 @@ void* oom_killer_cfallocator_malloc_zone(CFIndex alloc_size, void* info) { void* result = g_old_cfallocator_malloc_zone(alloc_size, hint, info); if (!result) - DebugUtil::BreakDebugger(); + debug::BreakDebugger(); return result; } @@ -544,7 +544,7 @@ id oom_killer_allocWithZone(id self, SEL _cmd, NSZone* zone) { id result = g_old_allocWithZone(self, _cmd, zone); if (!result) - DebugUtil::BreakDebugger(); + debug::BreakDebugger(); return result; } diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc index b1dbabd..07e3125 100644 --- a/base/process_util_posix.cc +++ b/base/process_util_posix.cc @@ -18,7 +18,7 @@ #include "base/command_line.h" #include "base/compiler_specific.h" -#include "base/debug_util.h" +#include "base/debug/stack_trace.h" #include "base/dir_reader_posix.h" #include "base/eintr_wrapper.h" #include "base/logging.h" @@ -103,7 +103,7 @@ int WaitpidWithTimeout(ProcessHandle handle, int64 wait_milliseconds, void StackDumpSignalHandler(int signal) { LOG(ERROR) << "Received signal " << signal; - StackTrace().PrintBacktrace(); + debug::StackTrace().PrintBacktrace(); _exit(1); } diff --git a/base/process_util_win.cc b/base/process_util_win.cc index 92077b1..097888e 100644 --- a/base/process_util_win.cc +++ b/base/process_util_win.cc @@ -13,7 +13,7 @@ #include <ios> #include "base/command_line.h" -#include "base/debug_util.h" +#include "base/debug/stack_trace.h" #include "base/logging.h" #include "base/metrics/histogram.h" #include "base/scoped_ptr.h" @@ -40,7 +40,7 @@ LPTOP_LEVEL_EXCEPTION_FILTER g_previous_filter = NULL; // Prints the exception call stack. // This is the unit tests exception filter. long WINAPI StackDumpExceptionFilter(EXCEPTION_POINTERS* info) { - StackTrace(info).PrintBacktrace(); + debug::StackTrace(info).PrintBacktrace(); if (g_previous_filter) return g_previous_filter(info); return EXCEPTION_CONTINUE_SEARCH; @@ -155,7 +155,7 @@ bool GetProcessIntegrityLevel(ProcessHandle process, IntegrityLevel *level) { if (!level) return false; - if (base::win::GetVersion() < base::win::VERSION_VISTA) + if (win::GetVersion() < base::win::VERSION_VISTA) return false; HANDLE process_token; @@ -163,7 +163,7 @@ bool GetProcessIntegrityLevel(ProcessHandle process, IntegrityLevel *level) { &process_token)) return false; - base::win::ScopedHandle scoped_process_token(process_token); + win::ScopedHandle scoped_process_token(process_token); DWORD token_info_length = 0; if (GetTokenInformation(process_token, TokenIntegrityLevel, NULL, 0, @@ -326,8 +326,8 @@ bool GetAppOutput(const CommandLine& cl, std::string* output) { } // Ensure we don't leak the handles. - base::win::ScopedHandle scoped_out_read(out_read); - base::win::ScopedHandle scoped_out_write(out_write); + win::ScopedHandle scoped_out_read(out_read); + win::ScopedHandle scoped_out_write(out_write); // Ensure the read handle to the pipe for STDOUT is not inherited. if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) { diff --git a/base/ref_counted.h b/base/ref_counted.h index 2cc4029..9c84efa 100644 --- a/base/ref_counted.h +++ b/base/ref_counted.h @@ -106,7 +106,7 @@ template <class T, typename Traits> class RefCountedThreadSafe; // count reaches 0. Overload to delete it on a different thread etc. template<typename T> struct DefaultRefCountedThreadSafeTraits { - static void Destruct(T* x) { + static void Destruct(const T* x) { // Delete through RefCountedThreadSafe to make child classes only need to be // friend with RefCountedThreadSafe instead of this struct, which is an // implementation detail. @@ -133,19 +133,19 @@ class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase { RefCountedThreadSafe() { } ~RefCountedThreadSafe() { } - void AddRef() { + void AddRef() const { subtle::RefCountedThreadSafeBase::AddRef(); } - void Release() { + void Release() const { if (subtle::RefCountedThreadSafeBase::Release()) { - Traits::Destruct(static_cast<T*>(this)); + Traits::Destruct(static_cast<const T*>(this)); } } private: friend struct DefaultRefCountedThreadSafeTraits<T>; - static void DeleteInternal(T* x) { delete x; } + static void DeleteInternal(const T* x) { delete x; } DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafe); }; diff --git a/base/ref_counted_unittest.cc b/base/ref_counted_unittest.cc index f2739fc..cd6f922 100644 --- a/base/ref_counted_unittest.cc +++ b/base/ref_counted_unittest.cc @@ -26,7 +26,7 @@ class CheckDerivedMemberAccess : public scoped_refptr<SelfAssign> { TEST(RefCountedUnitTest, TestSelfAssignment) { SelfAssign* p = new SelfAssign; - scoped_refptr<SelfAssign> var = p; + scoped_refptr<SelfAssign> var(p); var = var; EXPECT_EQ(var.get(), p); } diff --git a/base/registry.h b/base/registry.h deleted file mode 100644 index 5ef45f2..0000000 --- a/base/registry.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2010 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_REGISTRY_H_ -#define BASE_REGISTRY_H_ -#pragma once - -// TODO(brettw) remove this file when all callers are converted to using the -// new location & namespace. -#include "base/win/registry.h" - -class RegKey : public base::win::RegKey { - public: - RegKey() {} - RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access) - : base::win::RegKey(rootkey, subkey, access) {} - ~RegKey() { base::win::RegKey::~RegKey(); } -}; - -class RegistryValueIterator : public base::win::RegistryValueIterator { - public: - RegistryValueIterator(HKEY root_key, const wchar_t* folder_key) - : base::win::RegistryValueIterator(root_key, folder_key) {} - ~RegistryValueIterator() { - base::win::RegistryValueIterator::~RegistryValueIterator(); - } -}; - -class RegistryKeyIterator : public base::win::RegistryKeyIterator { - public: - RegistryKeyIterator(HKEY root_key, const wchar_t* folder_key) - : base::win::RegistryKeyIterator(root_key, folder_key) {} - ~RegistryKeyIterator() { - base::win::RegistryKeyIterator::~RegistryKeyIterator(); - } -}; - -#endif // BASE_REGISTRY_H_ diff --git a/base/scoped_temp_dir.cc b/base/scoped_temp_dir.cc index a510ddf..5cd13b4 100644 --- a/base/scoped_temp_dir.cc +++ b/base/scoped_temp_dir.cc @@ -15,6 +15,9 @@ ScopedTempDir::~ScopedTempDir() { } bool ScopedTempDir::CreateUniqueTempDir() { + if (!path_.empty()) + return false; + // This "scoped_dir" prefix is only used on Windows and serves as a template // for the unique name. if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("scoped_dir"), @@ -25,7 +28,10 @@ bool ScopedTempDir::CreateUniqueTempDir() { } bool ScopedTempDir::CreateUniqueTempDirUnderPath(const FilePath& base_path) { - // If |path| does not exist, create it. + if (!path_.empty()) + return false; + + // If |base_path| does not exist, create it. if (!file_util::CreateDirectory(base_path)) return false; @@ -33,18 +39,20 @@ bool ScopedTempDir::CreateUniqueTempDirUnderPath(const FilePath& base_path) { if (!file_util::CreateTemporaryDirInDir( base_path, FILE_PATH_LITERAL("scoped_dir_"), - &path_)) { + &path_)) return false; - } + return true; } bool ScopedTempDir::Set(const FilePath& path) { - DCHECK(path_.empty()); + if (!path_.empty()) + return false; + if (!file_util::DirectoryExists(path) && - !file_util::CreateDirectory(path)) { + !file_util::CreateDirectory(path)) return false; - } + path_ = path; return true; } diff --git a/base/scoped_temp_dir.h b/base/scoped_temp_dir.h index 66d52f6..6845562 100644 --- a/base/scoped_temp_dir.h +++ b/base/scoped_temp_dir.h @@ -11,6 +11,10 @@ // deletion occurs during the destructor, no further error handling is possible // if the directory fails to be deleted. As a result, deletion is not // guaranteed by this class. +// +// Multiple calls to the methods which establish a temporary directory +// (CreateUniqueTempDir, CreateUniqueTempDirUnderPath, and Set) must have +// intervening calls to Delete or Take, or the calls will fail. #include "base/file_path.h" diff --git a/base/scoped_temp_dir_unittest.cc b/base/scoped_temp_dir_unittest.cc index 4be0d07..cf5fed3 100644 --- a/base/scoped_temp_dir_unittest.cc +++ b/base/scoped_temp_dir_unittest.cc @@ -73,3 +73,17 @@ TEST(ScopedTempDir, UniqueTempDirUnderPath) { } EXPECT_FALSE(file_util::DirectoryExists(test_path)); } + +TEST(ScopedTempDir, MultipleInvocations) { + ScopedTempDir dir; + EXPECT_TRUE(dir.CreateUniqueTempDir()); + EXPECT_FALSE(dir.CreateUniqueTempDir()); + dir.Delete(); + EXPECT_TRUE(dir.CreateUniqueTempDir()); + EXPECT_FALSE(dir.CreateUniqueTempDir()); + ScopedTempDir other_dir; + other_dir.Set(dir.Take()); + EXPECT_TRUE(dir.CreateUniqueTempDir()); + EXPECT_FALSE(dir.CreateUniqueTempDir()); + EXPECT_FALSE(other_dir.CreateUniqueTempDir()); +} diff --git a/base/sha2_openssl.cc b/base/sha2_openssl.cc new file mode 100644 index 0000000..afbce2f --- /dev/null +++ b/base/sha2_openssl.cc @@ -0,0 +1,30 @@ +// Copyright (c) 2010 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/sha2.h" + +#include <openssl/ssl.h> + +#include "base/basictypes.h" +#include "base/openssl_util.h" +#include "base/stl_util-inl.h" + +namespace base { + +void SHA256HashString(const std::string& str, void* output, size_t len) { + COMPILE_ASSERT(SHA256_LENGTH == SHA256_DIGEST_LENGTH, + API_and_OpenSSL_SHA256_lengths_must_match); + ScopedOpenSSLSafeSizeBuffer<SHA256_DIGEST_LENGTH> result( + reinterpret_cast<unsigned char*>(output), len); + ::SHA256(reinterpret_cast<const unsigned char*>(str.data()), str.size(), + result.safe_buffer()); +} + +std::string SHA256HashString(const std::string& str) { + std::string output(SHA256_LENGTH, 0); + SHA256HashString(str, string_as_array(&output), output.size()); + return output; +} + +} // namespace base diff --git a/base/shared_memory.h b/base/shared_memory.h index 053026e..719eb69 100644 --- a/base/shared_memory.h +++ b/base/shared_memory.h @@ -43,6 +43,13 @@ class SharedMemory { public: SharedMemory(); +#if defined(OS_WIN) + // Similar to the default constructor, except that this allows for + // calling Lock() to acquire the named mutex before either Create or Open + // are called on Windows. + explicit SharedMemory(const std::wstring& name); +#endif + // Create a new SharedMemory object from an existing, open // shared memory file. SharedMemory(SharedMemoryHandle handle, bool read_only); @@ -66,14 +73,21 @@ class SharedMemory { // Closes a shared memory handle. static void CloseHandle(const SharedMemoryHandle& handle); + // Creates and maps an anonymous shared memory segment of size size. + // Returns true on success and false on failure. + bool CreateAndMapAnonymous(uint32 size); + + // Creates an anonymous shared memory segment of size size. + // Returns true on success and false on failure. + bool CreateAnonymous(uint32 size); + // Creates or opens a shared memory segment based on a name. - // If read_only is true, opens the memory as read-only. // If open_existing is true, and the shared memory already exists, // opens the existing shared memory and ignores the size parameter. - // If name is the empty string, use a unique name. + // If open_existing is false, shared memory must not exist. + // size is the size of the block to be created. // Returns true on success, false on failure. - bool Create(const std::string& name, bool read_only, bool open_existing, - uint32 size); + bool CreateNamed(const std::string& name, bool open_existing, uint32 size); // Deletes resources associated with a shared memory segment based on name. // Not all platforms require this call. @@ -81,7 +95,6 @@ class SharedMemory { // Opens a shared memory segment based on a name. // If read_only is true, opens for read-only access. - // If name is the empty string, use a unique name. // Returns true on success, false on failure. bool Open(const std::string& name, bool read_only); @@ -95,12 +108,15 @@ class SharedMemory { // memory is not mapped. bool Unmap(); - // Get the size of the opened shared memory backing file. + // Get the size of the shared memory backing file. // Note: This size is only available to the creator of the // shared memory, and not to those that opened shared memory // created externally. - // Returns 0 if not opened or unknown. - uint32 max_size() const { return max_size_; } + // Returns 0 if not created or unknown. + // Deprecated method, please keep track of the size yourself if you created + // it. + // http://crbug.com/60821 + uint32 created_size() const { return created_size_; } // Gets a pointer to the opened memory space if it has been // Mapped via Map(). Returns NULL if it is not mapped. @@ -156,12 +172,18 @@ class SharedMemory { // across Mac and Linux. void Lock(); +#if defined(OS_WIN) + // A Lock() implementation with a timeout. Returns true if the Lock() has + // been acquired, false if the timeout was reached. + bool Lock(uint32 timeout_ms); +#endif + // Releases the shared memory lock. void Unlock(); private: #if defined(OS_POSIX) - bool CreateOrOpen(const std::string& name, int posix_flags, uint32 size); + bool PrepareMapFile(FILE *fp); bool FilePathForMemoryName(const std::string& mem_name, FilePath* path); void LockOrUnlockCommon(int function); #endif @@ -174,11 +196,12 @@ class SharedMemory { HANDLE mapped_file_; #elif defined(OS_POSIX) int mapped_file_; + uint32 mapped_size_; ino_t inode_; #endif void* memory_; bool read_only_; - uint32 max_size_; + uint32 created_size_; #if !defined(OS_POSIX) SharedMemoryLock lock_; #endif diff --git a/base/shared_memory_posix.cc b/base/shared_memory_posix.cc index dfd86aa..88203dd 100644 --- a/base/shared_memory_posix.cc +++ b/base/shared_memory_posix.cc @@ -14,6 +14,7 @@ #include "base/logging.h" #include "base/platform_thread.h" #include "base/safe_strerror_posix.h" +#include "base/thread_restrictions.h" #include "base/utf_string_conversions.h" namespace base { @@ -26,18 +27,20 @@ const char kSemaphoreSuffix[] = "-sem"; SharedMemory::SharedMemory() : mapped_file_(-1), + mapped_size_(0), inode_(0), memory_(NULL), read_only_(false), - max_size_(0) { + created_size_(0) { } SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only) : mapped_file_(handle.fd), + mapped_size_(0), inode_(0), memory_(NULL), read_only_(read_only), - max_size_(0) { + created_size_(0) { struct stat st; if (fstat(handle.fd, &st) == 0) { // If fstat fails, then the file descriptor is invalid and we'll learn this @@ -49,9 +52,11 @@ SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only) SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only, ProcessHandle process) : mapped_file_(handle.fd), + mapped_size_(0), + inode_(0), memory_(NULL), read_only_(read_only), - max_size_(0) { + created_size_(0) { // We don't handle this case yet (note the ignored parameter); let's die if // someone comes calling. NOTREACHED(); @@ -77,20 +82,90 @@ void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) { close(handle.fd); } -bool SharedMemory::Create(const std::string& name, bool read_only, - bool open_existing, uint32 size) { - read_only_ = read_only; +bool SharedMemory::CreateAndMapAnonymous(uint32 size) { + return CreateAnonymous(size) && Map(size); +} + +bool SharedMemory::CreateAnonymous(uint32 size) { + return CreateNamed("", false, size); +} + +// Chromium mostly only uses the unique/private shmem as specified by +// "name == L"". The exception is in the StatsTable. +// TODO(jrg): there is no way to "clean up" all unused named shmem if +// we restart from a crash. (That isn't a new problem, but it is a problem.) +// In case we want to delete it later, it may be useful to save the value +// of mem_filename after FilePathForMemoryName(). +bool SharedMemory::CreateNamed(const std::string& name, + bool open_existing, uint32 size) { + DCHECK(mapped_file_ == -1); + if (size == 0) return false; + + // This function theoretically can block on the disk, but realistically + // the temporary files we create will just go into the buffer cache + // and be deleted before they ever make it out to disk. + base::ThreadRestrictions::ScopedAllowIO allow_io; - int posix_flags = 0; - posix_flags |= read_only ? O_RDONLY : O_RDWR; - if (!open_existing || mapped_file_ <= 0) - posix_flags |= O_CREAT; + FILE *fp; + bool fix_size = true; - if (!CreateOrOpen(name, posix_flags, size)) + FilePath path; + if (name.empty()) { + // It doesn't make sense to have a open-existing private piece of shmem + DCHECK(!open_existing); + // Q: Why not use the shm_open() etc. APIs? + // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU + fp = file_util::CreateAndOpenTemporaryShmemFile(&path); + + // Deleting the file prevents anyone else from mapping it in + // (making it private), and prevents the need for cleanup (once + // the last fd is closed, it is truly freed). + if (fp) + file_util::Delete(path, false); + + } else { + if (!FilePathForMemoryName(name, &path)) + return false; + + fp = file_util::OpenFile(path, "w+x"); + if (fp == NULL && open_existing) { + // "w+" will truncate if it already exists. + fp = file_util::OpenFile(path, "a+"); + fix_size = false; + } + } + if (fp && fix_size) { + // Get current size. + struct stat stat; + if (fstat(fileno(fp), &stat) != 0) + return false; + const uint32 current_size = stat.st_size; + if (current_size != size) { + if (ftruncate(fileno(fp), size) != 0) + return false; + if (fseeko(fp, size, SEEK_SET) != 0) + return false; + } + created_size_ = size; + } + if (fp == NULL) { +#if !defined(OS_MACOSX) + PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; + FilePath dir = path.DirName(); + if (access(dir.value().c_str(), W_OK | X_OK) < 0) { + PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value(); + if (dir.value() == "/dev/shm") { + LOG(FATAL) << "This is frequently caused by incorrect permissions on " + << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix."; + } + } +#else + PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; +#endif return false; + } - max_size_ = size; - return true; + return PrepareMapFile(fp); } // Our current implementation of shmem is with mmap()ing of files. @@ -110,12 +185,15 @@ bool SharedMemory::Delete(const std::string& name) { } bool SharedMemory::Open(const std::string& name, bool read_only) { - read_only_ = read_only; + FilePath path; + if (!FilePathForMemoryName(name, &path)) + return false; - int posix_flags = 0; - posix_flags |= read_only ? O_RDONLY : O_RDWR; + read_only_ = read_only; - return CreateOrOpen(name, posix_flags, 0); + const char *mode = read_only ? "r" : "r+"; + FILE *fp = file_util::OpenFile(path, mode); + return PrepareMapFile(fp); } // For the given shmem named |mem_name|, return a filename to mmap() @@ -136,92 +214,16 @@ bool SharedMemory::FilePathForMemoryName(const std::string& mem_name, return true; } -// Chromium mostly only use the unique/private shmem as specified by -// "name == L"". The exception is in the StatsTable. -// TODO(jrg): there is no way to "clean up" all unused named shmem if -// we restart from a crash. (That isn't a new problem, but it is a problem.) -// In case we want to delete it later, it may be useful to save the value -// of mem_filename after FilePathForMemoryName(). -bool SharedMemory::CreateOrOpen(const std::string& name, - int posix_flags, uint32 size) { +bool SharedMemory::PrepareMapFile(FILE *fp) { DCHECK(mapped_file_ == -1); + if (fp == NULL) return false; - file_util::ScopedFILE file_closer; - FILE *fp; - - FilePath path; - if (name.empty()) { - // It doesn't make sense to have a read-only private piece of shmem - DCHECK(posix_flags & (O_RDWR | O_WRONLY)); - - // Q: Why not use the shm_open() etc. APIs? - // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU - fp = file_util::CreateAndOpenTemporaryShmemFile(&path); - - // Deleting the file prevents anyone else from mapping it in - // (making it private), and prevents the need for cleanup (once - // the last fd is closed, it is truly freed). - if (fp) - file_util::Delete(path, false); - } else { - if (!FilePathForMemoryName(name, &path)) - return false; - - std::string mode; - switch (posix_flags) { - case (O_RDWR | O_CREAT): - // Careful: "w+" will truncate if it already exists. - mode = "a+"; - break; - case O_RDWR: - mode = "r+"; - break; - case O_RDONLY: - mode = "r"; - break; - default: - NOTIMPLEMENTED(); - break; - } - - fp = file_util::OpenFile(path, mode.c_str()); - } - - if (fp == NULL) { - if (posix_flags & O_CREAT) { -#if !defined(OS_MACOSX) - PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; - FilePath dir = path.DirName(); - if (access(dir.value().c_str(), W_OK | X_OK) < 0) { - PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value(); - if (dir.value() == "/dev/shm") { - LOG(FATAL) << "This is frequently caused by incorrect permissions on " - << "/dev/shm. Try 'sudo chmod 777 /dev/shm' to fix."; - } - } -#else - PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; -#endif - } - return false; - } - - file_closer.reset(fp); // close when we go out of scope + // This function theoretically can block on the disk, but realistically + // the temporary files we create will just go into the buffer cache + // and be deleted before they ever make it out to disk. + base::ThreadRestrictions::ScopedAllowIO allow_io; - // Make sure the (new) file is the right size. - if (size && (posix_flags & (O_RDWR | O_CREAT))) { - // Get current size. - struct stat stat; - if (fstat(fileno(fp), &stat) != 0) - return false; - const uint32 current_size = stat.st_size; - if (current_size != size) { - if (ftruncate(fileno(fp), size) != 0) - return false; - if (fseeko(fp, size, SEEK_SET) != 0) - return false; - } - } + file_util::ScopedFILE file_closer(fp); mapped_file_ = dup(fileno(fp)); if (mapped_file_ == -1) { @@ -249,7 +251,7 @@ bool SharedMemory::Map(uint32 bytes) { MAP_SHARED, mapped_file_, 0); if (memory_) - max_size_ = bytes; + mapped_size_ = bytes; bool mmap_succeeded = (memory_ != (void*)-1); DCHECK(mmap_succeeded) << "Call to mmap failed, errno=" << errno; @@ -260,9 +262,9 @@ bool SharedMemory::Unmap() { if (memory_ == NULL) return false; - munmap(memory_, max_size_); + munmap(memory_, mapped_size_); memory_ = NULL; - max_size_ = 0; + mapped_size_ = 0; return true; } diff --git a/base/shared_memory_unittest.cc b/base/shared_memory_unittest.cc index 53bffb4..f646158 100644 --- a/base/shared_memory_unittest.cc +++ b/base/shared_memory_unittest.cc @@ -8,6 +8,7 @@ #include "base/shared_memory.h" #include "base/scoped_ptr.h" #include "base/test/multiprocess_test.h" +#include "base/time.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/multiprocess_func_list.h" @@ -36,7 +37,7 @@ class MultipleThreadMain : public PlatformThread::Delegate { mac::ScopedNSAutoreleasePool pool; // noop if not OSX const uint32 kDataSize = 1024; SharedMemory memory; - bool rv = memory.Create(s_test_name_, false, true, kDataSize); + bool rv = memory.CreateNamed(s_test_name_, true, kDataSize); EXPECT_TRUE(rv); rv = memory.Map(kDataSize); EXPECT_TRUE(rv); @@ -82,8 +83,8 @@ class MultipleLockThread : public PlatformThread::Delegate { SharedMemoryHandle handle = NULL; { SharedMemory memory1; - EXPECT_TRUE(memory1.Create("SharedMemoryMultipleLockThreadTest", - false, true, kDataSize)); + EXPECT_TRUE(memory1.CreateNamed("SharedMemoryMultipleLockThreadTest", + true, kDataSize)); EXPECT_TRUE(memory1.ShareToProcess(GetCurrentProcess(), &handle)); // TODO(paulg): Implement this once we have a posix version of // SharedMemory::ShareToProcess. @@ -128,7 +129,7 @@ TEST(SharedMemoryTest, OpenClose) { EXPECT_TRUE(rv); rv = memory1.Open(test_name, false); EXPECT_FALSE(rv); - rv = memory1.Create(test_name, false, false, kDataSize); + rv = memory1.CreateNamed(test_name, false, kDataSize); EXPECT_TRUE(rv); rv = memory1.Map(kDataSize); EXPECT_TRUE(rv); @@ -163,6 +164,58 @@ TEST(SharedMemoryTest, OpenClose) { EXPECT_TRUE(rv); } +TEST(SharedMemoryTest, OpenExclusive) { + const uint32 kDataSize = 1024; + const uint32 kDataSize2 = 2048; + std::ostringstream test_name_stream; + test_name_stream << "SharedMemoryOpenExclusiveTest." + << Time::Now().ToDoubleT(); + std::string test_name = test_name_stream.str(); + + // Open two handles to a memory segment and check that open_existing works + // as expected. + SharedMemory memory1; + bool rv = memory1.CreateNamed(test_name, false, kDataSize); + EXPECT_TRUE(rv); + + // Memory1 knows it's size because it created it. + EXPECT_EQ(memory1.created_size(), kDataSize); + + rv = memory1.Map(kDataSize); + EXPECT_TRUE(rv); + + memset(memory1.memory(), 'G', kDataSize); + + SharedMemory memory2; + // Should not be able to create if openExisting is false. + rv = memory2.CreateNamed(test_name, false, kDataSize2); + EXPECT_FALSE(rv); + + // Should be able to create with openExisting true. + rv = memory2.CreateNamed(test_name, true, kDataSize2); + EXPECT_TRUE(rv); + + // Memory2 shouldn't know the size because we didn't create it. + EXPECT_EQ(memory2.created_size(), 0U); + + // We should be able to map the original size. + rv = memory2.Map(kDataSize); + EXPECT_TRUE(rv); + + // Verify that opening memory2 didn't truncate or delete memory 1. + char *start_ptr = static_cast<char *>(memory2.memory()); + char *end_ptr = start_ptr + kDataSize; + for (char* ptr = start_ptr; ptr < end_ptr; ptr++) { + EXPECT_EQ(*ptr, 'G'); + } + + memory1.Close(); + memory2.Close(); + + rv = memory1.Delete(test_name); + EXPECT_TRUE(rv); +} + // Create a set of N threads to each open a shared memory segment and write to // it. Verify that they are always reading/writing consistent data. TEST(SharedMemoryTest, MultipleThreads) { @@ -243,9 +296,7 @@ TEST(SharedMemoryTest, AnonymousPrivate) { ASSERT_TRUE(pointers.get()); for (i = 0; i < count; i++) { - rv = memories[i].Create("", false, true, kDataSize); - EXPECT_TRUE(rv); - rv = memories[i].Map(kDataSize); + rv = memories[i].CreateAndMapAnonymous(kDataSize); EXPECT_TRUE(rv); int *ptr = static_cast<int*>(memories[i].memory()); EXPECT_TRUE(ptr); @@ -289,7 +340,7 @@ class SharedMemoryProcessTest : public base::MultiProcessTest { mac::ScopedNSAutoreleasePool pool; // noop if not OSX const uint32 kDataSize = 1024; SharedMemory memory; - bool rv = memory.Create(s_test_name_, false, true, kDataSize); + bool rv = memory.CreateNamed(s_test_name_, true, kDataSize); EXPECT_TRUE(rv); if (rv != true) errors++; @@ -320,7 +371,13 @@ class SharedMemoryProcessTest : public base::MultiProcessTest { const char* const SharedMemoryProcessTest::s_test_name_ = "MPMem"; -TEST_F(SharedMemoryProcessTest, Tasks) { +#if defined(OS_MACOSX) +#define MAYBE_Tasks FLAKY_Tasks +#else +#define MAYBE_Tasks Tasks +#endif + +TEST_F(SharedMemoryProcessTest, MAYBE_Tasks) { SharedMemoryProcessTest::CleanUp(); base::ProcessHandle handles[kNumTasks]; diff --git a/base/shared_memory_win.cc b/base/shared_memory_win.cc index f35ada1..5f293fc 100644 --- a/base/shared_memory_win.cc +++ b/base/shared_memory_win.cc @@ -13,15 +13,24 @@ SharedMemory::SharedMemory() : mapped_file_(NULL), memory_(NULL), read_only_(false), - max_size_(0), + created_size_(0), lock_(NULL) { } +SharedMemory::SharedMemory(const std::wstring& name) + : mapped_file_(NULL), + memory_(NULL), + read_only_(false), + created_size_(0), + lock_(NULL), + name_(name) { +} + SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only) : mapped_file_(handle), memory_(NULL), read_only_(read_only), - max_size_(0), + created_size_(0), lock_(NULL) { } @@ -30,7 +39,7 @@ SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only, : mapped_file_(NULL), memory_(NULL), read_only_(read_only), - max_size_(0), + created_size_(0), lock_(NULL) { ::DuplicateHandle(process, handle, GetCurrentProcess(), &mapped_file_, @@ -61,9 +70,19 @@ void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) { ::CloseHandle(handle); } -bool SharedMemory::Create(const std::string& name, bool read_only, - bool open_existing, uint32 size) { +bool SharedMemory::CreateAndMapAnonymous(uint32 size) { + return CreateAnonymous(size) && Map(size); +} + +bool SharedMemory::CreateAnonymous(uint32 size) { + return CreateNamed("", false, size); +} + +bool SharedMemory::CreateNamed(const std::string& name, + bool open_existing, uint32 size) { DCHECK(mapped_file_ == NULL); + if (size == 0) + return false; // NaCl's memory allocator requires 0mod64K alignment and size for // shared memory objects. To allow passing shared memory to NaCl, @@ -72,20 +91,25 @@ bool SharedMemory::Create(const std::string& name, bool read_only, // actual requested size. uint32 rounded_size = (size + 0xffff) & ~0xffff; name_ = ASCIIToWide(name); - read_only_ = read_only; mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, - read_only_ ? PAGE_READONLY : PAGE_READWRITE, 0, - static_cast<DWORD>(rounded_size), + PAGE_READWRITE, 0, static_cast<DWORD>(rounded_size), name_.empty() ? NULL : name_.c_str()); if (!mapped_file_) return false; + created_size_ = size; + // Check if the shared memory pre-exists. - if (GetLastError() == ERROR_ALREADY_EXISTS && !open_existing) { - Close(); - return false; + if (GetLastError() == ERROR_ALREADY_EXISTS) { + // If the file already existed, set created_size_ to 0 to show that + // we don't know the size. + created_size_ = 0; + if (!open_existing) { + Close(); + return false; + } } - max_size_ = size; + return true; } @@ -173,6 +197,10 @@ void SharedMemory::Close() { } void SharedMemory::Lock() { + Lock(INFINITE); +} + +bool SharedMemory::Lock(uint32 timeout_ms) { if (lock_ == NULL) { std::wstring name = name_; name.append(L"lock"); @@ -180,10 +208,13 @@ void SharedMemory::Lock() { DCHECK(lock_ != NULL); if (lock_ == NULL) { DLOG(ERROR) << "Could not create mutex" << GetLastError(); - return; // there is nothing good we can do here. + return false; // there is nothing good we can do here. } } - WaitForSingleObject(lock_, INFINITE); + DWORD result = WaitForSingleObject(lock_, timeout_ms); + + // Return false for WAIT_ABANDONED, WAIT_TIMEOUT or WAIT_FAILED. + return (result == WAIT_OBJECT_0); } void SharedMemory::Unlock() { diff --git a/base/singleton.h b/base/singleton.h index ccb2c7d..3fe16ce 100644 --- a/base/singleton.h +++ b/base/singleton.h @@ -8,7 +8,6 @@ #include "base/at_exit.h" #include "base/atomicops.h" -#include "base/platform_thread.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" // Default traits for Singleton<Type>. Calls operator new and operator delete on diff --git a/base/string_number_conversions.cc b/base/string_number_conversions.cc index 9cce9cc..7677a86 100644 --- a/base/string_number_conversions.cc +++ b/base/string_number_conversions.cc @@ -143,7 +143,7 @@ template<typename CHAR> class WhitespaceHelper { template<> class WhitespaceHelper<char> { public: static bool Invoke(char c) { - return 0 != isspace(c); + return 0 != isspace(static_cast<unsigned char>(c)); } }; diff --git a/base/string_number_conversions_unittest.cc b/base/string_number_conversions_unittest.cc index 55487df..8cdd77d 100644 --- a/base/string_number_conversions_unittest.cc +++ b/base/string_number_conversions_unittest.cc @@ -80,6 +80,8 @@ TEST(StringNumberConversionsTest, StringToInt) { } cases[] = { {"0", 0, true}, {"42", 42, true}, + {"42\x99", 42, false}, + {"\x99" "42\x99", 0, false}, {"-2147483648", INT_MIN, true}, {"2147483647", INT_MAX, true}, {"", 0, false}, @@ -161,6 +163,11 @@ TEST(StringNumberConversionsTest, StringToInt) { utf16_chars + utf16_input.length(), &output)); EXPECT_EQ(6, output); + + output = 0; + const char16 negative_wide_input[] = { 0xFF4D, '4', '2', 0}; + EXPECT_FALSE(StringToInt(string16(negative_wide_input), &output)); + EXPECT_EQ(0, output); } TEST(StringNumberConversionsTest, StringToInt64) { diff --git a/base/string_util.cc b/base/string_util.cc index ece5ddb..6717ca5 100644 --- a/base/string_util.cc +++ b/base/string_util.cc @@ -484,7 +484,7 @@ static inline bool DoLowerCaseEqualsASCII(Iter a_begin, Iter a_end, const char* b) { for (Iter it = a_begin; it != a_end; ++it, ++b) { - if (!*b || ToLowerASCII(*it) != *b) + if (!*b || base::ToLowerASCII(*it) != *b) return false; } return *b == 0; @@ -572,7 +572,7 @@ bool StartsWithT(const STR& str, const STR& search, bool case_sensitive) { if (search.size() > str.size()) return false; return std::equal(search.begin(), search.end(), str.begin(), - CaseInsensitiveCompare<typename STR::value_type>()); + base::CaseInsensitiveCompare<typename STR::value_type>()); } } @@ -599,7 +599,7 @@ bool EndsWithT(const STR& str, const STR& search, bool case_sensitive) { } else { return std::equal(search.begin(), search.end(), str.begin() + (str_length - search_length), - CaseInsensitiveCompare<typename STR::value_type>()); + base::CaseInsensitiveCompare<typename STR::value_type>()); } } diff --git a/base/string_util.h b/base/string_util.h index 27de472..c238e4a 100644 --- a/base/string_util.h +++ b/base/string_util.h @@ -116,6 +116,30 @@ size_t wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size); // This function is intended to be called from base::vswprintf. bool IsWprintfFormatPortable(const wchar_t* format); +// ASCII-specific tolower. The standard library's tolower is locale sensitive, +// so we don't want to use it here. +template <class Char> inline Char ToLowerASCII(Char c) { + return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c; +} + +// Function objects to aid in comparing/searching strings. + +template<typename Char> struct CaseInsensitiveCompare { + public: + bool operator()(Char x, Char y) const { + // TODO(darin): Do we really want to do locale sensitive comparisons here? + // See http://crbug.com/24917 + return tolower(x) == tolower(y); + } +}; + +template<typename Char> struct CaseInsensitiveCompareASCII { + public: + bool operator()(Char x, Char y) const { + return ToLowerASCII(x) == ToLowerASCII(y); + } +}; + } // namespace base #if defined(OS_WIN) @@ -257,17 +281,11 @@ bool IsStringASCII(const std::wstring& str); bool IsStringASCII(const base::StringPiece& str); bool IsStringASCII(const string16& str); -// ASCII-specific tolower. The standard library's tolower is locale sensitive, -// so we don't want to use it here. -template <class Char> inline Char ToLowerASCII(Char c) { - return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c; -} - // Converts the elements of the given string. This version uses a pointer to // clearly differentiate it from the non-pointer variant. template <class str> inline void StringToLowerASCII(str* s) { for (typename str::iterator i = s->begin(); i != s->end(); ++i) - *i = ToLowerASCII(*i); + *i = base::ToLowerASCII(*i); } template <class str> inline str StringToLowerASCII(const str& s) { @@ -468,23 +486,6 @@ inline typename string_type::value_type* WriteInto(string_type* str, //----------------------------------------------------------------------------- -// Function objects to aid in comparing/searching strings. - -template<typename Char> struct CaseInsensitiveCompare { - public: - bool operator()(Char x, Char y) const { - // TODO(darin): Do we really want to do locale sensitive comparisons here? - // See http://crbug.com/24917 - return tolower(x) == tolower(y); - } -}; - -template<typename Char> struct CaseInsensitiveCompareASCII { - public: - bool operator()(Char x, Char y) const { - return ToLowerASCII(x) == ToLowerASCII(y); - } -}; // Splits a string into its fields delimited by any of the characters in // |delimiters|. Each field is added to the |tokens| vector. Returns the diff --git a/base/stringprintf.h b/base/stringprintf.h index a502a2f..3608f9d 100644 --- a/base/stringprintf.h +++ b/base/stringprintf.h @@ -49,8 +49,6 @@ void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) // // TODO(brettw) remove these when calling code is converted. using base::StringPrintf; -using base::StringPrintV; -using base::SStringPrintf; using base::StringAppendV; using base::StringAppendF; using base::StringAppendV; diff --git a/base/sys_info_chromeos.cc b/base/sys_info_chromeos.cc index 1cd9fb6..e554d73 100644 --- a/base/sys_info_chromeos.cc +++ b/base/sys_info_chromeos.cc @@ -9,6 +9,7 @@ #include "base/file_util.h" #include "base/string_number_conversions.h" #include "base/string_tokenizer.h" +#include "base/thread_restrictions.h" namespace base { @@ -24,6 +25,12 @@ const char kLinuxStandardBaseReleaseFile[] = "/etc/lsb-release"; void SysInfo::OperatingSystemVersionNumbers(int32 *major_version, int32 *minor_version, int32 *bugfix_version) { + // The other implementations of SysInfo don't block on the disk. + // See http://code.google.com/p/chromium/issues/detail?id=60394 + // Perhaps the caller ought to cache this? + // Temporary allowing while we work the bug out. + base::ThreadRestrictions::ScopedAllowIO allow_io; + // TODO(cmasone): If this gets called a lot, it may kill performance. // consider using static variables to cache these values? FilePath path(kLinuxStandardBaseReleaseFile); @@ -54,12 +61,18 @@ void SysInfo::ParseLsbRelease(const std::string& lsb_release, StringTokenizer tokenizer(version, "."); for (int i = 0; i < 3 && tokenizer.GetNext(); i++) { if (0 == i) { - StringToInt(tokenizer.token(), major_version); + StringToInt(tokenizer.token_begin(), + tokenizer.token_end(), + major_version); *minor_version = *bugfix_version = 0; } else if (1 == i) { - StringToInt(tokenizer.token(), minor_version); + StringToInt(tokenizer.token_begin(), + tokenizer.token_end(), + minor_version); } else { // 2 == i - StringToInt(tokenizer.token(), bugfix_version); + StringToInt(tokenizer.token_begin(), + tokenizer.token_end(), + bugfix_version); } } } diff --git a/base/task.h b/base/task.h index e6ac33c..28d15fc 100644 --- a/base/task.h +++ b/base/task.h @@ -180,7 +180,7 @@ class ScopedRunnableMethodFactory { template<class T> class DeleteTask : public CancelableTask { public: - explicit DeleteTask(T* obj) : obj_(obj) { + explicit DeleteTask(const T* obj) : obj_(obj) { } virtual void Run() { delete obj_; @@ -190,14 +190,14 @@ class DeleteTask : public CancelableTask { } private: - T* obj_; + const T* obj_; }; // Task to Release() an object template<class T> class ReleaseTask : public CancelableTask { public: - explicit ReleaseTask(T* obj) : obj_(obj) { + explicit ReleaseTask(const T* obj) : obj_(obj) { } virtual void Run() { if (obj_) @@ -208,7 +208,7 @@ class ReleaseTask : public CancelableTask { } private: - T* obj_; + const T* obj_; }; // RunnableMethodTraits -------------------------------------------------------- diff --git a/base/test/perf_test_suite.cc b/base/test/perf_test_suite.cc index b787add..232766a 100644 --- a/base/test/perf_test_suite.cc +++ b/base/test/perf_test_suite.cc @@ -5,7 +5,7 @@ #include "base/test/perf_test_suite.h" #include "base/command_line.h" -#include "base/debug_util.h" +#include "base/debug/debugger.h" #include "base/file_path.h" #include "base/path_service.h" #include "base/perftimer.h" @@ -34,7 +34,7 @@ void PerfTestSuite::Initialize() { // Raise to high priority to have more precise measurements. Since we don't // aim at 1% precision, it is not necessary to run at realtime level. - if (!DebugUtil::BeingDebugged()) + if (!base::debug::BeingDebugged()) base::RaiseProcessToHighPriority(); } diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc index f2e6069..7d25a60 100644 --- a/base/test/test_suite.cc +++ b/base/test/test_suite.cc @@ -10,6 +10,8 @@ #include "base/command_line.h" #include "base/debug_on_start.h" #include "base/debug_util.h" +#include "base/debug/debugger.h" +#include "base/debug/debugger.h" #include "base/file_path.h" #include "base/i18n/icu_util.h" #include "base/logging.h" @@ -191,7 +193,7 @@ void TestSuite::Initialize() { #endif // defined(OS_WIN) // In some cases, we do not want to see standard error dialogs. - if (!DebugUtil::BeingDebugged() && + if (!base::debug::BeingDebugged() && !CommandLine::ForCurrentProcess()->HasSwitch("show-error-dialogs")) { SuppressErrorDialogs(); DebugUtil::SuppressDialogs(); diff --git a/base/thread_restrictions.cc b/base/thread_restrictions.cc index 1ee8eee..270d663 100644 --- a/base/thread_restrictions.cc +++ b/base/thread_restrictions.cc @@ -21,8 +21,10 @@ LazyInstance<ThreadLocalBoolean, LeakyLazyInstanceTraits<ThreadLocalBoolean> > } // anonymous namespace // static -void ThreadRestrictions::SetIOAllowed(bool allowed) { +bool ThreadRestrictions::SetIOAllowed(bool allowed) { + bool previous_allowed = g_io_disallowed.Get().Get(); g_io_disallowed.Get().Set(!allowed); + return !previous_allowed; } // static diff --git a/base/thread_restrictions.h b/base/thread_restrictions.h index 4aa2cd6..a10aaec 100644 --- a/base/thread_restrictions.h +++ b/base/thread_restrictions.h @@ -5,6 +5,8 @@ #ifndef BASE_THREAD_RESTRICTIONS_H_ #define BASE_THREAD_RESTRICTIONS_H_ +#include "base/basictypes.h" + namespace base { // ThreadRestrictions helps protect threads that should not block from @@ -21,27 +23,49 @@ namespace base { // // ThreadRestrictions does nothing in release builds; it is debug-only. // +// Style tip: where should you put AssertIOAllowed checks? It's best +// if you put them as close to the disk access as possible, at the +// lowest level. This rule is simple to follow and helps catch all +// callers. For example, if your function GoDoSomeBlockingDiskCall() +// only calls other functions in Chrome and not fopen(), you should go +// add the AssertIOAllowed checks in the helper functions. + class ThreadRestrictions { public: + // Constructing a ScopedAllowIO temporarily allows IO for the current + // thread. Doing this is almost certainly always incorrect. + class ScopedAllowIO { + public: + ScopedAllowIO() { previous_value_ = SetIOAllowed(true); } + ~ScopedAllowIO() { SetIOAllowed(previous_value_); } + private: + // Whether IO is allowed when the ScopedAllowIO was constructed. + bool previous_value_; + + DISALLOW_COPY_AND_ASSIGN(ScopedAllowIO); + }; + +#ifndef NDEBUG // Set whether the current thread to make IO calls. // Threads start out in the *allowed* state. - static void SetIOAllowed(bool allowed); + // Returns the previous value. + static bool SetIOAllowed(bool allowed); // Check whether the current thread is allowed to make IO calls, - // and DCHECK if not. + // and DCHECK if not. See the block comment above the class for + // a discussion of where to add these checks. static void AssertIOAllowed(); +#else + // In Release builds, inline the empty definitions of these functions so + // that they can be compiled out. + static bool SetIOAllowed(bool allowed) { return true; } + static void AssertIOAllowed() {} +#endif private: ThreadRestrictions(); // class for namespacing only }; -// In Release builds, inline the empty definitions of these functions so -// that they can be compiled out. -#ifdef NDEBUG -void ThreadRestrictions::SetIOAllowed(bool allowed) {} -void ThreadRestrictions::AssertIOAllowed() {} -#endif - } // namespace base #endif // BASE_THREAD_RESTRICTIONS_H_ diff --git a/base/tracked.cc b/base/tracked.cc index 8ae7447..767f072 100644 --- a/base/tracked.cc +++ b/base/tracked.cc @@ -65,6 +65,10 @@ void Location::WriteFunctionName(std::string* output) const { Tracked::Tracked() {} Tracked::~Tracked() {} void Tracked::SetBirthPlace(const Location& from_here) {} +const Location Tracked::GetBirthPlace() const { + static Location kNone("NoFunctionName", "NeedToSetBirthPlace", -1); + return kNone; +} bool Tracked::MissingBirthplace() const { return false; } void Tracked::ResetBirthTime() {} @@ -96,6 +100,10 @@ void Tracked::SetBirthPlace(const Location& from_here) { tracked_births_ = current_thread_data->TallyABirth(from_here); } +const Location Tracked::GetBirthPlace() const { + return tracked_births_->location(); +} + void Tracked::ResetBirthTime() { tracked_birth_time_ = TimeTicks::Now(); } diff --git a/base/tracked.h b/base/tracked.h index 7af05ff..b4f6e36 100644 --- a/base/tracked.h +++ b/base/tracked.h @@ -94,6 +94,7 @@ class Tracked { // Used to record the FROM_HERE location of a caller. void SetBirthPlace(const Location& from_here); + const Location GetBirthPlace() const; // When a task sits around a long time, such as in a timer, or object watcher, // this method should be called when the task becomes active, and its diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc index 4831e4a..4631f34 100644 --- a/base/tracked_objects.cc +++ b/base/tracked_objects.cc @@ -156,7 +156,7 @@ void ThreadData::WriteHTML(const std::string& query, std::string* output) { DCHECK(ThreadData::current()); - output->append("<html><head><title>About Objects"); + output->append("<html><head><title>About Tasks"); std::string escaped_query = UnescapeQuery(query); if (!escaped_query.empty()) output->append(" - " + escaped_query); diff --git a/base/tracked_objects.h b/base/tracked_objects.h index b76a295..87912c0 100644 --- a/base/tracked_objects.h +++ b/base/tracked_objects.h @@ -21,18 +21,18 @@ // across a series of objects so that the counts and times can be rapidly // updated without (usually) having to lock the data, and hence there is usually // very little contention caused by the tracking. The data can be viewed via -// the about:objects URL, with a variety of sorting and filtering choices. +// the about:tasks URL, with a variety of sorting and filtering choices. // -// Theese classes serve as the basis of a profiler of sorts for the Tasks -// system. As a result, design decisions were made to maximize speed, by -// minimizing recurring allocation/deallocation, lock contention and data -// copying. In the "stable" state, which is reached relatively quickly, there -// is no separate marginal allocation cost associated with construction or -// destruction of tracked objects, no locks are generally employed, and probably -// the largest computational cost is associated with obtaining start and stop -// times for instances as they are created and destroyed. The introduction of -// worker threads had a slight impact on this approach, and required use of some -// locks when accessing data from the worker threads. +// These classes serve as the basis of a profiler of sorts for the Tasks system. +// As a result, design decisions were made to maximize speed, by minimizing +// recurring allocation/deallocation, lock contention and data copying. In the +// "stable" state, which is reached relatively quickly, there is no separate +// marginal allocation cost associated with construction or destruction of +// tracked objects, no locks are generally employed, and probably the largest +// computational cost is associated with obtaining start and stop times for +// instances as they are created and destroyed. The introduction of worker +// threads had a slight impact on this approach, and required use of some locks +// when accessing data from the worker threads. // // The following describes the lifecycle of tracking an instance. // @@ -111,7 +111,7 @@ // // The above description tries to define the high performance (run time) // portions of these classes. After gathering statistics, calls instigated -// by visiting about:objects will assemble and aggregate data for display. The +// by visiting about:tasks will assemble and aggregate data for display. The // following data structures are used for producing such displays. They are // not performance critical, and their only major constraint is that they should // be able to run concurrently with ongoing augmentation of the birth and death @@ -137,8 +137,7 @@ // need to be sorted, and possibly aggregated (example: how many threads are in // a specific consecutive set of Snapshots? What was the total birth count for // that set? etc.). Aggregation instances collect running sums of any set of -// snapshot instances, and are used to print sub-totals in an about:objects -// page. +// snapshot instances, and are used to print sub-totals in an about:tasks page. // // TODO(jar): I need to store DataCollections, and provide facilities for taking // the difference between two gathered DataCollections. For now, I'm just diff --git a/base/vlog.cc b/base/vlog.cc index 6075b0b..cda9cea 100644 --- a/base/vlog.cc +++ b/base/vlog.cc @@ -5,22 +5,44 @@ #include "base/vlog.h" #include "base/basictypes.h" +#include "base/logging.h" #include "base/string_number_conversions.h" #include "base/string_split.h" -#include "base/string_util.h" namespace logging { const int VlogInfo::kDefaultVlogLevel = 0; +VlogInfo::VmodulePattern::VmodulePattern(const std::string& pattern) + : pattern(pattern), + vlog_level(VlogInfo::kDefaultVlogLevel), + match_target(MATCH_MODULE) { + // If the pattern contains a {forward,back} slash, we assume that + // it's meant to be tested against the entire __FILE__ string. + std::string::size_type first_slash = pattern.find_first_of("\\/"); + if (first_slash != std::string::npos) + match_target = MATCH_FILE; +} + +VlogInfo::VmodulePattern::VmodulePattern() + : vlog_level(VlogInfo::kDefaultVlogLevel), + match_target(MATCH_MODULE) {} + VlogInfo::VlogInfo(const std::string& v_switch, - const std::string& vmodule_switch) - : max_vlog_level_(kDefaultVlogLevel) { + const std::string& vmodule_switch, + int* min_log_level) + : min_log_level_(min_log_level) { + DCHECK(min_log_level != NULL); + typedef std::pair<std::string, std::string> KVPair; - if (!base::StringToInt(v_switch, &max_vlog_level_)) { + int vlog_level = 0; + if (base::StringToInt(v_switch, &vlog_level)) { + SetMaxVlogLevel(vlog_level); + } else { LOG(WARNING) << "Parsed v switch \"" - << v_switch << "\" as " << max_vlog_level_; + << v_switch << "\" as " << vlog_level; } + std::vector<KVPair> kv_pairs; if (!base::SplitStringIntoKeyValuePairs( vmodule_switch, '=', ',', &kv_pairs)) { @@ -29,43 +51,114 @@ VlogInfo::VlogInfo(const std::string& v_switch, } for (std::vector<KVPair>::const_iterator it = kv_pairs.begin(); it != kv_pairs.end(); ++it) { - int vlog_level = kDefaultVlogLevel; - if (!base::StringToInt(it->second, &vlog_level)) { + VmodulePattern pattern(it->first); + if (!base::StringToInt(it->second, &pattern.vlog_level)) { LOG(WARNING) << "Parsed vlog level for \"" << it->first << "=" << it->second - << "\" as " << vlog_level; + << "\" as " << pattern.vlog_level; } - vmodule_levels_.push_back(std::make_pair(it->first, vlog_level)); + vmodule_levels_.push_back(pattern); } } VlogInfo::~VlogInfo() {} -int VlogInfo::GetVlogLevel(const base::StringPiece& file) { +void VlogInfo::SetMaxVlogLevel(int level) { + // Log severity is the negative verbosity. + *min_log_level_ = -level; +} + +int VlogInfo::GetMaxVlogLevel() const { + return -*min_log_level_; +} + +namespace { + +// Given a path, returns the basename with the extension chopped off +// (and any -inl suffix). We avoid using FilePath to minimize the +// number of dependencies the logging system has. +base::StringPiece GetModule(const base::StringPiece& file) { + base::StringPiece module(file); + base::StringPiece::size_type last_slash_pos = + module.find_last_of("\\/"); + if (last_slash_pos != base::StringPiece::npos) + module.remove_prefix(last_slash_pos + 1); + base::StringPiece::size_type extension_start = module.rfind('.'); + module = module.substr(0, extension_start); + static const char kInlSuffix[] = "-inl"; + static const int kInlSuffixLen = arraysize(kInlSuffix) - 1; + if (module.ends_with(kInlSuffix)) + module.remove_suffix(kInlSuffixLen); + return module; +} + +} // namespace + +int VlogInfo::GetVlogLevel(const base::StringPiece& file) const { if (!vmodule_levels_.empty()) { - base::StringPiece module(file); - base::StringPiece::size_type last_slash_pos = - module.find_last_of("\\/"); - if (last_slash_pos != base::StringPiece::npos) { - module.remove_prefix(last_slash_pos + 1); - } - base::StringPiece::size_type extension_start = module.find('.'); - module = module.substr(0, extension_start); - static const char kInlSuffix[] = "-inl"; - static const int kInlSuffixLen = arraysize(kInlSuffix) - 1; - if (module.ends_with(kInlSuffix)) { - module.remove_suffix(kInlSuffixLen); - } + base::StringPiece module(GetModule(file)); for (std::vector<VmodulePattern>::const_iterator it = vmodule_levels_.begin(); it != vmodule_levels_.end(); ++it) { - // TODO(akalin): Use a less-heavyweight version of MatchPattern - // (we can pretty much assume we're dealing with ASCII). - if (MatchPattern(module, it->first)) { - return it->second; - } + base::StringPiece target( + (it->match_target == VmodulePattern::MATCH_FILE) ? file : module); + if (MatchVlogPattern(target, it->pattern)) + return it->vlog_level; + } + } + return GetMaxVlogLevel(); +} + +bool MatchVlogPattern(const base::StringPiece& string, + const base::StringPiece& vlog_pattern) { + base::StringPiece p(vlog_pattern); + base::StringPiece s(string); + // Consume characters until the next star. + while (!p.empty() && !s.empty() && (p[0] != '*')) { + switch (p[0]) { + // A slash (forward or back) must match a slash (forward or back). + case '/': + case '\\': + if ((s[0] != '/') && (s[0] != '\\')) + return false; + break; + + // A '?' matches anything. + case '?': + break; + + // Anything else must match literally. + default: + if (p[0] != s[0]) + return false; + break; } + p.remove_prefix(1), s.remove_prefix(1); } - return max_vlog_level_; + + // An empty pattern here matches only an empty string. + if (p.empty()) + return s.empty(); + + // Coalesce runs of consecutive stars. There should be at least + // one. + while (!p.empty() && (p[0] == '*')) + p.remove_prefix(1); + + // Since we moved past the stars, an empty pattern here matches + // anything. + if (p.empty()) + return true; + + // Since we moved past the stars and p is non-empty, if some + // non-empty substring of s matches p, then we ourselves match. + while (!s.empty()) { + if (MatchVlogPattern(s, p)) + return true; + s.remove_prefix(1); + } + + // Otherwise, we couldn't find a match. + return false; } } // namespace diff --git a/base/vlog.h b/base/vlog.h index d4cffe4..529afd5 100644 --- a/base/vlog.h +++ b/base/vlog.h @@ -8,7 +8,6 @@ #include <cstddef> #include <string> -#include <utility> #include <vector> #include "base/basictypes.h" @@ -28,25 +27,63 @@ class VlogInfo { // E.g. "my_module=2,foo*=3" would change the logging level for all // code in source files "my_module.*" and "foo*.*" ("-inl" suffixes // are also disregarded for this matching). + // + // |log_severity| points to an int that stores the log level. If a valid + // |v_switch| is provided, it will set the log level, and the default + // vlog severity will be read from there.. + // + // Any pattern containing a forward or backward slash will be tested + // against the whole pathname and not just the module. E.g., + // "*/foo/bar/*=2" would change the logging level for all code in + // source files under a "foo/bar" directory. VlogInfo(const std::string& v_switch, - const std::string& vmodule_switch); + const std::string& vmodule_switch, + int* min_log_level); ~VlogInfo(); // Returns the vlog level for a given file (usually taken from // __FILE__). - int GetVlogLevel(const base::StringPiece& file); + int GetVlogLevel(const base::StringPiece& file) const; static const int kDefaultVlogLevel; private: - typedef std::pair<std::string, int> VmodulePattern; + void SetMaxVlogLevel(int level); + int GetMaxVlogLevel() const; + + // VmodulePattern holds all the information for each pattern parsed + // from |vmodule_switch|. + struct VmodulePattern { + enum MatchTarget { MATCH_MODULE, MATCH_FILE }; + + explicit VmodulePattern(const std::string& pattern); + + VmodulePattern(); + + std::string pattern; + int vlog_level; + MatchTarget match_target; + }; - int max_vlog_level_; std::vector<VmodulePattern> vmodule_levels_; + int* min_log_level_; DISALLOW_COPY_AND_ASSIGN(VlogInfo); }; +// Returns true if the string passed in matches the vlog pattern. The +// vlog pattern string can contain wildcards like * and ?. ? matches +// exactly one character while * matches 0 or more characters. Also, +// as a special case, a / or \ character matches either / or \. +// +// Examples: +// "kh?n" matches "khan" but not "khn" or "khaan" +// "kh*n" matches "khn", "khan", or even "khaaaaan" +// "/foo\bar" matches "/foo/bar", "\foo\bar", or "/foo\bar" +// (disregarding C escaping rules) +bool MatchVlogPattern(const base::StringPiece& string, + const base::StringPiece& vlog_pattern); + } // namespace logging #endif // BASE_VLOG_H_ diff --git a/base/vlog_unittest.cc b/base/vlog_unittest.cc index 95aa1b9..f583f44 100644 --- a/base/vlog_unittest.cc +++ b/base/vlog_unittest.cc @@ -18,19 +18,69 @@ class VlogTest : public testing::Test { }; TEST_F(VlogTest, NoVmodule) { - EXPECT_EQ(0, VlogInfo("", "").GetVlogLevel("test1")); - EXPECT_EQ(0, VlogInfo("0", "").GetVlogLevel("test2")); - EXPECT_EQ(0, VlogInfo("blah", "").GetVlogLevel("test3")); - EXPECT_EQ(0, VlogInfo("0blah1", "").GetVlogLevel("test4")); - EXPECT_EQ(1, VlogInfo("1", "").GetVlogLevel("test5")); - EXPECT_EQ(5, VlogInfo("5", "").GetVlogLevel("test6")); + int min_log_level = 0; + EXPECT_EQ(0, VlogInfo("", "", &min_log_level).GetVlogLevel("test1")); + EXPECT_EQ(0, VlogInfo("0", "", &min_log_level).GetVlogLevel("test2")); + EXPECT_EQ(0, VlogInfo("blah", "", &min_log_level).GetVlogLevel("test3")); + EXPECT_EQ(0, VlogInfo("0blah1", "", &min_log_level).GetVlogLevel("test4")); + EXPECT_EQ(1, VlogInfo("1", "", &min_log_level).GetVlogLevel("test5")); + EXPECT_EQ(5, VlogInfo("5", "", &min_log_level).GetVlogLevel("test6")); } -TEST_F(VlogTest, Vmodule) { +TEST_F(VlogTest, MatchVlogPattern) { + // Degenerate cases. + EXPECT_TRUE(MatchVlogPattern("", "")); + EXPECT_TRUE(MatchVlogPattern("", "****")); + EXPECT_FALSE(MatchVlogPattern("", "x")); + EXPECT_FALSE(MatchVlogPattern("x", "")); + + // Basic. + EXPECT_TRUE(MatchVlogPattern("blah", "blah")); + + // ? should match exactly one character. + EXPECT_TRUE(MatchVlogPattern("blah", "bl?h")); + EXPECT_FALSE(MatchVlogPattern("blh", "bl?h")); + EXPECT_FALSE(MatchVlogPattern("blaah", "bl?h")); + EXPECT_TRUE(MatchVlogPattern("blah", "?lah")); + EXPECT_FALSE(MatchVlogPattern("lah", "?lah")); + EXPECT_FALSE(MatchVlogPattern("bblah", "?lah")); + + // * can match any number (even 0) of characters. + EXPECT_TRUE(MatchVlogPattern("blah", "bl*h")); + EXPECT_TRUE(MatchVlogPattern("blabcdefh", "bl*h")); + EXPECT_TRUE(MatchVlogPattern("blh", "bl*h")); + EXPECT_TRUE(MatchVlogPattern("blah", "*blah")); + EXPECT_TRUE(MatchVlogPattern("ohblah", "*blah")); + EXPECT_TRUE(MatchVlogPattern("blah", "blah*")); + EXPECT_TRUE(MatchVlogPattern("blahhhh", "blah*")); + EXPECT_TRUE(MatchVlogPattern("blahhhh", "blah*")); + EXPECT_TRUE(MatchVlogPattern("blah", "*blah*")); + EXPECT_TRUE(MatchVlogPattern("blahhhh", "*blah*")); + EXPECT_TRUE(MatchVlogPattern("bbbblahhhh", "*blah*")); + + // Multiple *s should work fine. + EXPECT_TRUE(MatchVlogPattern("ballaah", "b*la*h")); + EXPECT_TRUE(MatchVlogPattern("blah", "b*la*h")); + EXPECT_TRUE(MatchVlogPattern("bbbblah", "b*la*h")); + EXPECT_TRUE(MatchVlogPattern("blaaah", "b*la*h")); + + // There should be no escaping going on. + EXPECT_TRUE(MatchVlogPattern("bl\\ah", "bl\\?h")); + EXPECT_FALSE(MatchVlogPattern("bl?h", "bl\\?h")); + EXPECT_TRUE(MatchVlogPattern("bl\\aaaah", "bl\\*h")); + EXPECT_FALSE(MatchVlogPattern("bl*h", "bl\\*h")); + + // Any slash matches any slash. + EXPECT_TRUE(MatchVlogPattern("/b\\lah", "/b\\lah")); + EXPECT_TRUE(MatchVlogPattern("\\b/lah", "/b\\lah")); +} + +TEST_F(VlogTest, VmoduleBasic) { const char kVSwitch[] = "-1"; const char kVModuleSwitch[] = - "foo=,bar=0,baz=blah,,qux=0blah1,quux=1,corge=5"; - VlogInfo vlog_info(kVSwitch, kVModuleSwitch); + "foo=,bar=0,baz=blah,,qux=0blah1,quux=1,corge.ext=5"; + int min_log_level = 0; + VlogInfo vlog_info(kVSwitch, kVModuleSwitch, &min_log_level); EXPECT_EQ(-1, vlog_info.GetVlogLevel("/path/to/grault.cc")); EXPECT_EQ(0, vlog_info.GetVlogLevel("/path/to/foo.cc")); EXPECT_EQ(0, vlog_info.GetVlogLevel("D:\\Path\\To\\bar-inl.mm")); @@ -38,70 +88,34 @@ TEST_F(VlogTest, Vmodule) { EXPECT_EQ(0, vlog_info.GetVlogLevel("baz.h")); EXPECT_EQ(0, vlog_info.GetVlogLevel("/another/path/to/qux.h")); EXPECT_EQ(1, vlog_info.GetVlogLevel("/path/to/quux")); - EXPECT_EQ(5, vlog_info.GetVlogLevel("c:\\path/to/corge.h")); -} - -#define BENCHMARK(iters, elapsed, code) \ - do { \ - base::TimeTicks start = base::TimeTicks::Now(); \ - for (int i = 0; i < iters; ++i) code; \ - base::TimeTicks end = base::TimeTicks::Now(); \ - elapsed = end - start; \ - double cps = iters / elapsed.InSecondsF(); \ - LOG(INFO) << cps << " cps (" << elapsed.InSecondsF() \ - << "s elapsed)"; \ - } while (0) - -double GetSlowdown(const base::TimeDelta& base, - const base::TimeDelta& elapsed) { - return elapsed.InSecondsF() / base.InSecondsF(); + EXPECT_EQ(5, vlog_info.GetVlogLevel("c:\\path/to/corge.ext.h")); } - -TEST_F(VlogTest, Perf) { - const char* kVlogs[] = { - "/path/to/foo.cc", - "C:\\path\\to\\bar.h", - "/path/to/not-matched.mm", - "C:\\path\\to\\baz-inl.mm", - "C:\\path\\to\\qux.mm", - "/path/to/quux.mm", - "/path/to/another-not-matched.mm", - }; - const int kVlogCount = arraysize(kVlogs); - const int kBenchmarkIterations = RunningOnValgrind() ? 30000 : 10000000; - - base::TimeDelta null_elapsed; - { - VlogInfo null_vlog_info("", ""); - BENCHMARK(kBenchmarkIterations, null_elapsed, { - EXPECT_NE(-1, null_vlog_info.GetVlogLevel(kVlogs[i % kVlogCount])); - }); - } - - { - VlogInfo small_vlog_info("0", "foo=1,bar=2,baz=3,qux=4,quux=5"); - base::TimeDelta elapsed; - BENCHMARK(kBenchmarkIterations, elapsed, { - EXPECT_NE(-1, small_vlog_info.GetVlogLevel(kVlogs[i % kVlogCount])); - }); - LOG(INFO) << "slowdown = " << GetSlowdown(null_elapsed, elapsed) - << "x"; - } - - { - VlogInfo pattern_vlog_info("0", "fo*=1,ba?=2,b*?z=3,*ux=4,?uux=5"); - base::TimeDelta elapsed; - BENCHMARK(kBenchmarkIterations, elapsed, { - EXPECT_NE(-1, pattern_vlog_info.GetVlogLevel(kVlogs[i % kVlogCount])); - }); - LOG(INFO) << "slowdown = " << GetSlowdown(null_elapsed, elapsed) - << "x"; - } +TEST_F(VlogTest, VmoduleDirs) { + const char kVModuleSwitch[] = + "foo/bar.cc=1,baz\\*\\qux.cc=2,*quux/*=3,*/*-inl.h=4"; + int min_log_level = 0; + VlogInfo vlog_info("", kVModuleSwitch, &min_log_level); + EXPECT_EQ(0, vlog_info.GetVlogLevel("/foo/bar.cc")); + EXPECT_EQ(0, vlog_info.GetVlogLevel("bar.cc")); + EXPECT_EQ(1, vlog_info.GetVlogLevel("foo/bar.cc")); + + EXPECT_EQ(0, vlog_info.GetVlogLevel("baz/grault/qux.h")); + EXPECT_EQ(0, vlog_info.GetVlogLevel("/baz/grault/qux.cc")); + EXPECT_EQ(2, vlog_info.GetVlogLevel("baz/grault/qux.cc")); + EXPECT_EQ(2, vlog_info.GetVlogLevel("baz/grault/blah/qux.cc")); + EXPECT_EQ(2, vlog_info.GetVlogLevel("baz\\grault\\qux.cc")); + EXPECT_EQ(2, vlog_info.GetVlogLevel("baz\\grault//blah\\qux.cc")); + + EXPECT_EQ(0, vlog_info.GetVlogLevel("/foo/bar/baz/quux.cc")); + EXPECT_EQ(3, vlog_info.GetVlogLevel("/foo/bar/baz/quux/grault.cc")); + EXPECT_EQ(3, vlog_info.GetVlogLevel("/foo\\bar/baz\\quux/grault.cc")); + + EXPECT_EQ(0, vlog_info.GetVlogLevel("foo/bar/test-inl.cc")); + EXPECT_EQ(4, vlog_info.GetVlogLevel("foo/bar/test-inl.h")); + EXPECT_EQ(4, vlog_info.GetVlogLevel("foo/bar/baz/blah-inl.h")); } -#undef BENCHMARK - } // namespace } // namespace logging diff --git a/base/waitable_event_watcher_unittest.cc b/base/waitable_event_watcher_unittest.cc index 86acee0..e6a6ac0 100644 --- a/base/waitable_event_watcher_unittest.cc +++ b/base/waitable_event_watcher_unittest.cc @@ -151,7 +151,13 @@ TEST(WaitableEventWatcherTest, OutlivesMessageLoop) { RunTest_OutlivesMessageLoop(MessageLoop::TYPE_UI); } -TEST(WaitableEventWatcherTest, DeleteUnder) { +#if defined(OS_WIN) +// Crashes sometimes on vista. http://crbug.com/62119 +#define MAYBE_DeleteUnder DISABLED_DeleteUnder +#else +#define MAYBE_DeleteUnder DeleteUnder +#endif +TEST(WaitableEventWatcherTest, MAYBE_DeleteUnder) { RunTest_DeleteUnder(MessageLoop::TYPE_DEFAULT); RunTest_DeleteUnder(MessageLoop::TYPE_IO); RunTest_DeleteUnder(MessageLoop::TYPE_UI); diff --git a/base/event_trace_consumer_win.h b/base/win/event_trace_consumer.h index de53ab2..1ceb9ee 100644 --- a/base/event_trace_consumer_win.h +++ b/base/win/event_trace_consumer.h @@ -3,8 +3,8 @@ // found in the LICENSE file. // // Declaration of a Windows event trace consumer base class. -#ifndef BASE_EVENT_TRACE_CONSUMER_WIN_H_ -#define BASE_EVENT_TRACE_CONSUMER_WIN_H_ +#ifndef BASE_WIN_EVENT_TRACE_CONSUMER_H_ +#define BASE_WIN_EVENT_TRACE_CONSUMER_H_ #pragma once #include <windows.h> @@ -13,6 +13,9 @@ #include <vector> #include "base/basictypes.h" +namespace base { +namespace win { + // This class is a base class that makes it easier to consume events // from realtime or file sessions. Concrete consumers need to sublass // a specialization of this class and override the ProcessEvent and/or @@ -141,4 +144,7 @@ HRESULT EtwTraceConsumerBase<ImplClass>::Close() { return hr; } -#endif // BASE_EVENT_TRACE_CONSUMER_WIN_H_ +} // namespace win +} // namespace base + +#endif // BASE_WIN_EVENT_TRACE_CONSUMER_H_ diff --git a/base/event_trace_consumer_win_unittest.cc b/base/win/event_trace_consumer_unittest.cc index 412ca0c..f11f459 100644 --- a/base/event_trace_consumer_win_unittest.cc +++ b/base/win/event_trace_consumer_unittest.cc @@ -3,11 +3,11 @@ // found in the LICENSE file. // // Unit tests for event trace consumer_ base class. -#include "base/event_trace_consumer_win.h" +#include "base/win/event_trace_consumer.h" #include <list> #include "base/basictypes.h" -#include "base/event_trace_controller_win.h" -#include "base/event_trace_provider_win.h" +#include "base/win/event_trace_controller.h" +#include "base/win/event_trace_provider.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/logging.h" @@ -18,6 +18,12 @@ namespace { +using base::win::EtwMofEvent; +using base::win::EtwTraceController; +using base::win::EtwTraceConsumerBase; +using base::win::EtwTraceProperties; +using base::win::EtwTraceProvider; + typedef std::list<EVENT_TRACE> EventQueue; class TestConsumer: public EtwTraceConsumerBase<TestConsumer> { diff --git a/base/event_trace_controller_win.cc b/base/win/event_trace_controller.cc index 0e11d8a..0391fbc 100644 --- a/base/event_trace_controller_win.cc +++ b/base/win/event_trace_controller.cc @@ -3,9 +3,12 @@ // found in the LICENSE file. // // Implementation of a Windows event trace controller class. -#include "base/event_trace_controller_win.h" +#include "base/win/event_trace_controller.h" #include "base/logging.h" +namespace base { +namespace win { + EtwTraceProperties::EtwTraceProperties() { memset(buffer_, 0, sizeof(buffer_)); EVENT_TRACE_PROPERTIES* prop = get(); @@ -165,3 +168,6 @@ HRESULT EtwTraceController::Flush(const wchar_t* session_name, EVENT_TRACE_CONTROL_FLUSH); return HRESULT_FROM_WIN32(err); } + +} // namespace win +} // namespace base diff --git a/base/event_trace_controller_win.h b/base/win/event_trace_controller.h index bf44fa9..8eb172e 100644 --- a/base/event_trace_controller_win.h +++ b/base/win/event_trace_controller.h @@ -17,8 +17,8 @@ // // A trace consumer consumes events from zero or one realtime session, // as well as potentially from multiple binary trace files. -#ifndef BASE_EVENT_TRACE_CONTROLLER_WIN_H_ -#define BASE_EVENT_TRACE_CONTROLLER_WIN_H_ +#ifndef BASE_WIN_EVENT_TRACE_CONTROLLER_H_ +#define BASE_WIN_EVENT_TRACE_CONTROLLER_H_ #pragma once #include <windows.h> @@ -27,6 +27,9 @@ #include <string> #include "base/basictypes.h" +namespace base { +namespace win { + // Utility class to make it easier to work with EVENT_TRACE_PROPERTIES. // The EVENT_TRACE_PROPERTIES structure contains information about an // event tracing session. @@ -141,4 +144,7 @@ class EtwTraceController { DISALLOW_COPY_AND_ASSIGN(EtwTraceController); }; -#endif // BASE_EVENT_TRACE_CONTROLLER_WIN_H_ +} // namespace win +} // namespace base + +#endif // BASE_WIN_EVENT_TRACE_CONTROLLER_H_ diff --git a/base/event_trace_controller_win_unittest.cc b/base/win/event_trace_controller_unittest.cc index 1fcb616..2b3cd66 100644 --- a/base/event_trace_controller_win_unittest.cc +++ b/base/win/event_trace_controller_unittest.cc @@ -3,8 +3,8 @@ // found in the LICENSE file. // // Unit tests for event trace controller. -#include "base/event_trace_controller_win.h" -#include "base/event_trace_provider_win.h" +#include "base/win/event_trace_controller.h" +#include "base/win/event_trace_provider.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/logging.h" @@ -15,6 +15,10 @@ namespace { +using base::win::EtwTraceController; +using base::win::EtwTraceProvider; +using base::win::EtwTraceProperties; + const wchar_t kTestSessionName[] = L"TestLogSession"; // {0D236A42-CD18-4e3d-9975-DCEEA2106E05} diff --git a/base/event_trace_provider_win.cc b/base/win/event_trace_provider.cc index 826a4bf..332fac3 100644 --- a/base/event_trace_provider_win.cc +++ b/base/win/event_trace_provider.cc @@ -2,10 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -#include "base/event_trace_provider_win.h" +#include "base/win/event_trace_provider.h" #include <windows.h> #include <cguid.h> +namespace base { +namespace win { + TRACE_GUID_REGISTRATION EtwTraceProvider::obligatory_guid_registration_ = { &GUID_NULL, NULL @@ -126,3 +129,6 @@ ULONG EtwTraceProvider::Log(EVENT_TRACE_HEADER* event) { return ::TraceEvent(session_handle_, event); } + +} // namespace win +} // namespace base diff --git a/base/event_trace_provider_win.h b/base/win/event_trace_provider.h index 5c89d1a..6614c91 100644 --- a/base/event_trace_provider_win.h +++ b/base/win/event_trace_provider.h @@ -4,8 +4,8 @@ // // Declaration of a Windows event trace provider class, to allow using // Windows Event Tracing for logging transport and control. -#ifndef BASE_EVENT_TRACE_PROVIDER_WIN_H_ -#define BASE_EVENT_TRACE_PROVIDER_WIN_H_ +#ifndef BASE_WIN_EVENT_TRACE_PROVIDER_H_ +#define BASE_WIN_EVENT_TRACE_PROVIDER_H_ #pragma once #include <windows.h> @@ -13,6 +13,9 @@ #include <evntrace.h> #include "base/basictypes.h" +namespace base { +namespace win { + typedef GUID EtwEventClass; typedef UCHAR EtwEventType; typedef UCHAR EtwEventLevel; @@ -165,4 +168,7 @@ class EtwTraceProvider { DISALLOW_COPY_AND_ASSIGN(EtwTraceProvider); }; -#endif // BASE_EVENT_TRACE_PROVIDER_WIN_H_ +} // namespace win +} // namespace base + +#endif // BASE_WIN_EVENT_TRACE_PROVIDER_H_ diff --git a/base/event_trace_provider_win_unittest.cc b/base/win/event_trace_provider_unittest.cc index 1efa74f..55b5ae6 100644 --- a/base/event_trace_provider_win_unittest.cc +++ b/base/win/event_trace_provider_unittest.cc @@ -3,13 +3,16 @@ // found in the LICENSE file. // // Unit tests for event trace provider. -#include "base/event_trace_provider_win.h" +#include "base/win/event_trace_provider.h" #include <new> #include "testing/gtest/include/gtest/gtest.h" #include <initguid.h> // NOLINT - has to be last namespace { +using base::win::EtwTraceProvider; +using base::win::EtwMofEvent; + // {7F0FD37F-FA3C-4cd6-9242-DF60967A2CB2} DEFINE_GUID(kTestProvider, 0x7f0fd37f, 0xfa3c, 0x4cd6, 0x92, 0x42, 0xdf, 0x60, 0x96, 0x7a, 0x2c, 0xb2); diff --git a/base/win/registry.cc b/base/win/registry.cc index 545c337..977ea00 100644 --- a/base/win/registry.cc +++ b/base/win/registry.cc @@ -7,6 +7,7 @@ #include <shlwapi.h> #include "base/logging.h" +#include "base/thread_restrictions.h" #pragma comment(lib, "shlwapi.lib") // for SHDeleteKey @@ -15,6 +16,8 @@ namespace win { RegistryValueIterator::RegistryValueIterator(HKEY root_key, const wchar_t* folder_key) { + base::ThreadRestrictions::AssertIOAllowed(); + LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_); if (result != ERROR_SUCCESS) { key_ = NULL; @@ -35,6 +38,7 @@ RegistryValueIterator::RegistryValueIterator(HKEY root_key, } RegistryValueIterator::~RegistryValueIterator() { + base::ThreadRestrictions::AssertIOAllowed(); if (key_) ::RegCloseKey(key_); } @@ -49,6 +53,7 @@ void RegistryValueIterator::operator++() { } bool RegistryValueIterator::Read() { + base::ThreadRestrictions::AssertIOAllowed(); if (Valid()) { DWORD ncount = arraysize(name_); value_size_ = sizeof(value_); @@ -65,6 +70,7 @@ bool RegistryValueIterator::Read() { } DWORD RegistryValueIterator::ValueCount() const { + base::ThreadRestrictions::AssertIOAllowed(); DWORD count = 0; HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL); @@ -77,6 +83,7 @@ DWORD RegistryValueIterator::ValueCount() const { RegistryKeyIterator::RegistryKeyIterator(HKEY root_key, const wchar_t* folder_key) { + base::ThreadRestrictions::AssertIOAllowed(); LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_); if (result != ERROR_SUCCESS) { key_ = NULL; @@ -97,6 +104,7 @@ RegistryKeyIterator::RegistryKeyIterator(HKEY root_key, } RegistryKeyIterator::~RegistryKeyIterator() { + base::ThreadRestrictions::AssertIOAllowed(); if (key_) ::RegCloseKey(key_); } @@ -111,6 +119,7 @@ void RegistryKeyIterator::operator++() { } bool RegistryKeyIterator::Read() { + base::ThreadRestrictions::AssertIOAllowed(); if (Valid()) { DWORD ncount = arraysize(name_); FILETIME written; @@ -125,6 +134,7 @@ bool RegistryKeyIterator::Read() { } DWORD RegistryKeyIterator::SubkeyCount() const { + base::ThreadRestrictions::AssertIOAllowed(); DWORD count = 0; HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, NULL, NULL, NULL, NULL, NULL); @@ -143,6 +153,7 @@ RegKey::RegKey() RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access) : key_(NULL), watch_event_(0) { + base::ThreadRestrictions::AssertIOAllowed(); if (rootkey) { if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) Create(rootkey, subkey, access); @@ -158,6 +169,7 @@ RegKey::~RegKey() { } void RegKey::Close() { + base::ThreadRestrictions::AssertIOAllowed(); StopWatching(); if (key_) { ::RegCloseKey(key_); @@ -172,6 +184,7 @@ bool RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) { bool RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey, DWORD* disposition, REGSAM access) { + base::ThreadRestrictions::AssertIOAllowed(); DCHECK(rootkey && subkey && access && disposition); Close(); @@ -193,6 +206,7 @@ bool RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey, } bool RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) { + base::ThreadRestrictions::AssertIOAllowed(); DCHECK(rootkey && subkey && access); Close(); @@ -205,6 +219,7 @@ bool RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) { } bool RegKey::CreateKey(const wchar_t* name, REGSAM access) { + base::ThreadRestrictions::AssertIOAllowed(); DCHECK(name && access); HKEY subkey = NULL; @@ -217,6 +232,7 @@ bool RegKey::CreateKey(const wchar_t* name, REGSAM access) { } bool RegKey::OpenKey(const wchar_t* name, REGSAM access) { + base::ThreadRestrictions::AssertIOAllowed(); DCHECK(name && access); HKEY subkey = NULL; @@ -229,6 +245,7 @@ bool RegKey::OpenKey(const wchar_t* name, REGSAM access) { } DWORD RegKey::ValueCount() { + base::ThreadRestrictions::AssertIOAllowed(); DWORD count = 0; HRESULT result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL); @@ -236,6 +253,7 @@ DWORD RegKey::ValueCount() { } bool RegKey::ReadName(int index, std::wstring* name) { + base::ThreadRestrictions::AssertIOAllowed(); wchar_t buf[256]; DWORD bufsize = arraysize(buf); LRESULT r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, @@ -248,6 +266,7 @@ bool RegKey::ReadName(int index, std::wstring* name) { } bool RegKey::ValueExists(const wchar_t* name) { + base::ThreadRestrictions::AssertIOAllowed(); if (!key_) return false; HRESULT result = RegQueryValueEx(key_, name, 0, NULL, NULL, NULL); @@ -256,6 +275,7 @@ bool RegKey::ValueExists(const wchar_t* name) { bool RegKey::ReadValue(const wchar_t* name, void* data, DWORD* dsize, DWORD* dtype) { + base::ThreadRestrictions::AssertIOAllowed(); if (!key_) return false; HRESULT result = RegQueryValueEx(key_, name, 0, dtype, @@ -264,6 +284,7 @@ bool RegKey::ReadValue(const wchar_t* name, void* data, } bool RegKey::ReadValue(const wchar_t* name, std::wstring* value) { + base::ThreadRestrictions::AssertIOAllowed(); DCHECK(value); const size_t kMaxStringLength = 1024; // This is after expansion. // Use the one of the other forms of ReadValue if 1024 is too small for you. @@ -308,6 +329,7 @@ bool RegKey::ReadValueDW(const wchar_t* name, DWORD* value) { bool RegKey::WriteValue(const wchar_t* name, const void * data, DWORD dsize, DWORD dtype) { + base::ThreadRestrictions::AssertIOAllowed(); DCHECK(data); if (!key_) @@ -334,10 +356,12 @@ bool RegKey::WriteValue(const wchar_t* name, DWORD value) { } bool RegKey::DeleteKey(const wchar_t* name) { + base::ThreadRestrictions::AssertIOAllowed(); return (!key_) ? false : (ERROR_SUCCESS == SHDeleteKey(key_, name)); } bool RegKey::DeleteValue(const wchar_t* value_name) { + base::ThreadRestrictions::AssertIOAllowed(); DCHECK(value_name); HRESULT result = RegDeleteValue(key_, value_name); return (result == ERROR_SUCCESS); diff --git a/base/win_util.cc b/base/win_util.cc index a576122..3fcfb92 100644 --- a/base/win_util.cc +++ b/base/win_util.cc @@ -17,6 +17,7 @@ #include "base/scoped_ptr.h" #include "base/string_util.h" #include "base/stringprintf.h" +#include "base/thread_restrictions.h" #include "base/win/windows_version.h" namespace win_util { @@ -127,6 +128,11 @@ std::wstring GetClassName(HWND window) { } bool UserAccountControlIsEnabled() { + // This can be slow if Windows ends up going to disk. Should watch this key + // for changes and only read it once, preferably on the file thread. + // http://code.google.com/p/chromium/issues/detail?id=61644 + base::ThreadRestrictions::ScopedAllowIO allow_io; + base::win::RegKey key(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", KEY_READ); diff --git a/base/win_util.h b/base/win_util.h index 18400f2..4ba7eeb 100644 --- a/base/win_util.h +++ b/base/win_util.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_WIN_UTIL_H__ -#define BASE_WIN_UTIL_H__ +#ifndef BASE_WIN_UTIL_H_ +#define BASE_WIN_UTIL_H_ #pragma once #include <windows.h> @@ -16,36 +16,10 @@ struct IPropertyStore; struct _tagpropertykey; typedef _tagpropertykey PROPERTYKEY; -// TODO(brettw) remove this once RLZ is updated to use the new version in -// base/win/windows_version.h. -#if defined(RLZ_WIN_LIB_LIB_MUTEX_H_) || defined(RLZ_WIN_LIB_USER_KEY_H_) \ - || defined(RLZ_WIN_LIB_PROCESS_INFO_H_) -#include "base/win/windows_version.h" -#endif - namespace win_util { void GetNonClientMetrics(NONCLIENTMETRICS* metrics); -// TODO(brettw) remove this once RLZ is updated to use the new version in -// base/win/windows_version.h. -#if defined(RLZ_WIN_LIB_LIB_MUTEX_H_) || defined(RLZ_WIN_LIB_USER_KEY_H_) \ - || defined(RLZ_WIN_LIB_PROCESS_INFO_H_) -// These must match the values in base::win! -enum WinVersion { - WINVERSION_PRE_2000 = 0, // Not supported - WINVERSION_2000 = 1, // Not supported - WINVERSION_XP = 2, - WINVERSION_SERVER_2003 = 3, - WINVERSION_VISTA = 4, - WINVERSION_2008 = 5, - WINVERSION_WIN7 = 6, -}; -inline WinVersion GetWinVersion() { - return static_cast<WinVersion>(base::win::GetVersion()); -} -#endif // RLZ_* - // Returns the string representing the current user sid. bool GetUserSidString(std::wstring* user_sid); @@ -101,4 +75,4 @@ bool RemoveCommandFromAutoRun(HKEY root_key, const string16& name); } // namespace win_util -#endif // BASE_WIN_UTIL_H__ +#endif // BASE_WIN_UTIL_H_ diff --git a/base/worker_pool_linux.cc b/base/worker_pool_linux.cc index 2aa1df2..8c96ca0 100644 --- a/base/worker_pool_linux.cc +++ b/base/worker_pool_linux.cc @@ -84,11 +84,29 @@ void WorkerThread::ThreadMain() { } // namespace +// NOTE(shess): Temporarily allow the Mac WorkerPool implementation to +// call into the linux so that it can provide a command-line flag for +// switching back and forth. After evaluating, either remove the +// ifdef, or shift this to a shared POSIX implementation. +// http://crbug.com/44392 +#if defined(OS_MACOSX) +namespace worker_pool_mac { + +bool MacPostTaskHelper(const tracked_objects::Location& from_here, + Task* task, bool task_is_slow) { + g_lazy_worker_pool.Pointer()->PostTask(from_here, task, task_is_slow); + return true; +} + +} // namespace worker_pool_mac + +#else bool WorkerPool::PostTask(const tracked_objects::Location& from_here, Task* task, bool task_is_slow) { g_lazy_worker_pool.Pointer()->PostTask(from_here, task, task_is_slow); return true; } +#endif namespace base { diff --git a/base/worker_pool_linux.h b/base/worker_pool_linux.h index e8c1931..ea6bab7 100644 --- a/base/worker_pool_linux.h +++ b/base/worker_pool_linux.h @@ -86,4 +86,18 @@ class LinuxDynamicThreadPool } // namespace base +#if defined(OS_MACOSX) +namespace worker_pool_mac { + +// NOTE(shess): Helper so that Mac WorkerPool implementation can call +// into Linux implementation while determining if the implementations +// should be merged. After evaluating, either remove the ifdef, or +// shift this to a shared POSIX implementation. +// http://crbug.com/44392 +bool MacPostTaskHelper(const tracked_objects::Location& from_here, + Task* task, bool task_is_slow); + +} // namespace worker_pool_mac +#endif + #endif // BASE_WORKER_POOL_LINUX_H_ diff --git a/base/worker_pool_mac.h b/base/worker_pool_mac.h index 85cab8b..1e86891 100644 --- a/base/worker_pool_mac.h +++ b/base/worker_pool_mac.h @@ -24,4 +24,10 @@ @end // @interface WorkerPoolObjC +namespace worker_pool_mac { + +void SetUseLinuxWorkerPool(bool flag); + +} // namespace worker_pool_mac + #endif // BASE_WORKER_POOL_MAC_H_ diff --git a/base/worker_pool_mac.mm b/base/worker_pool_mac.mm index 01c0a98..bbc7892 100644 --- a/base/worker_pool_mac.mm +++ b/base/worker_pool_mac.mm @@ -5,12 +5,13 @@ #include "base/worker_pool_mac.h" #include "base/logging.h" -#import "base/mac/scoped_nsautorelease_pool.h" +#include "base/mac/scoped_nsautorelease_pool.h" #include "base/metrics/histogram.h" #include "base/scoped_ptr.h" #import "base/singleton_objc.h" #include "base/task.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" +#include "base/worker_pool_linux.h" // When C++ exceptions are disabled, the C++ library defines |try| and // |catch| so as to allow exception-expecting C++ code to build properly when @@ -23,6 +24,10 @@ namespace { +// |true| to use the Linux WorkerPool implementation for +// |WorkerPool::PostTask()|. +bool use_linux_workerpool_ = true; + Lock lock_; base::Time last_check_; // Last hung-test check. std::vector<id> outstanding_ops_; // Outstanding operations at last check. @@ -31,6 +36,14 @@ size_t outstanding_ = 0; // Operations posted but not completed. } // namespace +namespace worker_pool_mac { + +void SetUseLinuxWorkerPool(bool flag) { + use_linux_workerpool_ = flag; +} + +} // namespace worker_pool_mac + @implementation WorkerPoolObjC + (NSOperationQueue*)sharedOperationQueue { @@ -117,6 +130,10 @@ size_t outstanding_ = 0; // Operations posted but not completed. bool WorkerPool::PostTask(const tracked_objects::Location& from_here, Task* task, bool task_is_slow) { + if (use_linux_workerpool_) { + return worker_pool_mac::MacPostTaskHelper(from_here, task, task_is_slow); + } + base::mac::ScopedNSAutoreleasePool autorelease_pool; // Ignore |task_is_slow|, it doesn't map directly to any tunable aspect of |
