diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-04-05 17:15:33 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-04-11 07:47:18 +0000 |
commit | 7324afb043a0b1e623d8e8eb906cdc53bdeb4685 (patch) | |
tree | a3fe2d74ea9c9e142c390dac4ca0e219382ace46 /chromium/components/tracing | |
parent | 6a4cabb866f66d4128a97cdc6d9d08ce074f1247 (diff) | |
download | qtwebengine-chromium-7324afb043a0b1e623d8e8eb906cdc53bdeb4685.tar.gz |
BASELINE: Update Chromium to 58.0.3029.54
Change-Id: I67f57065a7afdc8e4614adb5c0230281428df4d1
Reviewed-by: Peter Varga <pvarga@inf.u-szeged.hu>
Diffstat (limited to 'chromium/components/tracing')
-rw-r--r-- | chromium/components/tracing/BUILD.gn | 8 | ||||
-rw-r--r-- | chromium/components/tracing/OWNERS | 3 | ||||
-rw-r--r-- | chromium/components/tracing/common/process_metrics_memory_dump_provider.cc | 350 | ||||
-rw-r--r-- | chromium/components/tracing/common/process_metrics_memory_dump_provider.h | 21 | ||||
-rw-r--r-- | chromium/components/tracing/common/process_metrics_memory_dump_provider_unittest.cc | 165 | ||||
-rw-r--r-- | chromium/components/tracing/common/trace_config_file.cc (renamed from chromium/components/tracing/browser/trace_config_file.cc) | 11 | ||||
-rw-r--r-- | chromium/components/tracing/common/trace_config_file.h (renamed from chromium/components/tracing/browser/trace_config_file.h) | 9 | ||||
-rw-r--r-- | chromium/components/tracing/common/trace_config_file_unittest.cc (renamed from chromium/components/tracing/browser/trace_config_file_unittest.cc) | 52 | ||||
-rw-r--r-- | chromium/components/tracing/common/trace_startup.cc | 48 | ||||
-rw-r--r-- | chromium/components/tracing/common/trace_startup.h | 19 |
10 files changed, 635 insertions, 51 deletions
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/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 <libproc.h> +#include <mach/mach.h> +#include <mach/mach_vm.h> +#include <mach/shared_region.h> +#include <sys/param.h> + +#include <mach-o/dyld_images.h> +#include <mach-o/loader.h> +#include <mach/mach.h> + +#include "base/numerics/safe_math.h" +#endif // defined(OS_MACOSX) + +#if defined(OS_WIN) +#include <psapi.h> +#include <tchar.h> +#include <windows.h> + +#include <base/strings/sys_string_conversions.h> +#include <base/win/win_util.h> +#endif // defined(OS_WIN) + namespace tracing { namespace { @@ -190,6 +213,10 @@ std::unique_ptr<base::ProcessMetrics> 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<HMODULE> 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<uint64_t>(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<VMRegion>* 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<task_info_t>(&dyld_info), &count); + if (kr != KERN_SUCCESS) + return false; + + const struct dyld_all_image_infos* all_image_infos = + reinterpret_cast<const struct dyld_all_image_infos*>( + 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<const struct mach_header_64* const>( + all_image_infos->infoArray[i].imageLoadAddress); + + uint64_t next_command = reinterpret_cast<uint64_t>(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<const struct load_command*>(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<const segment_command_64*>(load_cmd); + if (strcmp(seg->segname, SEG_PAGEZERO) == 0) + continue; + if (strcmp(seg->segname, SEG_TEXT) == 0) { + slide = reinterpret_cast<uint64_t>(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<VMRegion>* 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<vm_region_info_t>(&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<mach_vm_address_t> 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<VMRegion> dyld_regions; + if (!GetDyldRegions(&dyld_regions)) + return false; + std::vector<VMRegion> 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<ProcessMetricsMemoryDumpProvider> 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<ProcessMetricsMemoryDumpProvider> owned_provider; + if (factory_for_testing) { + owned_provider = factory_for_testing(process); + } else { + owned_provider = std::unique_ptr<ProcessMetricsMemoryDumpProvider>( + 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<ProcessMetricsMemoryDumpProvider> (*)(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 <stdint.h> #include <memory> +#include <unordered_set> #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 <libgen.h> +#include <mach-o/dyld.h> +#endif + +#if defined(OS_WIN) +#include <base/strings/sys_string_conversions.h> +#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<MockMemoryDumpProvider*> g_live_mocks; +std::unordered_set<MockMemoryDumpProvider*> 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<ProcessMetricsMemoryDumpProvider>( + 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<uintptr_t>(&DummyFunction); + result = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + reinterpret_cast<LPCWSTR>(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<VMRegion> 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/browser/trace_config_file.cc b/chromium/components/tracing/common/trace_config_file.cc index 97f8a16810a..180834461f9 100644 --- a/chromium/components/tracing/browser/trace_config_file.cc +++ b/chromium/components/tracing/common/trace_config_file.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 "components/tracing/browser/trace_config_file.h" +#include "components/tracing/common/trace_config_file.h" #include <stddef.h> @@ -41,7 +41,7 @@ const char kTraceConfigParam[] = "trace_config"; const char kStartupDurationParam[] = "startup_duration"; const char kResultFileParam[] = "result_file"; -} // namespace +} // namespace TraceConfigFile* TraceConfigFile::GetInstance() { return base::Singleton<TraceConfigFile, @@ -93,8 +93,7 @@ TraceConfigFile::TraceConfigFile() DLOG(WARNING) << "Cannot parse the trace config file correctly."; } -TraceConfigFile::~TraceConfigFile() { -} +TraceConfigFile::~TraceConfigFile() {} bool TraceConfigFile::ParseTraceConfigFileContent(const std::string& content) { std::unique_ptr<base::Value> value(base::JSONReader::Read(content)); @@ -111,10 +110,10 @@ bool TraceConfigFile::ParseTraceConfigFileContent(const std::string& content) { trace_config_ = base::trace_event::TraceConfig(*trace_config_dict); if (!dict->GetInteger(kStartupDurationParam, &startup_duration_)) - startup_duration_ = 0; + startup_duration_ = 0; if (startup_duration_ < 0) - startup_duration_ = 0; + startup_duration_ = 0; base::FilePath::StringType result_file_str; if (dict->GetString(kResultFileParam, &result_file_str)) diff --git a/chromium/components/tracing/browser/trace_config_file.h b/chromium/components/tracing/common/trace_config_file.h index 06766d0ecd9..31adcef508c 100644 --- a/chromium/components/tracing/browser/trace_config_file.h +++ b/chromium/components/tracing/common/trace_config_file.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 COMPONENTS_TRACING_BROWSER_TRACE_CONFIG_FILE_H_ -#define COMPONENTS_TRACING_BROWSER_TRACE_CONFIG_FILE_H_ +#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" @@ -12,7 +12,8 @@ #include "components/tracing/tracing_export.h" namespace base { -template <typename Type> struct DefaultSingletonTraits; +template <typename Type> +struct DefaultSingletonTraits; } // namespace base namespace tracing { @@ -94,4 +95,4 @@ class TRACING_EXPORT TraceConfigFile { } // namespace tracing -#endif // COMPONENTS_TRACING_BROWSER_TRACE_CONFIG_FILE_H_ +#endif // COMPONENTS_TRACING_COMMON_TRACE_CONFIG_FILE_H_ diff --git a/chromium/components/tracing/browser/trace_config_file_unittest.cc b/chromium/components/tracing/common/trace_config_file_unittest.cc index 7920a5e8c6b..2c7df54d2e1 100644 --- a/chromium/components/tracing/browser/trace_config_file_unittest.cc +++ b/chromium/components/tracing/common/trace_config_file_unittest.cc @@ -2,11 +2,12 @@ // 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/browser/trace_config_file.h" #include "components/tracing/common/tracing_switches.h" #include "testing/gtest/include/gtest/gtest.h" @@ -49,12 +50,11 @@ std::string GetTraceConfigFileContent(std::string trace_config, return content; } -} // namespace +} // namespace TEST(TraceConfigFileTest, TraceStartupEnabled) { base::ShadowingAtExitManager sem; - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kTraceStartup); + base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kTraceStartup); base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kTraceConfigFile); @@ -100,16 +100,16 @@ TEST(TraceConfigFileTest, TraceConfigFileEnabledWithInvalidPath) { TEST(TraceConfigFileTest, ValidContent) { base::ShadowingAtExitManager sem; - std::string content = GetTraceConfigFileContent( - kTraceConfig, "10", "trace_result_file.log"); + 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())); + ASSERT_NE(-1, base::WriteFile(trace_config_file, content.c_str(), + (int)content.length())); base::CommandLine::ForCurrentProcess()->AppendSwitchPath( switches::kTraceConfigFile, trace_config_file); @@ -131,8 +131,8 @@ TEST(TraceConfigFileTest, ValidContentWithOnlyTraceConfig) { 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())); + ASSERT_NE(-1, base::WriteFile(trace_config_file, content.c_str(), + (int)content.length())); base::CommandLine::ForCurrentProcess()->AppendSwitchPath( switches::kTraceConfigFile, trace_config_file); @@ -155,20 +155,20 @@ TEST(TraceConfigFileTest, ContentWithAbsoluteResultFilePath) { 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(), '\\'); + 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(), '\\'); + it = std::find(it2 + 2, result_file_path_str.end(), '\\'); } - std::string content = GetTraceConfigFileContent( - kTraceConfig, "10", result_file_path_str); + 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())); + ASSERT_NE(-1, base::WriteFile(trace_config_file, content.c_str(), + (int)content.length())); base::CommandLine::ForCurrentProcess()->AppendSwitchPath( switches::kTraceConfigFile, trace_config_file); @@ -185,8 +185,8 @@ TEST(TraceConfigFileTest, ContentWithNegtiveDuration) { 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())); + ASSERT_NE(-1, base::WriteFile(trace_config_file, content.c_str(), + (int)content.length())); base::CommandLine::ForCurrentProcess()->AppendSwitchPath( switches::kTraceConfigFile, trace_config_file); @@ -201,16 +201,16 @@ TEST(TraceConfigFileTest, ContentWithNegtiveDuration) { TEST(TraceConfigFileTest, ContentWithoutTraceConfig) { base::ShadowingAtExitManager sem; - std::string content = GetTraceConfigFileContent( - "", "10", "trace_result_file.log"); + 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())); + ASSERT_NE(-1, base::WriteFile(trace_config_file, content.c_str(), + (int)content.length())); base::CommandLine::ForCurrentProcess()->AppendSwitchPath( switches::kTraceConfigFile, trace_config_file); @@ -226,8 +226,8 @@ TEST(TraceConfigFileTest, InvalidContent) { 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())); + ASSERT_NE(-1, base::WriteFile(trace_config_file, content.c_str(), + (int)content.length())); base::CommandLine::ForCurrentProcess()->AppendSwitchPath( switches::kTraceConfigFile, trace_config_file); @@ -247,4 +247,4 @@ TEST(TraceConfigFileTest, EmptyContent) { EXPECT_FALSE(TraceConfigFile::GetInstance()->IsEnabled()); } -} // namespace tracing +} // 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 |