From 7324afb043a0b1e623d8e8eb906cdc53bdeb4685 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Wed, 5 Apr 2017 17:15:33 +0200 Subject: BASELINE: Update Chromium to 58.0.3029.54 Change-Id: I67f57065a7afdc8e4614adb5c0230281428df4d1 Reviewed-by: Peter Varga --- chromium/components/tracing/BUILD.gn | 8 +- chromium/components/tracing/OWNERS | 3 + .../tracing/browser/trace_config_file.cc | 147 --------- .../components/tracing/browser/trace_config_file.h | 97 ------ .../tracing/browser/trace_config_file_unittest.cc | 250 --------------- .../common/process_metrics_memory_dump_provider.cc | 350 ++++++++++++++++++++- .../common/process_metrics_memory_dump_provider.h | 21 +- ...rocess_metrics_memory_dump_provider_unittest.cc | 165 ++++++++++ .../components/tracing/common/trace_config_file.cc | 146 +++++++++ .../components/tracing/common/trace_config_file.h | 98 ++++++ .../tracing/common/trace_config_file_unittest.cc | 250 +++++++++++++++ .../components/tracing/common/trace_startup.cc | 48 +++ chromium/components/tracing/common/trace_startup.h | 19 ++ 13 files changed, 1093 insertions(+), 509 deletions(-) delete mode 100644 chromium/components/tracing/browser/trace_config_file.cc delete mode 100644 chromium/components/tracing/browser/trace_config_file.h delete mode 100644 chromium/components/tracing/browser/trace_config_file_unittest.cc create mode 100644 chromium/components/tracing/common/trace_config_file.cc create mode 100644 chromium/components/tracing/common/trace_config_file.h create mode 100644 chromium/components/tracing/common/trace_config_file_unittest.cc create mode 100644 chromium/components/tracing/common/trace_startup.cc create mode 100644 chromium/components/tracing/common/trace_startup.h (limited to 'chromium/components/tracing') diff --git a/chromium/components/tracing/BUILD.gn b/chromium/components/tracing/BUILD.gn index b73c3d6ffa3..deab4a66a3d 100644 --- a/chromium/components/tracing/BUILD.gn +++ b/chromium/components/tracing/BUILD.gn @@ -50,8 +50,10 @@ component("tracing") { component("startup_tracing") { sources = [ - "browser/trace_config_file.cc", - "browser/trace_config_file.h", + "common/trace_config_file.cc", + "common/trace_config_file.h", + "common/trace_startup.cc", + "common/trace_startup.h", "common/trace_to_console.cc", "common/trace_to_console.h", "common/tracing_switches.cc", @@ -113,7 +115,7 @@ source_set("unit_tests") { ] if (!is_android) { - sources += [ "browser/trace_config_file_unittest.cc" ] + sources += [ "common/trace_config_file_unittest.cc" ] deps += [ ":startup_tracing" ] } } diff --git a/chromium/components/tracing/OWNERS b/chromium/components/tracing/OWNERS index 1d9100e7b8d..8c97000af7b 100644 --- a/chromium/components/tracing/OWNERS +++ b/chromium/components/tracing/OWNERS @@ -1,2 +1,5 @@ file://base/trace_event/OWNERS jbauman@chromium.org + +# TEAM: tracing@chromium.org +# COMPONENT: Speed>Tracing diff --git a/chromium/components/tracing/browser/trace_config_file.cc b/chromium/components/tracing/browser/trace_config_file.cc deleted file mode 100644 index 97f8a16810a..00000000000 --- a/chromium/components/tracing/browser/trace_config_file.cc +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/tracing/browser/trace_config_file.h" - -#include - -#include -#include - -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/json/json_reader.h" -#include "base/logging.h" -#include "base/memory/singleton.h" -#include "base/values.h" -#include "build/build_config.h" -#include "components/tracing/common/tracing_switches.h" - -namespace tracing { - -namespace { - -// Maximum trace config file size that will be loaded, in bytes. -const size_t kTraceConfigFileSizeLimit = 64 * 1024; - -// Trace config file path: -// - Android: /data/local/chrome-trace-config.json -// - Others: specified by --trace-config-file flag. -#if defined(OS_ANDROID) -const base::FilePath::CharType kAndroidTraceConfigFile[] = - FILE_PATH_LITERAL("/data/local/chrome-trace-config.json"); -#endif - -const base::FilePath::CharType kDefaultResultFile[] = - FILE_PATH_LITERAL("chrometrace.log"); - -// String parameters that can be used to parse the trace config file content. -const char kTraceConfigParam[] = "trace_config"; -const char kStartupDurationParam[] = "startup_duration"; -const char kResultFileParam[] = "result_file"; - -} // namespace - -TraceConfigFile* TraceConfigFile::GetInstance() { - return base::Singleton>::get(); -} - -TraceConfigFile::TraceConfigFile() - : is_enabled_(false), - trace_config_(base::trace_event::TraceConfig()), - startup_duration_(0), - result_file_(kDefaultResultFile) { -#if defined(OS_ANDROID) - base::FilePath trace_config_file(kAndroidTraceConfigFile); -#else - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - if (!command_line.HasSwitch(switches::kTraceConfigFile) || - command_line.HasSwitch(switches::kTraceStartup) || - command_line.HasSwitch(switches::kTraceShutdown)) { - return; - } - base::FilePath trace_config_file = - command_line.GetSwitchValuePath(switches::kTraceConfigFile); -#endif - - if (trace_config_file.empty()) { - // If the trace config file path is not specified, trace Chrome with the - // default configuration for 5 sec. - startup_duration_ = 5; - is_enabled_ = true; - DLOG(WARNING) << "Use default trace config."; - return; - } - - if (!base::PathExists(trace_config_file)) { - DLOG(WARNING) << "The trace config file does not exist."; - return; - } - - std::string trace_config_file_content; - if (!base::ReadFileToStringWithMaxSize(trace_config_file, - &trace_config_file_content, - kTraceConfigFileSizeLimit)) { - DLOG(WARNING) << "Cannot read the trace config file correctly."; - return; - } - is_enabled_ = ParseTraceConfigFileContent(trace_config_file_content); - if (!is_enabled_) - DLOG(WARNING) << "Cannot parse the trace config file correctly."; -} - -TraceConfigFile::~TraceConfigFile() { -} - -bool TraceConfigFile::ParseTraceConfigFileContent(const std::string& content) { - std::unique_ptr value(base::JSONReader::Read(content)); - if (!value || !value->IsType(base::Value::Type::DICTIONARY)) - return false; - - std::unique_ptr dict( - static_cast(value.release())); - - base::DictionaryValue* trace_config_dict = NULL; - if (!dict->GetDictionary(kTraceConfigParam, &trace_config_dict)) - return false; - - trace_config_ = base::trace_event::TraceConfig(*trace_config_dict); - - if (!dict->GetInteger(kStartupDurationParam, &startup_duration_)) - startup_duration_ = 0; - - if (startup_duration_ < 0) - startup_duration_ = 0; - - base::FilePath::StringType result_file_str; - if (dict->GetString(kResultFileParam, &result_file_str)) - result_file_ = base::FilePath(result_file_str); - - return true; -} - -bool TraceConfigFile::IsEnabled() const { - return is_enabled_; -} - -base::trace_event::TraceConfig TraceConfigFile::GetTraceConfig() const { - DCHECK(IsEnabled()); - return trace_config_; -} - -int TraceConfigFile::GetStartupDuration() const { - DCHECK(IsEnabled()); - return startup_duration_; -} - -#if !defined(OS_ANDROID) -base::FilePath TraceConfigFile::GetResultFile() const { - DCHECK(IsEnabled()); - return result_file_; -} -#endif - -} // namespace tracing diff --git a/chromium/components/tracing/browser/trace_config_file.h b/chromium/components/tracing/browser/trace_config_file.h deleted file mode 100644 index 06766d0ecd9..00000000000 --- a/chromium/components/tracing/browser/trace_config_file.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_TRACING_BROWSER_TRACE_CONFIG_FILE_H_ -#define COMPONENTS_TRACING_BROWSER_TRACE_CONFIG_FILE_H_ - -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/trace_event/trace_config.h" -#include "build/build_config.h" -#include "components/tracing/tracing_export.h" - -namespace base { -template struct DefaultSingletonTraits; -} // namespace base - -namespace tracing { - -// TraceConfigFile is a singleton that contains the configurations of tracing. -// One can create a trace config file and use it to configure startup and/or -// shutdown tracing. -// -// The trace config file should be JSON formated. One example is: -// { -// "trace_config": { -// "record_mode": "record-until-full", -// "included_categories": ["cc", "skia"] -// }, -// "startup_duration": 5, -// "result_file": "chrometrace.log" -// } -// -// trace_config: The configuration of tracing. Please see the details in -// base/trace_event/trace_config.h. -// -// startup_duration: The duration for startup tracing in terms of seconds. -// Tracing will stop automatically after the duration. If this -// value is not specified, the duration is 0 and one needs -// to stop tracing by other ways, e.g., by DevTools, or get -// the result file after shutting the browser down. -// -// result_file: The file that contains the trace log. The default result -// file path is chrometrace.log. Chrome will dump the trace -// log to this file -// 1) after startup_duration if it is specified; -// 2) or after browser shutdown if startup duration is 0. -// One can also stop tracing and get the result by other ways, -// e.g., by DevTools. In that case, the trace log will not be -// saved to this file. -// Notice: This is not supported on Android. The result file -// path will be generated by tracing controller. -// -// The trace config file can be specified by the --trace-config-file flag on -// most platforms except on Android, e.g., --trace-config-file=path/to/file/. -// This flag should not be used with --trace-startup or --trace-shutdown. If -// those two flags are used, --trace-config-file flag will be ignored. If the -// --trace-config-file flag is used without the file path, Chrome will do -// startup tracing with 5 seconds' startup duration. -// -// On Android, Chrome does not read the --trace-config-file flag, because not -// all Chrome based browsers read customized flag, e.g., Android WebView. Chrome -// on Android reads from a fixed file location: -// /data/local/chrome-trace-config.json -// If this file exists, Chrome will start tracing according to the configuration -// specified in the file, otherwise, Chrome will not start tracing. -class TRACING_EXPORT TraceConfigFile { - public: - static TraceConfigFile* GetInstance(); - - bool IsEnabled() const; - base::trace_event::TraceConfig GetTraceConfig() const; - int GetStartupDuration() const; -#if !defined(OS_ANDROID) - base::FilePath GetResultFile() const; -#endif - - private: - // This allows constructor and destructor to be private and usable only - // by the Singleton class. - friend struct base::DefaultSingletonTraits; - TraceConfigFile(); - ~TraceConfigFile(); - - bool ParseTraceConfigFileContent(const std::string& content); - - bool is_enabled_; - base::trace_event::TraceConfig trace_config_; - int startup_duration_; - base::FilePath result_file_; - - DISALLOW_COPY_AND_ASSIGN(TraceConfigFile); -}; - -} // namespace tracing - -#endif // COMPONENTS_TRACING_BROWSER_TRACE_CONFIG_FILE_H_ diff --git a/chromium/components/tracing/browser/trace_config_file_unittest.cc b/chromium/components/tracing/browser/trace_config_file_unittest.cc deleted file mode 100644 index 7920a5e8c6b..00000000000 --- a/chromium/components/tracing/browser/trace_config_file_unittest.cc +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright (c) 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/at_exit.h" -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "components/tracing/browser/trace_config_file.h" -#include "components/tracing/common/tracing_switches.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace tracing { - -namespace { - -const char kTraceConfig[] = - "{" - "\"enable_argument_filter\":true," - "\"enable_systrace\":true," - "\"excluded_categories\":[\"excluded\",\"exc_pattern*\"]," - "\"included_categories\":[\"included\"," - "\"inc_pattern*\"," - "\"disabled-by-default-cc\"]," - "\"record_mode\":\"record-continuously\"," - "\"synthetic_delays\":[\"test.Delay1;16\",\"test.Delay2;32\"]" - "}"; - -std::string GetTraceConfigFileContent(std::string trace_config, - std::string startup_duration, - std::string result_file) { - std::string content = "{"; - if (!trace_config.empty()) - content += "\"trace_config\":" + trace_config; - - if (!startup_duration.empty()) { - if (content != "{") - content += ","; - content += "\"startup_duration\":" + startup_duration; - } - - if (!result_file.empty()) { - if (content != "{") - content += ","; - content += "\"result_file\":\"" + result_file + "\""; - } - - content += "}"; - return content; -} - -} // namespace - -TEST(TraceConfigFileTest, TraceStartupEnabled) { - base::ShadowingAtExitManager sem; - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kTraceStartup); - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kTraceConfigFile); - - EXPECT_FALSE(TraceConfigFile::GetInstance()->IsEnabled()); -} - -TEST(TraceConfigFileTest, TraceShutdownEnabled) { - base::ShadowingAtExitManager sem; - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kTraceShutdown); - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kTraceConfigFile); - - EXPECT_FALSE(TraceConfigFile::GetInstance()->IsEnabled()); -} - -TEST(TraceConfigFileTest, TraceConfigFileNotEnabled) { - base::ShadowingAtExitManager sem; - EXPECT_FALSE(TraceConfigFile::GetInstance()->IsEnabled()); -} - -TEST(TraceConfigFileTest, TraceConfigFileEnabledWithoutPath) { - base::ShadowingAtExitManager sem; - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kTraceConfigFile); - - ASSERT_TRUE(TraceConfigFile::GetInstance()->IsEnabled()); - EXPECT_EQ(base::trace_event::TraceConfig().ToString(), - TraceConfigFile::GetInstance()->GetTraceConfig().ToString()); - EXPECT_EQ(5, TraceConfigFile::GetInstance()->GetStartupDuration()); - EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("chrometrace.log")), - TraceConfigFile::GetInstance()->GetResultFile()); -} - -TEST(TraceConfigFileTest, TraceConfigFileEnabledWithInvalidPath) { - base::ShadowingAtExitManager sem; - base::CommandLine::ForCurrentProcess()->AppendSwitchPath( - switches::kTraceConfigFile, - base::FilePath(FILE_PATH_LITERAL("invalid-trace-config-file-path"))); - - EXPECT_FALSE(TraceConfigFile::GetInstance()->IsEnabled()); -} - -TEST(TraceConfigFileTest, ValidContent) { - base::ShadowingAtExitManager sem; - std::string content = GetTraceConfigFileContent( - kTraceConfig, "10", "trace_result_file.log"); - - base::FilePath trace_config_file; - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - ASSERT_TRUE( - base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file)); - ASSERT_NE(-1, base::WriteFile( - trace_config_file, content.c_str(), (int)content.length())); - base::CommandLine::ForCurrentProcess()->AppendSwitchPath( - switches::kTraceConfigFile, trace_config_file); - - ASSERT_TRUE(TraceConfigFile::GetInstance()->IsEnabled()); - EXPECT_STREQ( - kTraceConfig, - TraceConfigFile::GetInstance()->GetTraceConfig().ToString().c_str()); - EXPECT_EQ(10, TraceConfigFile::GetInstance()->GetStartupDuration()); - EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("trace_result_file.log")), - TraceConfigFile::GetInstance()->GetResultFile()); -} - -TEST(TraceConfigFileTest, ValidContentWithOnlyTraceConfig) { - base::ShadowingAtExitManager sem; - std::string content = GetTraceConfigFileContent(kTraceConfig, "", ""); - - base::FilePath trace_config_file; - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - ASSERT_TRUE( - base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file)); - ASSERT_NE(-1, base::WriteFile( - trace_config_file, content.c_str(), (int)content.length())); - base::CommandLine::ForCurrentProcess()->AppendSwitchPath( - switches::kTraceConfigFile, trace_config_file); - - ASSERT_TRUE(TraceConfigFile::GetInstance()->IsEnabled()); - EXPECT_STREQ( - kTraceConfig, - TraceConfigFile::GetInstance()->GetTraceConfig().ToString().c_str()); - EXPECT_EQ(0, TraceConfigFile::GetInstance()->GetStartupDuration()); - EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("chrometrace.log")), - TraceConfigFile::GetInstance()->GetResultFile()); -} - -TEST(TraceConfigFileTest, ContentWithAbsoluteResultFilePath) { - base::ShadowingAtExitManager sem; - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - const base::FilePath result_file_path = - temp_dir.GetPath().Append(FILE_PATH_LITERAL("trace_result_file.log")); - ASSERT_TRUE(result_file_path.IsAbsolute()); - - std::string result_file_path_str = result_file_path.AsUTF8Unsafe(); - auto it = std::find( - result_file_path_str.begin(), result_file_path_str.end(), '\\'); - while (it != result_file_path_str.end()) { - auto it2 = result_file_path_str.insert(it, '\\'); - it = std::find(it2+2, result_file_path_str.end(), '\\'); - } - std::string content = GetTraceConfigFileContent( - kTraceConfig, "10", result_file_path_str); - - base::FilePath trace_config_file; - ASSERT_TRUE( - base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file)); - ASSERT_NE(-1, base::WriteFile( - trace_config_file, content.c_str(), (int)content.length())); - base::CommandLine::ForCurrentProcess()->AppendSwitchPath( - switches::kTraceConfigFile, trace_config_file); - - ASSERT_TRUE(TraceConfigFile::GetInstance()->IsEnabled()); - EXPECT_EQ(result_file_path, TraceConfigFile::GetInstance()->GetResultFile()); -} - -TEST(TraceConfigFileTest, ContentWithNegtiveDuration) { - base::ShadowingAtExitManager sem; - std::string content = GetTraceConfigFileContent(kTraceConfig, "-1", ""); - - base::FilePath trace_config_file; - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - ASSERT_TRUE( - base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file)); - ASSERT_NE(-1, base::WriteFile( - trace_config_file, content.c_str(), (int)content.length())); - base::CommandLine::ForCurrentProcess()->AppendSwitchPath( - switches::kTraceConfigFile, trace_config_file); - - ASSERT_TRUE(TraceConfigFile::GetInstance()->IsEnabled()); - EXPECT_STREQ( - kTraceConfig, - TraceConfigFile::GetInstance()->GetTraceConfig().ToString().c_str()); - EXPECT_EQ(0, TraceConfigFile::GetInstance()->GetStartupDuration()); - EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("chrometrace.log")), - TraceConfigFile::GetInstance()->GetResultFile()); -} - -TEST(TraceConfigFileTest, ContentWithoutTraceConfig) { - base::ShadowingAtExitManager sem; - std::string content = GetTraceConfigFileContent( - "", "10", "trace_result_file.log"); - - base::FilePath trace_config_file; - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - ASSERT_TRUE( - base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file)); - ASSERT_NE(-1, base::WriteFile( - trace_config_file, content.c_str(), (int)content.length())); - base::CommandLine::ForCurrentProcess()->AppendSwitchPath( - switches::kTraceConfigFile, trace_config_file); - - EXPECT_FALSE(TraceConfigFile::GetInstance()->IsEnabled()); -} - -TEST(TraceConfigFileTest, InvalidContent) { - base::ShadowingAtExitManager sem; - std::string content = "invalid trace config file content"; - - base::FilePath trace_config_file; - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - ASSERT_TRUE( - base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file)); - ASSERT_NE(-1, base::WriteFile( - trace_config_file, content.c_str(), (int)content.length())); - base::CommandLine::ForCurrentProcess()->AppendSwitchPath( - switches::kTraceConfigFile, trace_config_file); - - EXPECT_FALSE(TraceConfigFile::GetInstance()->IsEnabled()); -} - -TEST(TraceConfigFileTest, EmptyContent) { - base::ShadowingAtExitManager sem; - base::FilePath trace_config_file; - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - ASSERT_TRUE( - base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file)); - base::CommandLine::ForCurrentProcess()->AppendSwitchPath( - switches::kTraceConfigFile, trace_config_file); - - EXPECT_FALSE(TraceConfigFile::GetInstance()->IsEnabled()); -} - -} // namespace tracing diff --git a/chromium/components/tracing/common/process_metrics_memory_dump_provider.cc b/chromium/components/tracing/common/process_metrics_memory_dump_provider.cc index 593755bada1..d8a29651182 100644 --- a/chromium/components/tracing/common/process_metrics_memory_dump_provider.cc +++ b/chromium/components/tracing/common/process_metrics_memory_dump_provider.cc @@ -24,6 +24,29 @@ #include "base/trace_event/process_memory_totals.h" #include "build/build_config.h" +#if defined(OS_MACOSX) +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "base/numerics/safe_math.h" +#endif // defined(OS_MACOSX) + +#if defined(OS_WIN) +#include +#include +#include + +#include +#include +#endif // defined(OS_WIN) + namespace tracing { namespace { @@ -190,6 +213,10 @@ std::unique_ptr CreateProcessMetrics( // static uint64_t ProcessMetricsMemoryDumpProvider::rss_bytes_for_testing = 0; +// static +ProcessMetricsMemoryDumpProvider::FactoryFunction + ProcessMetricsMemoryDumpProvider::factory_for_testing = nullptr; + #if defined(OS_LINUX) || defined(OS_ANDROID) // static @@ -219,26 +246,331 @@ bool ProcessMetricsMemoryDumpProvider::DumpProcessMemoryMaps( } #endif // defined(OS_LINUX) || defined(OS_ANDROID) +#if defined(OS_WIN) +bool ProcessMetricsMemoryDumpProvider::DumpProcessMemoryMaps( + const base::trace_event::MemoryDumpArgs& args, + base::trace_event::ProcessMemoryDump* pmd) { + std::vector modules; + if (!base::win::GetLoadedModulesSnapshot(::GetCurrentProcess(), &modules)) + return false; + + // Query the base address for each module, and attach it to the dump. + for (size_t i = 0; i < modules.size(); ++i) { + wchar_t module_name[MAX_PATH]; + if (!::GetModuleFileName(modules[i], module_name, MAX_PATH)) + continue; + + MODULEINFO module_info; + if (!::GetModuleInformation(::GetCurrentProcess(), modules[i], + &module_info, sizeof(MODULEINFO))) { + continue; + } + base::trace_event::ProcessMemoryMaps::VMRegion region; + region.size_in_bytes = module_info.SizeOfImage; + region.mapped_file = base::SysWideToNativeMB(module_name); + region.start_address = reinterpret_cast(module_info.lpBaseOfDll); + pmd->process_mmaps()->AddVMRegion(region); + } + if (!pmd->process_mmaps()->vm_regions().empty()) + pmd->set_has_process_mmaps(); + return true; +} +#endif // defined(OS_WIN) + +#if defined(OS_MACOSX) + +namespace { + +using VMRegion = base::trace_event::ProcessMemoryMaps::VMRegion; + +bool IsAddressInSharedRegion(uint64_t address) { + return address >= SHARED_REGION_BASE_X86_64 && + address < (SHARED_REGION_BASE_X86_64 + SHARED_REGION_SIZE_X86_64); +} + +bool IsRegionContainedInRegion(const VMRegion& containee, + const VMRegion& container) { + uint64_t containee_end_address = + containee.start_address + containee.size_in_bytes; + uint64_t container_end_address = + container.start_address + container.size_in_bytes; + return containee.start_address >= container.start_address && + containee_end_address <= container_end_address; +} + +bool DoRegionsIntersect(const VMRegion& a, const VMRegion& b) { + uint64_t a_end_address = a.start_address + a.size_in_bytes; + uint64_t b_end_address = b.start_address + b.size_in_bytes; + return a.start_address < b_end_address && b.start_address < a_end_address; +} + +// Creates VMRegions for all dyld images. Returns whether the operation +// succeeded. +bool GetDyldRegions(std::vector* regions) { + task_dyld_info_data_t dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + kern_return_t kr = + task_info(mach_task_self(), TASK_DYLD_INFO, + reinterpret_cast(&dyld_info), &count); + if (kr != KERN_SUCCESS) + return false; + + const struct dyld_all_image_infos* all_image_infos = + reinterpret_cast( + dyld_info.all_image_info_addr); + + bool emitted_linkedit_from_dyld_shared_cache = false; + for (size_t i = 0; i < all_image_infos->infoArrayCount; i++) { + const char* image_name = all_image_infos->infoArray[i].imageFilePath; + + // The public definition for dyld_all_image_infos/dyld_image_info is wrong + // for 64-bit platforms. We explicitly cast to struct mach_header_64 even + // though the public definition claims that this is a struct mach_header. + const struct mach_header_64* const header = + reinterpret_cast( + all_image_infos->infoArray[i].imageLoadAddress); + + uint64_t next_command = reinterpret_cast(header + 1); + uint64_t command_end = next_command + header->sizeofcmds; + uint64_t slide = 0; + for (unsigned int j = 0; j < header->ncmds; ++j) { + // Ensure that next_command doesn't run past header->sizeofcmds. + if (next_command + sizeof(struct load_command) > command_end) + return false; + const struct load_command* load_cmd = + reinterpret_cast(next_command); + next_command += load_cmd->cmdsize; + + if (load_cmd->cmd == LC_SEGMENT_64) { + if (load_cmd->cmdsize < sizeof(segment_command_64)) + return false; + const segment_command_64* seg = + reinterpret_cast(load_cmd); + if (strcmp(seg->segname, SEG_PAGEZERO) == 0) + continue; + if (strcmp(seg->segname, SEG_TEXT) == 0) { + slide = reinterpret_cast(header) - seg->vmaddr; + } + + // Avoid emitting LINKEDIT regions in the dyld shared cache, since they + // all overlap. + if (IsAddressInSharedRegion(seg->vmaddr) && + strcmp(seg->segname, SEG_LINKEDIT) == 0) { + if (emitted_linkedit_from_dyld_shared_cache) { + continue; + } else { + emitted_linkedit_from_dyld_shared_cache = true; + image_name = "dyld shared cache combined __LINKEDIT"; + } + } + + uint32_t protection_flags = 0; + if (seg->initprot & VM_PROT_READ) + protection_flags |= VMRegion::kProtectionFlagsRead; + if (seg->initprot & VM_PROT_WRITE) + protection_flags |= VMRegion::kProtectionFlagsWrite; + if (seg->initprot & VM_PROT_EXECUTE) + protection_flags |= VMRegion::kProtectionFlagsExec; + + VMRegion region; + region.size_in_bytes = seg->vmsize; + region.protection_flags = protection_flags; + region.mapped_file = image_name; + region.start_address = slide + seg->vmaddr; + + // We intentionally avoid setting any page information, which is not + // available from dyld. The fields will be populated later. + regions->push_back(region); + } + } + } + return true; +} + +void PopulateByteStats(VMRegion* region, const vm_region_submap_info_64& info) { + uint32_t share_mode = info.share_mode; + if (share_mode == SM_COW && info.ref_count == 1) + share_mode = SM_PRIVATE; + + uint64_t dirty_bytes = info.pages_dirtied * PAGE_SIZE; + uint64_t clean_bytes = + (info.pages_resident - info.pages_reusable - info.pages_dirtied) * + PAGE_SIZE; + switch (share_mode) { + case SM_LARGE_PAGE: + case SM_PRIVATE: + region->byte_stats_private_dirty_resident = dirty_bytes; + region->byte_stats_private_clean_resident = clean_bytes; + break; + case SM_COW: + region->byte_stats_private_dirty_resident = dirty_bytes; + region->byte_stats_shared_clean_resident = clean_bytes; + break; + case SM_SHARED: + case SM_PRIVATE_ALIASED: + case SM_TRUESHARED: + case SM_SHARED_ALIASED: + region->byte_stats_shared_dirty_resident = dirty_bytes; + region->byte_stats_shared_clean_resident = clean_bytes; + break; + case SM_EMPTY: + break; + default: + NOTREACHED(); + break; + } +} + +// Creates VMRegions from mach_vm_region_recurse. Returns whether the operation +// succeeded. +bool GetAllRegions(std::vector* regions) { + const int pid = getpid(); + task_t task = mach_task_self(); + mach_vm_size_t size = 0; + vm_region_submap_info_64 info; + natural_t depth = 1; + mach_msg_type_number_t count = sizeof(info); + for (mach_vm_address_t address = MACH_VM_MIN_ADDRESS;; address += size) { + memset(&info, 0, sizeof(info)); + kern_return_t kr = mach_vm_region_recurse( + task, &address, &size, &depth, + reinterpret_cast(&info), &count); + if (kr == KERN_INVALID_ADDRESS) // nothing else left + break; + if (kr != KERN_SUCCESS) // something bad + return false; + if (info.is_submap) { + size = 0; + ++depth; + continue; + } + + VMRegion region; + PopulateByteStats(®ion, info); + + if (info.protection & VM_PROT_READ) + region.protection_flags |= VMRegion::kProtectionFlagsRead; + if (info.protection & VM_PROT_WRITE) + region.protection_flags |= VMRegion::kProtectionFlagsWrite; + if (info.protection & VM_PROT_EXECUTE) + region.protection_flags |= VMRegion::kProtectionFlagsExec; + + char buffer[MAXPATHLEN]; + int length = proc_regionfilename(pid, address, buffer, MAXPATHLEN); + if (length != 0) + region.mapped_file.assign(buffer, length); + + region.byte_stats_swapped = info.pages_swapped_out * PAGE_SIZE; + region.start_address = address; + region.size_in_bytes = size; + regions->push_back(region); + + base::CheckedNumeric numeric(address); + numeric += size; + if (!numeric.IsValid()) + return false; + address = numeric.ValueOrDie(); + } + return true; +} + +void AddRegionByteStats(VMRegion* dest, const VMRegion& source) { + dest->byte_stats_private_dirty_resident += + source.byte_stats_private_dirty_resident; + dest->byte_stats_private_clean_resident += + source.byte_stats_private_clean_resident; + dest->byte_stats_shared_dirty_resident += + source.byte_stats_shared_dirty_resident; + dest->byte_stats_shared_clean_resident += + source.byte_stats_shared_clean_resident; + dest->byte_stats_swapped += source.byte_stats_swapped; + dest->byte_stats_proportional_resident += + source.byte_stats_proportional_resident; +} + +} // namespace + +bool ProcessMetricsMemoryDumpProvider::DumpProcessMemoryMaps( + const base::trace_event::MemoryDumpArgs& args, + base::trace_event::ProcessMemoryDump* pmd) { + using VMRegion = base::trace_event::ProcessMemoryMaps::VMRegion; + + std::vector dyld_regions; + if (!GetDyldRegions(&dyld_regions)) + return false; + std::vector all_regions; + if (!GetAllRegions(&all_regions)) + return false; + + // Merge information from dyld regions and all regions. + for (const VMRegion& region : all_regions) { + bool skip = false; + const bool in_shared_region = IsAddressInSharedRegion(region.start_address); + for (VMRegion& dyld_region : dyld_regions) { + // If this region is fully contained in a dyld region, then add the bytes + // stats. + if (IsRegionContainedInRegion(region, dyld_region)) { + AddRegionByteStats(&dyld_region, region); + skip = true; + break; + } + + // Check to see if the region is likely used for the dyld shared cache. + if (in_shared_region) { + // This region is likely used for the dyld shared cache. Don't record + // any byte stats since: + // 1. It's not possible to figure out which dyld regions the byte + // stats correspond to. + // 2. The region is likely shared by non-Chrome processes, so there's + // no point in charging the pages towards Chrome. + if (DoRegionsIntersect(region, dyld_region)) { + skip = true; + break; + } + } + } + if (skip) + continue; + pmd->process_mmaps()->AddVMRegion(region); + } + + for (VMRegion& region : dyld_regions) { + pmd->process_mmaps()->AddVMRegion(region); + } + + pmd->set_has_process_mmaps(); + return true; +} +#endif // defined(OS_MACOSX) + // static void ProcessMetricsMemoryDumpProvider::RegisterForProcess( base::ProcessId process) { - std::unique_ptr metrics_provider( - new ProcessMetricsMemoryDumpProvider(process)); - base::trace_event::MemoryDumpProvider::Options options; - options.target_pid = process; - options.is_fast_polling_supported = true; - base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( - metrics_provider.get(), "ProcessMemoryMetrics", nullptr, options); + std::unique_ptr owned_provider; + if (factory_for_testing) { + owned_provider = factory_for_testing(process); + } else { + owned_provider = std::unique_ptr( + new ProcessMetricsMemoryDumpProvider(process)); + } + + ProcessMetricsMemoryDumpProvider* provider = owned_provider.get(); bool did_insert = g_dump_providers_map.Get() - .insert(std::make_pair(process, std::move(metrics_provider))) + .insert(std::make_pair(process, std::move(owned_provider))) .second; if (!did_insert) { DLOG(ERROR) << "ProcessMetricsMemoryDumpProvider already registered for " << (process == base::kNullProcessId ? "current process" : "process id " + base::IntToString(process)); + return; } + base::trace_event::MemoryDumpProvider::Options options; + options.target_pid = process; + options.is_fast_polling_supported = true; + base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( + provider, "ProcessMemoryMetrics", nullptr, options); } // static @@ -267,11 +599,9 @@ bool ProcessMetricsMemoryDumpProvider::OnMemoryDump( base::trace_event::ProcessMemoryDump* pmd) { bool res = DumpProcessTotals(args, pmd); -#if defined(OS_LINUX) || defined(OS_ANDROID) if (args.level_of_detail == base::trace_event::MemoryDumpLevelOfDetail::DETAILED) res &= DumpProcessMemoryMaps(args, pmd); -#endif return res; } diff --git a/chromium/components/tracing/common/process_metrics_memory_dump_provider.h b/chromium/components/tracing/common/process_metrics_memory_dump_provider.h index 4874685f62d..730a9be97d2 100644 --- a/chromium/components/tracing/common/process_metrics_memory_dump_provider.h +++ b/chromium/components/tracing/common/process_metrics_memory_dump_provider.h @@ -37,14 +37,30 @@ class TRACING_EXPORT ProcessMetricsMemoryDumpProvider void PollFastMemoryTotal(uint64_t* memory_total) override; void SuspendFastMemoryPolling() override; + protected: + ProcessMetricsMemoryDumpProvider(base::ProcessId process); + private: + using FactoryFunction = + std::unique_ptr (*)(base::ProcessId); + FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest, ParseProcSmaps); FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest, DumpRSS); FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest, TestPollFastMemoryTotal); - - ProcessMetricsMemoryDumpProvider(base::ProcessId process); +#if defined(OS_MACOSX) + FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest, + TestMachOReading); + FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest, + NoDuplicateRegions); +#elif defined(OS_WIN) + FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest, + TestWinModuleReading); +#elif defined(OS_LINUX) || defined(OS_ANDROID) + FRIEND_TEST_ALL_PREFIXES(ProcessMetricsMemoryDumpProviderTest, + DoubleRegister); +#endif bool DumpProcessTotals(const base::trace_event::MemoryDumpArgs& args, base::trace_event::ProcessMemoryDump* pmd); @@ -52,6 +68,7 @@ class TRACING_EXPORT ProcessMetricsMemoryDumpProvider base::trace_event::ProcessMemoryDump* pmd); static uint64_t rss_bytes_for_testing; + static FactoryFunction factory_for_testing; #if defined(OS_LINUX) || defined(OS_ANDROID) static FILE* proc_smaps_for_testing; diff --git a/chromium/components/tracing/common/process_metrics_memory_dump_provider_unittest.cc b/chromium/components/tracing/common/process_metrics_memory_dump_provider_unittest.cc index 5b1b25f1d0e..77124349694 100644 --- a/chromium/components/tracing/common/process_metrics_memory_dump_provider_unittest.cc +++ b/chromium/components/tracing/common/process_metrics_memory_dump_provider_unittest.cc @@ -7,16 +7,27 @@ #include #include +#include #include "base/files/file_util.h" #include "base/memory/ptr_util.h" #include "base/process/process_metrics.h" +#include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/process_memory_dump.h" #include "base/trace_event/process_memory_maps.h" #include "base/trace_event/process_memory_totals.h" #include "base/trace_event/trace_event_argument.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(OS_MACOSX) +#include +#include +#endif + +#if defined(OS_WIN) +#include +#endif + namespace tracing { #if defined(OS_LINUX) || defined(OS_ANDROID) @@ -124,6 +135,25 @@ void CreateTempFileWithContents(const char* contents, base::ScopedFILE* file) { } // namespace #endif // defined(OS_LINUX) || defined(OS_ANDROID) +class MockMemoryDumpProvider : public ProcessMetricsMemoryDumpProvider { + public: + MockMemoryDumpProvider(base::ProcessId process); + ~MockMemoryDumpProvider() override; +}; + +std::unordered_set g_live_mocks; +std::unordered_set g_dead_mocks; + +MockMemoryDumpProvider::MockMemoryDumpProvider(base::ProcessId process) + : ProcessMetricsMemoryDumpProvider(process) { + g_live_mocks.insert(this); +} + +MockMemoryDumpProvider::~MockMemoryDumpProvider() { + g_live_mocks.erase(this); + g_dead_mocks.insert(this); +} + TEST(ProcessMetricsMemoryDumpProviderTest, DumpRSS) { const base::trace_event::MemoryDumpArgs high_detail_args = { base::trace_event::MemoryDumpLevelOfDetail::DETAILED}; @@ -235,6 +265,27 @@ TEST(ProcessMetricsMemoryDumpProviderTest, ParseProcSmaps) { EXPECT_EQ(4 * 1024UL, regions_2[0].byte_stats_private_dirty_resident); EXPECT_EQ(0 * 1024UL, regions_2[0].byte_stats_swapped); } + +TEST(ProcessMetricsMemoryDumpProviderTest, DoubleRegister) { + auto factory = [](base::ProcessId process) { + return std::unique_ptr( + new MockMemoryDumpProvider(process)); + }; + ProcessMetricsMemoryDumpProvider::factory_for_testing = factory; + ProcessMetricsMemoryDumpProvider::RegisterForProcess(1); + ProcessMetricsMemoryDumpProvider::RegisterForProcess(1); + ASSERT_EQ(1u, g_live_mocks.size()); + ASSERT_EQ(1u, g_dead_mocks.size()); + auto* manager = base::trace_event::MemoryDumpManager::GetInstance(); + MockMemoryDumpProvider* live_mock = *g_live_mocks.begin(); + EXPECT_TRUE(manager->IsDumpProviderRegisteredForTesting(live_mock)); + auto* dead_mock = *g_dead_mocks.begin(); + EXPECT_FALSE(manager->IsDumpProviderRegisteredForTesting(dead_mock)); + ProcessMetricsMemoryDumpProvider::UnregisterForProcess(1); + g_live_mocks.clear(); + g_dead_mocks.clear(); +} + #endif // defined(OS_LINUX) || defined(OS_ANDROID) TEST(ProcessMetricsMemoryDumpProviderTest, TestPollFastMemoryTotal) { @@ -269,4 +320,118 @@ TEST(ProcessMetricsMemoryDumpProviderTest, TestPollFastMemoryTotal) { #endif } +#if defined(OS_WIN) + +void DummyFunction() {} + +TEST(ProcessMetricsMemoryDumpProviderTest, TestWinModuleReading) { + using VMRegion = base::trace_event::ProcessMemoryMaps::VMRegion; + + ProcessMetricsMemoryDumpProvider mdp(base::kNullProcessId); + base::trace_event::MemoryDumpArgs args; + base::trace_event::ProcessMemoryDump dump(nullptr, args); + ASSERT_TRUE(mdp.DumpProcessMemoryMaps(args, &dump)); + ASSERT_TRUE(dump.has_process_mmaps()); + + wchar_t module_name[MAX_PATH]; + DWORD result = GetModuleFileName(nullptr, module_name, MAX_PATH); + ASSERT_TRUE(result); + std::string executable_name = base::SysWideToNativeMB(module_name); + + HMODULE module_containing_dummy = nullptr; + uintptr_t dummy_function_address = + reinterpret_cast(&DummyFunction); + result = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + reinterpret_cast(dummy_function_address), + &module_containing_dummy); + ASSERT_TRUE(result); + result = GetModuleFileName(nullptr, module_name, MAX_PATH); + ASSERT_TRUE(result); + std::string module_containing_dummy_name = + base::SysWideToNativeMB(module_name); + + bool found_executable = false; + bool found_region_with_dummy = false; + for (const VMRegion& region : dump.process_mmaps()->vm_regions()) { + EXPECT_NE(0u, region.start_address); + EXPECT_NE(0u, region.size_in_bytes); + + if (region.mapped_file.find(executable_name) != std::string::npos) + found_executable = true; + + if (dummy_function_address >= region.start_address && + dummy_function_address < region.start_address + region.size_in_bytes) { + found_region_with_dummy = true; + EXPECT_EQ(module_containing_dummy_name, region.mapped_file); + } + } + EXPECT_TRUE(found_executable); + EXPECT_TRUE(found_region_with_dummy); +} +#endif + +#if defined(OS_MACOSX) +TEST(ProcessMetricsMemoryDumpProviderTest, TestMachOReading) { + using VMRegion = base::trace_event::ProcessMemoryMaps::VMRegion; + ProcessMetricsMemoryDumpProvider mdp(base::kNullProcessId); + base::trace_event::MemoryDumpArgs args; + base::trace_event::ProcessMemoryDump dump(nullptr, args); + ASSERT_TRUE(mdp.DumpProcessMemoryMaps(args, &dump)); + ASSERT_TRUE(dump.has_process_mmaps()); + uint32_t size = 100; + char full_path[size]; + int result = _NSGetExecutablePath(full_path, &size); + ASSERT_EQ(0, result); + std::string name = basename(full_path); + + uint64_t components_unittests_resident_pages = 0; + bool found_appkit = false; + for (const VMRegion& region : dump.process_mmaps()->vm_regions()) { + EXPECT_NE(0u, region.start_address); + EXPECT_NE(0u, region.size_in_bytes); + + EXPECT_LT(region.size_in_bytes, 1ull << 32); + uint32_t required_protection_flags = + VMRegion::kProtectionFlagsRead | VMRegion::kProtectionFlagsExec; + if (region.mapped_file.find(name) != std::string::npos && + region.protection_flags == required_protection_flags) { + components_unittests_resident_pages += + region.byte_stats_private_dirty_resident + + region.byte_stats_shared_dirty_resident + + region.byte_stats_private_clean_resident + + region.byte_stats_shared_clean_resident; + } + + if (region.mapped_file.find("AppKit") != std::string::npos) { + found_appkit = true; + } + } + EXPECT_GT(components_unittests_resident_pages, 0u); + EXPECT_TRUE(found_appkit); +} + +TEST(ProcessMetricsMemoryDumpProviderTest, NoDuplicateRegions) { + using VMRegion = base::trace_event::ProcessMemoryMaps::VMRegion; + ProcessMetricsMemoryDumpProvider mdp(base::kNullProcessId); + base::trace_event::MemoryDumpArgs args; + base::trace_event::ProcessMemoryDump dump(nullptr, args); + ASSERT_TRUE(mdp.DumpProcessMemoryMaps(args, &dump)); + ASSERT_TRUE(dump.has_process_mmaps()); + + std::vector regions; + regions.reserve(dump.process_mmaps()->vm_regions().size()); + for (const VMRegion& region : dump.process_mmaps()->vm_regions()) + regions.push_back(region); + std::sort(regions.begin(), regions.end(), + [](const VMRegion& a, const VMRegion& b) -> bool { + return a.start_address < b.start_address; + }); + uint64_t last_address = 0; + for (const VMRegion& region : regions) { + EXPECT_GE(region.start_address, last_address); + last_address = region.start_address + region.size_in_bytes; + } +} + +#endif // defined(OS_MACOSX) } // namespace tracing diff --git a/chromium/components/tracing/common/trace_config_file.cc b/chromium/components/tracing/common/trace_config_file.cc new file mode 100644 index 00000000000..180834461f9 --- /dev/null +++ b/chromium/components/tracing/common/trace_config_file.cc @@ -0,0 +1,146 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/tracing/common/trace_config_file.h" + +#include + +#include +#include + +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/json/json_reader.h" +#include "base/logging.h" +#include "base/memory/singleton.h" +#include "base/values.h" +#include "build/build_config.h" +#include "components/tracing/common/tracing_switches.h" + +namespace tracing { + +namespace { + +// Maximum trace config file size that will be loaded, in bytes. +const size_t kTraceConfigFileSizeLimit = 64 * 1024; + +// Trace config file path: +// - Android: /data/local/chrome-trace-config.json +// - Others: specified by --trace-config-file flag. +#if defined(OS_ANDROID) +const base::FilePath::CharType kAndroidTraceConfigFile[] = + FILE_PATH_LITERAL("/data/local/chrome-trace-config.json"); +#endif + +const base::FilePath::CharType kDefaultResultFile[] = + FILE_PATH_LITERAL("chrometrace.log"); + +// String parameters that can be used to parse the trace config file content. +const char kTraceConfigParam[] = "trace_config"; +const char kStartupDurationParam[] = "startup_duration"; +const char kResultFileParam[] = "result_file"; + +} // namespace + +TraceConfigFile* TraceConfigFile::GetInstance() { + return base::Singleton>::get(); +} + +TraceConfigFile::TraceConfigFile() + : is_enabled_(false), + trace_config_(base::trace_event::TraceConfig()), + startup_duration_(0), + result_file_(kDefaultResultFile) { +#if defined(OS_ANDROID) + base::FilePath trace_config_file(kAndroidTraceConfigFile); +#else + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + if (!command_line.HasSwitch(switches::kTraceConfigFile) || + command_line.HasSwitch(switches::kTraceStartup) || + command_line.HasSwitch(switches::kTraceShutdown)) { + return; + } + base::FilePath trace_config_file = + command_line.GetSwitchValuePath(switches::kTraceConfigFile); +#endif + + if (trace_config_file.empty()) { + // If the trace config file path is not specified, trace Chrome with the + // default configuration for 5 sec. + startup_duration_ = 5; + is_enabled_ = true; + DLOG(WARNING) << "Use default trace config."; + return; + } + + if (!base::PathExists(trace_config_file)) { + DLOG(WARNING) << "The trace config file does not exist."; + return; + } + + std::string trace_config_file_content; + if (!base::ReadFileToStringWithMaxSize(trace_config_file, + &trace_config_file_content, + kTraceConfigFileSizeLimit)) { + DLOG(WARNING) << "Cannot read the trace config file correctly."; + return; + } + is_enabled_ = ParseTraceConfigFileContent(trace_config_file_content); + if (!is_enabled_) + DLOG(WARNING) << "Cannot parse the trace config file correctly."; +} + +TraceConfigFile::~TraceConfigFile() {} + +bool TraceConfigFile::ParseTraceConfigFileContent(const std::string& content) { + std::unique_ptr value(base::JSONReader::Read(content)); + if (!value || !value->IsType(base::Value::Type::DICTIONARY)) + return false; + + std::unique_ptr dict( + static_cast(value.release())); + + base::DictionaryValue* trace_config_dict = NULL; + if (!dict->GetDictionary(kTraceConfigParam, &trace_config_dict)) + return false; + + trace_config_ = base::trace_event::TraceConfig(*trace_config_dict); + + if (!dict->GetInteger(kStartupDurationParam, &startup_duration_)) + startup_duration_ = 0; + + if (startup_duration_ < 0) + startup_duration_ = 0; + + base::FilePath::StringType result_file_str; + if (dict->GetString(kResultFileParam, &result_file_str)) + result_file_ = base::FilePath(result_file_str); + + return true; +} + +bool TraceConfigFile::IsEnabled() const { + return is_enabled_; +} + +base::trace_event::TraceConfig TraceConfigFile::GetTraceConfig() const { + DCHECK(IsEnabled()); + return trace_config_; +} + +int TraceConfigFile::GetStartupDuration() const { + DCHECK(IsEnabled()); + return startup_duration_; +} + +#if !defined(OS_ANDROID) +base::FilePath TraceConfigFile::GetResultFile() const { + DCHECK(IsEnabled()); + return result_file_; +} +#endif + +} // namespace tracing diff --git a/chromium/components/tracing/common/trace_config_file.h b/chromium/components/tracing/common/trace_config_file.h new file mode 100644 index 00000000000..31adcef508c --- /dev/null +++ b/chromium/components/tracing/common/trace_config_file.h @@ -0,0 +1,98 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_TRACING_COMMON_TRACE_CONFIG_FILE_H_ +#define COMPONENTS_TRACING_COMMON_TRACE_CONFIG_FILE_H_ + +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/trace_event/trace_config.h" +#include "build/build_config.h" +#include "components/tracing/tracing_export.h" + +namespace base { +template +struct DefaultSingletonTraits; +} // namespace base + +namespace tracing { + +// TraceConfigFile is a singleton that contains the configurations of tracing. +// One can create a trace config file and use it to configure startup and/or +// shutdown tracing. +// +// The trace config file should be JSON formated. One example is: +// { +// "trace_config": { +// "record_mode": "record-until-full", +// "included_categories": ["cc", "skia"] +// }, +// "startup_duration": 5, +// "result_file": "chrometrace.log" +// } +// +// trace_config: The configuration of tracing. Please see the details in +// base/trace_event/trace_config.h. +// +// startup_duration: The duration for startup tracing in terms of seconds. +// Tracing will stop automatically after the duration. If this +// value is not specified, the duration is 0 and one needs +// to stop tracing by other ways, e.g., by DevTools, or get +// the result file after shutting the browser down. +// +// result_file: The file that contains the trace log. The default result +// file path is chrometrace.log. Chrome will dump the trace +// log to this file +// 1) after startup_duration if it is specified; +// 2) or after browser shutdown if startup duration is 0. +// One can also stop tracing and get the result by other ways, +// e.g., by DevTools. In that case, the trace log will not be +// saved to this file. +// Notice: This is not supported on Android. The result file +// path will be generated by tracing controller. +// +// The trace config file can be specified by the --trace-config-file flag on +// most platforms except on Android, e.g., --trace-config-file=path/to/file/. +// This flag should not be used with --trace-startup or --trace-shutdown. If +// those two flags are used, --trace-config-file flag will be ignored. If the +// --trace-config-file flag is used without the file path, Chrome will do +// startup tracing with 5 seconds' startup duration. +// +// On Android, Chrome does not read the --trace-config-file flag, because not +// all Chrome based browsers read customized flag, e.g., Android WebView. Chrome +// on Android reads from a fixed file location: +// /data/local/chrome-trace-config.json +// If this file exists, Chrome will start tracing according to the configuration +// specified in the file, otherwise, Chrome will not start tracing. +class TRACING_EXPORT TraceConfigFile { + public: + static TraceConfigFile* GetInstance(); + + bool IsEnabled() const; + base::trace_event::TraceConfig GetTraceConfig() const; + int GetStartupDuration() const; +#if !defined(OS_ANDROID) + base::FilePath GetResultFile() const; +#endif + + private: + // This allows constructor and destructor to be private and usable only + // by the Singleton class. + friend struct base::DefaultSingletonTraits; + TraceConfigFile(); + ~TraceConfigFile(); + + bool ParseTraceConfigFileContent(const std::string& content); + + bool is_enabled_; + base::trace_event::TraceConfig trace_config_; + int startup_duration_; + base::FilePath result_file_; + + DISALLOW_COPY_AND_ASSIGN(TraceConfigFile); +}; + +} // namespace tracing + +#endif // COMPONENTS_TRACING_COMMON_TRACE_CONFIG_FILE_H_ diff --git a/chromium/components/tracing/common/trace_config_file_unittest.cc b/chromium/components/tracing/common/trace_config_file_unittest.cc new file mode 100644 index 00000000000..2c7df54d2e1 --- /dev/null +++ b/chromium/components/tracing/common/trace_config_file_unittest.cc @@ -0,0 +1,250 @@ +// Copyright (c) 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/tracing/common/trace_config_file.h" + +#include "base/at_exit.h" +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "components/tracing/common/tracing_switches.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace tracing { + +namespace { + +const char kTraceConfig[] = + "{" + "\"enable_argument_filter\":true," + "\"enable_systrace\":true," + "\"excluded_categories\":[\"excluded\",\"exc_pattern*\"]," + "\"included_categories\":[\"included\"," + "\"inc_pattern*\"," + "\"disabled-by-default-cc\"]," + "\"record_mode\":\"record-continuously\"," + "\"synthetic_delays\":[\"test.Delay1;16\",\"test.Delay2;32\"]" + "}"; + +std::string GetTraceConfigFileContent(std::string trace_config, + std::string startup_duration, + std::string result_file) { + std::string content = "{"; + if (!trace_config.empty()) + content += "\"trace_config\":" + trace_config; + + if (!startup_duration.empty()) { + if (content != "{") + content += ","; + content += "\"startup_duration\":" + startup_duration; + } + + if (!result_file.empty()) { + if (content != "{") + content += ","; + content += "\"result_file\":\"" + result_file + "\""; + } + + content += "}"; + return content; +} + +} // namespace + +TEST(TraceConfigFileTest, TraceStartupEnabled) { + base::ShadowingAtExitManager sem; + base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kTraceStartup); + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kTraceConfigFile); + + EXPECT_FALSE(TraceConfigFile::GetInstance()->IsEnabled()); +} + +TEST(TraceConfigFileTest, TraceShutdownEnabled) { + base::ShadowingAtExitManager sem; + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kTraceShutdown); + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kTraceConfigFile); + + EXPECT_FALSE(TraceConfigFile::GetInstance()->IsEnabled()); +} + +TEST(TraceConfigFileTest, TraceConfigFileNotEnabled) { + base::ShadowingAtExitManager sem; + EXPECT_FALSE(TraceConfigFile::GetInstance()->IsEnabled()); +} + +TEST(TraceConfigFileTest, TraceConfigFileEnabledWithoutPath) { + base::ShadowingAtExitManager sem; + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kTraceConfigFile); + + ASSERT_TRUE(TraceConfigFile::GetInstance()->IsEnabled()); + EXPECT_EQ(base::trace_event::TraceConfig().ToString(), + TraceConfigFile::GetInstance()->GetTraceConfig().ToString()); + EXPECT_EQ(5, TraceConfigFile::GetInstance()->GetStartupDuration()); + EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("chrometrace.log")), + TraceConfigFile::GetInstance()->GetResultFile()); +} + +TEST(TraceConfigFileTest, TraceConfigFileEnabledWithInvalidPath) { + base::ShadowingAtExitManager sem; + base::CommandLine::ForCurrentProcess()->AppendSwitchPath( + switches::kTraceConfigFile, + base::FilePath(FILE_PATH_LITERAL("invalid-trace-config-file-path"))); + + EXPECT_FALSE(TraceConfigFile::GetInstance()->IsEnabled()); +} + +TEST(TraceConfigFileTest, ValidContent) { + base::ShadowingAtExitManager sem; + std::string content = + GetTraceConfigFileContent(kTraceConfig, "10", "trace_result_file.log"); + + base::FilePath trace_config_file; + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + ASSERT_TRUE( + base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file)); + ASSERT_NE(-1, base::WriteFile(trace_config_file, content.c_str(), + (int)content.length())); + base::CommandLine::ForCurrentProcess()->AppendSwitchPath( + switches::kTraceConfigFile, trace_config_file); + + ASSERT_TRUE(TraceConfigFile::GetInstance()->IsEnabled()); + EXPECT_STREQ( + kTraceConfig, + TraceConfigFile::GetInstance()->GetTraceConfig().ToString().c_str()); + EXPECT_EQ(10, TraceConfigFile::GetInstance()->GetStartupDuration()); + EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("trace_result_file.log")), + TraceConfigFile::GetInstance()->GetResultFile()); +} + +TEST(TraceConfigFileTest, ValidContentWithOnlyTraceConfig) { + base::ShadowingAtExitManager sem; + std::string content = GetTraceConfigFileContent(kTraceConfig, "", ""); + + base::FilePath trace_config_file; + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + ASSERT_TRUE( + base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file)); + ASSERT_NE(-1, base::WriteFile(trace_config_file, content.c_str(), + (int)content.length())); + base::CommandLine::ForCurrentProcess()->AppendSwitchPath( + switches::kTraceConfigFile, trace_config_file); + + ASSERT_TRUE(TraceConfigFile::GetInstance()->IsEnabled()); + EXPECT_STREQ( + kTraceConfig, + TraceConfigFile::GetInstance()->GetTraceConfig().ToString().c_str()); + EXPECT_EQ(0, TraceConfigFile::GetInstance()->GetStartupDuration()); + EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("chrometrace.log")), + TraceConfigFile::GetInstance()->GetResultFile()); +} + +TEST(TraceConfigFileTest, ContentWithAbsoluteResultFilePath) { + base::ShadowingAtExitManager sem; + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + + const base::FilePath result_file_path = + temp_dir.GetPath().Append(FILE_PATH_LITERAL("trace_result_file.log")); + ASSERT_TRUE(result_file_path.IsAbsolute()); + + std::string result_file_path_str = result_file_path.AsUTF8Unsafe(); + auto it = + std::find(result_file_path_str.begin(), result_file_path_str.end(), '\\'); + while (it != result_file_path_str.end()) { + auto it2 = result_file_path_str.insert(it, '\\'); + it = std::find(it2 + 2, result_file_path_str.end(), '\\'); + } + std::string content = + GetTraceConfigFileContent(kTraceConfig, "10", result_file_path_str); + + base::FilePath trace_config_file; + ASSERT_TRUE( + base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file)); + ASSERT_NE(-1, base::WriteFile(trace_config_file, content.c_str(), + (int)content.length())); + base::CommandLine::ForCurrentProcess()->AppendSwitchPath( + switches::kTraceConfigFile, trace_config_file); + + ASSERT_TRUE(TraceConfigFile::GetInstance()->IsEnabled()); + EXPECT_EQ(result_file_path, TraceConfigFile::GetInstance()->GetResultFile()); +} + +TEST(TraceConfigFileTest, ContentWithNegtiveDuration) { + base::ShadowingAtExitManager sem; + std::string content = GetTraceConfigFileContent(kTraceConfig, "-1", ""); + + base::FilePath trace_config_file; + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + ASSERT_TRUE( + base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file)); + ASSERT_NE(-1, base::WriteFile(trace_config_file, content.c_str(), + (int)content.length())); + base::CommandLine::ForCurrentProcess()->AppendSwitchPath( + switches::kTraceConfigFile, trace_config_file); + + ASSERT_TRUE(TraceConfigFile::GetInstance()->IsEnabled()); + EXPECT_STREQ( + kTraceConfig, + TraceConfigFile::GetInstance()->GetTraceConfig().ToString().c_str()); + EXPECT_EQ(0, TraceConfigFile::GetInstance()->GetStartupDuration()); + EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("chrometrace.log")), + TraceConfigFile::GetInstance()->GetResultFile()); +} + +TEST(TraceConfigFileTest, ContentWithoutTraceConfig) { + base::ShadowingAtExitManager sem; + std::string content = + GetTraceConfigFileContent("", "10", "trace_result_file.log"); + + base::FilePath trace_config_file; + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + ASSERT_TRUE( + base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file)); + ASSERT_NE(-1, base::WriteFile(trace_config_file, content.c_str(), + (int)content.length())); + base::CommandLine::ForCurrentProcess()->AppendSwitchPath( + switches::kTraceConfigFile, trace_config_file); + + EXPECT_FALSE(TraceConfigFile::GetInstance()->IsEnabled()); +} + +TEST(TraceConfigFileTest, InvalidContent) { + base::ShadowingAtExitManager sem; + std::string content = "invalid trace config file content"; + + base::FilePath trace_config_file; + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + ASSERT_TRUE( + base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file)); + ASSERT_NE(-1, base::WriteFile(trace_config_file, content.c_str(), + (int)content.length())); + base::CommandLine::ForCurrentProcess()->AppendSwitchPath( + switches::kTraceConfigFile, trace_config_file); + + EXPECT_FALSE(TraceConfigFile::GetInstance()->IsEnabled()); +} + +TEST(TraceConfigFileTest, EmptyContent) { + base::ShadowingAtExitManager sem; + base::FilePath trace_config_file; + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + ASSERT_TRUE( + base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file)); + base::CommandLine::ForCurrentProcess()->AppendSwitchPath( + switches::kTraceConfigFile, trace_config_file); + + EXPECT_FALSE(TraceConfigFile::GetInstance()->IsEnabled()); +} + +} // namespace tracing diff --git a/chromium/components/tracing/common/trace_startup.cc b/chromium/components/tracing/common/trace_startup.cc new file mode 100644 index 00000000000..2c8ae37025c --- /dev/null +++ b/chromium/components/tracing/common/trace_startup.cc @@ -0,0 +1,48 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/tracing/common/trace_startup.h" + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/trace_event/memory_dump_manager.h" +#include "base/trace_event/trace_log.h" +#include "components/tracing/common/trace_config_file.h" +#include "components/tracing/common/trace_to_console.h" +#include "components/tracing/common/tracing_switches.h" + +namespace tracing { + +void EnableStartupTracingIfNeeded(bool can_access_file_system) { + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + + // Enables heap profiling if "--enable-heap-profiling" flag is passed. + base::trace_event::MemoryDumpManager::GetInstance() + ->EnableHeapProfilingIfNeeded(); + + if (command_line.HasSwitch(switches::kTraceStartup)) { + base::trace_event::TraceConfig trace_config( + command_line.GetSwitchValueASCII(switches::kTraceStartup), + base::trace_event::RECORD_UNTIL_FULL); + base::trace_event::TraceLog::GetInstance()->SetEnabled( + trace_config, base::trace_event::TraceLog::RECORDING_MODE); + } else if (command_line.HasSwitch(switches::kTraceToConsole)) { + base::trace_event::TraceConfig trace_config = + tracing::GetConfigForTraceToConsole(); + LOG(ERROR) << "Start " << switches::kTraceToConsole + << " with CategoryFilter '" + << trace_config.ToCategoryFilterString() << "'."; + base::trace_event::TraceLog::GetInstance()->SetEnabled( + trace_config, base::trace_event::TraceLog::RECORDING_MODE); + } else if (can_access_file_system && + tracing::TraceConfigFile::GetInstance()->IsEnabled()) { + // This checks kTraceConfigFile switch. + base::trace_event::TraceLog::GetInstance()->SetEnabled( + tracing::TraceConfigFile::GetInstance()->GetTraceConfig(), + base::trace_event::TraceLog::RECORDING_MODE); + } +} + +} // namespace tracing diff --git a/chromium/components/tracing/common/trace_startup.h b/chromium/components/tracing/common/trace_startup.h new file mode 100644 index 00000000000..4a388d8d44f --- /dev/null +++ b/chromium/components/tracing/common/trace_startup.h @@ -0,0 +1,19 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_TRACING_COMMON_TRACE_STARTUP_H +#define COMPONENTS_TRACING_COMMON_TRACE_STARTUP_H + +#include "components/tracing/tracing_export.h" + +namespace tracing { + +// Enables TraceLog with config based on the command line flags of the process. +// If |can_access_file_system| is false then TraceLog is not enabled in case it +// is required to read config file to start tracing. +void TRACING_EXPORT EnableStartupTracingIfNeeded(bool can_access_file_system); + +} // namespace tracing + +#endif // COMPONENTS_TRACING_COMMON_TRACE_STARTUP_H -- cgit v1.2.1