summaryrefslogtreecommitdiff
path: root/chromium/components/tracing
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-04-05 17:15:33 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-04-11 07:47:18 +0000
commit7324afb043a0b1e623d8e8eb906cdc53bdeb4685 (patch)
treea3fe2d74ea9c9e142c390dac4ca0e219382ace46 /chromium/components/tracing
parent6a4cabb866f66d4128a97cdc6d9d08ce074f1247 (diff)
downloadqtwebengine-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.gn8
-rw-r--r--chromium/components/tracing/OWNERS3
-rw-r--r--chromium/components/tracing/common/process_metrics_memory_dump_provider.cc350
-rw-r--r--chromium/components/tracing/common/process_metrics_memory_dump_provider.h21
-rw-r--r--chromium/components/tracing/common/process_metrics_memory_dump_provider_unittest.cc165
-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.cc48
-rw-r--r--chromium/components/tracing/common/trace_startup.h19
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(&region, 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